Blame view

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

436f7c206   Fabian Frederick   igmp: remove came...
119
  #define IGMP_INITIAL_REPORT_DELAY		(1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120

436f7c206   Fabian Frederick   igmp: remove came...
121
  /* IGMP_INITIAL_REPORT_DELAY is not from IGMP specs!
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
124
125
126
   * 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...
127
  #define IGMP_V1_SEEN(in_dev) \
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
128
  	(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \
42f811b8b   Herbert Xu   [IPV4]: Convert I...
129
130
131
132
  	 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...
133
  	(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \
42f811b8b   Herbert Xu   [IPV4]: Convert I...
134
135
136
  	 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
137

cab70040d   William Manley   net: igmp: Reduce...
138
139
  static int unsolicited_report_interval(struct in_device *in_dev)
  {
2690048c0   William Manley   net: igmp: Allow ...
140
  	int interval_ms, interval_jiffies;
cab70040d   William Manley   net: igmp: Reduce...
141
  	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev))
2690048c0   William Manley   net: igmp: Allow ...
142
143
144
  		interval_ms = IN_DEV_CONF_GET(
  			in_dev,
  			IGMPV2_UNSOLICITED_REPORT_INTERVAL);
cab70040d   William Manley   net: igmp: Reduce...
145
  	else /* v3 */
2690048c0   William Manley   net: igmp: Allow ...
146
147
148
149
150
151
152
153
154
155
156
157
  		interval_ms = IN_DEV_CONF_GET(
  			in_dev,
  			IGMPV3_UNSOLICITED_REPORT_INTERVAL);
  
  	interval_jiffies = msecs_to_jiffies(interval_ms);
  
  	/* _timer functions can't handle a delay of 0 jiffies so ensure
  	 *  we always return a positive value.
  	 */
  	if (interval_jiffies <= 0)
  		interval_jiffies = 1;
  	return interval_jiffies;
cab70040d   William Manley   net: igmp: Reduce...
158
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
  static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
24803f38a   Hangbin Liu   igmp: do not remo...
160
  static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
163
164
165
  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...
166
167
  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
168
169
170
  
  static void ip_ma_put(struct ip_mc_list *im)
  {
8851ab526   Reshetova, Elena   net: convert ip_m...
171
  	if (refcount_dec_and_test(&im->refcnt)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  		in_dev_put(im->interface);
42ea299d3   Lai Jiangshan   net,rcu: convert ...
173
  		kfree_rcu(im, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
  	}
  }
d9aa93804   David S. Miller   ipv4: Fix build w...
176
177
178
179
180
181
182
183
184
  #define for_each_pmc_rcu(in_dev, pmc)				\
  	for (pmc = rcu_dereference(in_dev->mc_list);		\
  	     pmc != NULL;					\
  	     pmc = rcu_dereference(pmc->next_rcu))
  
  #define for_each_pmc_rtnl(in_dev, pmc)				\
  	for (pmc = rtnl_dereference(in_dev->mc_list);		\
  	     pmc != NULL;					\
  	     pmc = rtnl_dereference(pmc->next_rcu))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
  #ifdef CONFIG_IP_MULTICAST
  
  /*
   *	Timer management
   */
1d7138de8   Eric Dumazet   igmp: RCU convers...
190
  static void igmp_stop_timer(struct ip_mc_list *im)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
  {
  	spin_lock_bh(&im->lock);
  	if (del_timer(&im->timer))
8851ab526   Reshetova, Elena   net: convert ip_m...
194
  		refcount_dec(&im->refcnt);
a7e9ff735   Jianjun Kong   net: clean up net...
195
  	im->tm_running = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
201
202
203
  	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)
  {
63862b5be   Aruna-Hewapathirane   net: replace macr...
204
  	int tv = prandom_u32() % max_delay;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205

a7e9ff735   Jianjun Kong   net: clean up net...
206
  	im->tm_running = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  	if (!mod_timer(&im->timer, jiffies+tv+2))
8851ab526   Reshetova, Elena   net: convert ip_m...
208
  		refcount_inc(&im->refcnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
  }
  
  static void igmp_gq_start_timer(struct in_device *in_dev)
  {
63862b5be   Aruna-Hewapathirane   net: replace macr...
213
  	int tv = prandom_u32() % in_dev->mr_maxdelay;
7ababb782   Michal Tesar   igmp: Make igmp g...
214
215
216
217
218
  	unsigned long exp = jiffies + tv + 2;
  
  	if (in_dev->mr_gq_running &&
  	    time_after_eq(exp, (in_dev->mr_gq_timer).expires))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
  
  	in_dev->mr_gq_running = 1;
7ababb782   Michal Tesar   igmp: Make igmp g...
221
  	if (!mod_timer(&in_dev->mr_gq_timer, exp))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
  		in_dev_hold(in_dev);
  }
  
  static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)
  {
63862b5be   Aruna-Hewapathirane   net: replace macr...
227
  	int tv = prandom_u32() % delay;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
231
232
233
234
235
236
237
238
239
  
  	if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2))
  		in_dev_hold(in_dev);
  }
  
  static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
  {
  	spin_lock_bh(&im->lock);
  	im->unsolicit_count = 0;
  	if (del_timer(&im->timer)) {
  		if ((long)(im->timer.expires-jiffies) < max_delay) {
  			add_timer(&im->timer);
a7e9ff735   Jianjun Kong   net: clean up net...
240
  			im->tm_running = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
  			spin_unlock_bh(&im->lock);
  			return;
  		}
8851ab526   Reshetova, Elena   net: convert ip_m...
244
  		refcount_dec(&im->refcnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  	}
  	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...
266
267
268
269
270
271
272
273
274
275
276
277
  		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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  	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;
c71151f05   Weilong Chen   ipv4: fix all spa...
307
  	for (psf = pmc->sources; psf; psf = psf->sf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
313
  		if (!is_in(pmc, psf, type, gdeleted, sdeleted))
  			continue;
  		scount++;
  	}
  	return scount;
  }
f9c484692   Kevin Cernekee   net: igmp: Use co...
314
315
316
317
318
319
320
321
322
323
  /* source address selection per RFC 3376 section 4.2.13 */
  static __be32 igmpv3_get_srcaddr(struct net_device *dev,
  				 const struct flowi4 *fl4)
  {
  	struct in_device *in_dev = __in_dev_get_rcu(dev);
  
  	if (!in_dev)
  		return htonl(INADDR_ANY);
  
  	for_ifa(in_dev) {
d9bee33e3   Felix Fietkau   net: igmp: fix so...
324
  		if (fl4->saddr == ifa->ifa_local)
f9c484692   Kevin Cernekee   net: igmp: Use co...
325
326
327
328
329
  			return fl4->saddr;
  	} endfor_ifa(in_dev);
  
  	return htonl(INADDR_ANY);
  }
4c672e4b4   Daniel Borkmann   ipv6: mld: fix ad...
330
  static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
335
  {
  	struct sk_buff *skb;
  	struct rtable *rt;
  	struct iphdr *pip;
  	struct igmpv3_report *pig;
877acedc0   Daniel Lezcano   netns: Fix crash ...
336
  	struct net *net = dev_net(dev);
31e4543db   David S. Miller   ipv4: Make caller...
337
  	struct flowi4 fl4;
660882432   Herbert Xu   ipv4: Remove all ...
338
339
  	int hlen = LL_RESERVED_SPACE(dev);
  	int tlen = dev->needed_tailroom;
4c672e4b4   Daniel Borkmann   ipv6: mld: fix ad...
340
  	unsigned int size = mtu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341

57e1ab6ea   Eric Dumazet   igmp: refine skb ...
342
  	while (1) {
660882432   Herbert Xu   ipv4: Remove all ...
343
  		skb = alloc_skb(size + hlen + tlen,
57e1ab6ea   Eric Dumazet   igmp: refine skb ...
344
345
346
347
348
349
350
  				GFP_ATOMIC | __GFP_NOWARN);
  		if (skb)
  			break;
  		size >>= 1;
  		if (size < 256)
  			return NULL;
  	}
9d4a03146   Hannes Frederic Sowa   ipv4, ipv6: send ...
351
  	skb->priority = TC_PRIO_CONTROL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352

31e4543db   David S. Miller   ipv4: Make caller...
353
  	rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0,
78fbfd8a6   David S. Miller   ipv4: Create and ...
354
355
356
357
358
  				   0, 0,
  				   IPPROTO_IGMP, 0, dev->ifindex);
  	if (IS_ERR(rt)) {
  		kfree_skb(skb);
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360

d8d1f30b9   Changli Gao   net-next: remove ...
361
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  	skb->dev = dev;
660882432   Herbert Xu   ipv4: Remove all ...
363
  	skb_reserve(skb, hlen);
1837b2e2b   Benjamin Poirier   mld, igmp: Fix re...
364
  	skb_tailroom_reserve(skb, mtu, tlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365

7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
366
  	skb_reset_network_header(skb);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
367
  	pip = ip_hdr(skb);
7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
368
  	skb_put(skb, sizeof(struct iphdr) + 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
373
374
  
  	pip->version  = 4;
  	pip->ihl      = (sizeof(struct iphdr)+4)>>2;
  	pip->tos      = 0xc0;
  	pip->frag_off = htons(IP_DF);
  	pip->ttl      = 1;
492f64ce1   David S. Miller   ipv4: Use flowi4'...
375
  	pip->daddr    = fl4.daddr;
166f27322   Eric Dumazet   net: igmp: add a ...
376
377
  
  	rcu_read_lock();
f9c484692   Kevin Cernekee   net: igmp: Use co...
378
  	pip->saddr    = igmpv3_get_srcaddr(dev, &fl4);
166f27322   Eric Dumazet   net: igmp: add a ...
379
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
  	pip->protocol = IPPROTO_IGMP;
  	pip->tot_len  = 0;	/* filled in later */
b6a7719ae   Hannes Frederic Sowa   ipv4: hash net pt...
382
  	ip_select_ident(net, skb, NULL);
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
383
384
385
386
  	((u8 *)&pip[1])[0] = IPOPT_RA;
  	((u8 *)&pip[1])[1] = 4;
  	((u8 *)&pip[1])[2] = 0;
  	((u8 *)&pip[1])[3] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387

b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
388
  	skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4;
d10ba34b0   Arnaldo Carvalho de Melo   [SK_BUFF]: More s...
389
  	skb_put(skb, sizeof(*pig));
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
390
  	pig = igmpv3_report_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
398
399
400
  	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...
401
  	struct igmphdr *pig = igmp_hdr(skb);
f7c0c2ae8   Simon Horman   ipv4: Correct com...
402
  	const int igmplen = skb_tail_pointer(skb) - skb_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403

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

33224b16f   Eric W. Biederman   ipv4, ipv6: Pass ...
406
  	return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
  }
  
  static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
  {
a7e9ff735   Jianjun Kong   net: clean up net...
411
  	return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc, type, gdel, sdel);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
  }
  
  static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
415
  	int type, struct igmpv3_grec **ppgr, unsigned int mtu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
419
  {
  	struct net_device *dev = pmc->interface->dev;
  	struct igmpv3_report *pih;
  	struct igmpv3_grec *pgr;
57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
420
421
422
423
424
  	if (!skb) {
  		skb = igmpv3_newpack(dev, mtu);
  		if (!skb)
  			return NULL;
  	}
4df864c1d   Johannes Berg   networking: make ...
425
  	pgr = skb_put(skb, sizeof(struct igmpv3_grec));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
429
  	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...
430
  	pih = igmpv3_report_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
  	pih->ngrec = htons(ntohs(pih->ngrec)+1);
  	*ppgr = pgr;
  	return skb;
  }
4c672e4b4   Daniel Borkmann   ipv6: mld: fix ad...
435
  #define AVAILABLE(skb)	((skb) ? skb_availroom(skb) : 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
437
438
439
440
  
  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;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
441
  	struct net *net = dev_net(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
  	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...
445
  	int scount, stotal, first, isquery, truncate;
57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
446
  	unsigned int mtu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
  
  	if (pmc->multiaddr == IGMP_ALL_HOSTS)
  		return skb;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
450
  	if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
451
  		return skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452

57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
453
454
455
  	mtu = READ_ONCE(dev->mtu);
  	if (mtu < IPV4_MIN_MTU)
  		return skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
457
458
459
  	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...
460
  	stotal = scount = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
  	psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
462
463
  	if (!*psf_list)
  		goto empty_source;
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
464
  	pih = skb ? igmpv3_report_hdr(skb) : NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
468
469
470
471
  
  	/* 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);
57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
472
  			skb = igmpv3_newpack(dev, mtu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
  		}
  	}
  	first = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
  	psf_prev = NULL;
c71151f05   Weilong Chen   ipv4: fix all spa...
477
  	for (psf = *psf_list; psf; psf = psf_next) {
ea4d9e722   Al Viro   [IPV4]: struct ip...
478
  		__be32 *psrc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
480
481
482
483
484
485
  
  		psf_next = psf->sf_next;
  
  		if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
  			psf_prev = psf;
  			continue;
  		}
a052517a8   Hangbin Liu   net/multicast: sh...
486
487
488
489
490
491
492
493
  		/* Based on RFC3376 5.1. Should not send source-list change
  		 * records when there is a filter mode change.
  		 */
  		if (((gdeleted && pmc->sfmode == MCAST_EXCLUDE) ||
  		     (!gdeleted && pmc->crcount)) &&
  		    (type == IGMPV3_ALLOW_NEW_SOURCES ||
  		     type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount)
  			goto decrease_sf_crcount;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
  		/* clear marks on query responses */
  		if (isquery)
  			psf->sf_gsresp = 0;
63007727e   Al Viro   [IPV4]: trivial i...
497
  		if (AVAILABLE(skb) < sizeof(__be32) +
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
500
501
502
503
504
  		    first*sizeof(struct igmpv3_grec)) {
  			if (truncate && !first)
  				break;	 /* truncate these */
  			if (pgr)
  				pgr->grec_nsrcs = htons(scount);
  			if (skb)
  				igmpv3_sendpack(skb);
57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
505
  			skb = igmpv3_newpack(dev, mtu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
509
  			first = 1;
  			scount = 0;
  		}
  		if (first) {
57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
510
  			skb = add_grhead(skb, pmc, type, &pgr, mtu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
  			first = 0;
  		}
cc63f70b8   Alexey Dobriyan   [IPV4/IPV6] multi...
513
514
  		if (!skb)
  			return NULL;
4df864c1d   Johannes Berg   networking: make ...
515
  		psrc = skb_put(skb, sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  		*psrc = psf->sf_inaddr;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
517
  		scount++; stotal++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
  		if ((type == IGMPV3_ALLOW_NEW_SOURCES ||
  		     type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) {
a052517a8   Hangbin Liu   net/multicast: sh...
520
  decrease_sf_crcount:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
523
524
525
526
527
528
529
530
531
532
  			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...
533
534
535
536
537
538
539
540
  
  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 */
c71151f05   Weilong Chen   ipv4: fix all spa...
541
  			if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)) {
ad12583f4   David L Stevens   [IPV4]: Fix multi...
542
543
544
  				igmpv3_sendpack(skb);
  				skb = NULL; /* add_grhead will get a new one */
  			}
57dfc3d10   Eric Dumazet   ipv4: igmp: guard...
545
  			skb = add_grhead(skb, pmc, type, &pgr, mtu);
ad12583f4   David L Stevens   [IPV4]: Fix multi...
546
547
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
550
551
552
553
554
555
556
557
558
  	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;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
559
  	struct net *net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
561
562
  	int type;
  
  	if (!pmc) {
1d7138de8   Eric Dumazet   igmp: RCU convers...
563
564
  		rcu_read_lock();
  		for_each_pmc_rcu(in_dev, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
566
  			if (pmc->multiaddr == IGMP_ALL_HOSTS)
  				continue;
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
567
  			if (ipv4_is_local_multicast(pmc->multiaddr) &&
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
568
  			     !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
569
  				continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
573
574
575
576
577
  			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);
  		}
1d7138de8   Eric Dumazet   igmp: RCU convers...
578
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  	} 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;
c71151f05   Weilong Chen   ipv4: fix all spa...
601
  	for (psf = *ppsf; psf; psf = psf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  		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;
1d7138de8   Eric Dumazet   igmp: RCU convers...
619
  	rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
621
622
623
  	spin_lock_bh(&in_dev->mc_tomb_lock);
  
  	/* deleted MCA's */
  	pmc_prev = NULL;
c71151f05   Weilong Chen   ipv4: fix all spa...
624
  	for (pmc = in_dev->mc_tomb; pmc; pmc = pmc_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
627
628
629
630
631
632
  		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
633
634
635
636
  			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...
637
  			pmc->crcount--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  			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 */
1d7138de8   Eric Dumazet   igmp: RCU convers...
656
  	for_each_pmc_rcu(in_dev, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
658
659
660
661
662
663
664
665
666
667
668
669
  		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
670
671
672
673
674
  			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...
675
  			pmc->crcount--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
678
  		}
  		spin_unlock_bh(&pmc->lock);
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
679
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
681
682
683
684
685
686
687
688
689
690
691
692
693
  
  	if (!skb)
  		return;
  	(void) igmpv3_sendpack(skb);
  }
  
  static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
  	int type)
  {
  	struct sk_buff *skb;
  	struct iphdr *iph;
  	struct igmphdr *ih;
  	struct rtable *rt;
  	struct net_device *dev = in_dev->dev;
877acedc0   Daniel Lezcano   netns: Fix crash ...
694
  	struct net *net = dev_net(dev);
63007727e   Al Viro   [IPV4]: trivial i...
695
  	__be32	group = pmc ? pmc->multiaddr : 0;
31e4543db   David S. Miller   ipv4: Make caller...
696
  	struct flowi4 fl4;
63007727e   Al Viro   [IPV4]: trivial i...
697
  	__be32	dst;
660882432   Herbert Xu   ipv4: Remove all ...
698
  	int hlen, tlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
700
701
  
  	if (type == IGMPV3_HOST_MEMBERSHIP_REPORT)
  		return igmpv3_send_report(in_dev, pmc);
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
702

87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
703
  	if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
704
705
706
  		return 0;
  
  	if (type == IGMP_HOST_LEAVE_MESSAGE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
  		dst = IGMP_ALL_ROUTER;
  	else
  		dst = group;
31e4543db   David S. Miller   ipv4: Make caller...
710
  	rt = ip_route_output_ports(net, &fl4, NULL, dst, 0,
78fbfd8a6   David S. Miller   ipv4: Create and ...
711
712
713
714
  				   0, 0,
  				   IPPROTO_IGMP, 0, dev->ifindex);
  	if (IS_ERR(rt))
  		return -1;
660882432   Herbert Xu   ipv4: Remove all ...
715
716
717
  	hlen = LL_RESERVED_SPACE(dev);
  	tlen = dev->needed_tailroom;
  	skb = alloc_skb(IGMP_SIZE + hlen + tlen, GFP_ATOMIC);
51456b291   Ian Morris   ipv4: coding styl...
718
  	if (!skb) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
721
  		ip_rt_put(rt);
  		return -1;
  	}
9d4a03146   Hannes Frederic Sowa   ipv4, ipv6: send ...
722
  	skb->priority = TC_PRIO_CONTROL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723

d8d1f30b9   Changli Gao   net-next: remove ...
724
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725

660882432   Herbert Xu   ipv4: Remove all ...
726
  	skb_reserve(skb, hlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727

7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
728
  	skb_reset_network_header(skb);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
729
  	iph = ip_hdr(skb);
7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
730
  	skb_put(skb, sizeof(struct iphdr) + 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
733
734
735
736
737
  
  	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;
492f64ce1   David S. Miller   ipv4: Use flowi4'...
738
  	iph->saddr    = fl4.saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
  	iph->protocol = IPPROTO_IGMP;
b6a7719ae   Hannes Frederic Sowa   ipv4: hash net pt...
740
  	ip_select_ident(net, skb, NULL);
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
741
742
743
744
  	((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
745

4df864c1d   Johannes Berg   networking: make ...
746
  	ih = skb_put(skb, sizeof(struct igmphdr));
a7e9ff735   Jianjun Kong   net: clean up net...
747
748
749
750
751
  	ih->type = type;
  	ih->code = 0;
  	ih->csum = 0;
  	ih->group = group;
  	ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752

33224b16f   Eric W. Biederman   ipv4, ipv6: Pass ...
753
  	return ip_local_out(net, skb->sk, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
756
757
758
759
760
761
  }
  
  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);
e2401654d   Salam Noureddine   ipv4 igmp: use in...
762
  	in_dev_put(in_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
764
765
766
767
768
769
770
771
  }
  
  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--;
cab70040d   William Manley   net: igmp: Reduce...
772
773
  		igmp_ifc_start_timer(in_dev,
  				     unsolicited_report_interval(in_dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
  	}
e2401654d   Salam Noureddine   ipv4 igmp: use in...
775
  	in_dev_put(in_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
777
778
779
  }
  
  static void igmp_ifc_event(struct in_device *in_dev)
  {
165094afc   Nikolay Borisov   igmp: Namespacify...
780
  	struct net *net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
782
  	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev))
  		return;
165094afc   Nikolay Borisov   igmp: Namespacify...
783
  	in_dev->mr_ifc_count = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
786
787
788
789
  	igmp_ifc_start_timer(in_dev, 1);
  }
  
  
  static void igmp_timer_expire(unsigned long data)
  {
c71151f05   Weilong Chen   ipv4: fix all spa...
790
  	struct ip_mc_list *im = (struct ip_mc_list *)data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
793
  	struct in_device *in_dev = im->interface;
  
  	spin_lock(&im->lock);
a7e9ff735   Jianjun Kong   net: clean up net...
794
  	im->tm_running = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
796
797
  
  	if (im->unsolicit_count) {
  		im->unsolicit_count--;
cab70040d   William Manley   net: igmp: Reduce...
798
  		igmp_start_timer(im, unsolicited_report_interval(in_dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
800
801
802
803
804
805
806
807
808
809
810
811
  	}
  	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...
812
  /* mark EXCLUDE-mode sources */
ea4d9e722   Al Viro   [IPV4]: struct ip...
813
  static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
816
817
818
  {
  	struct ip_sf_list *psf;
  	int i, scount;
  
  	scount = 0;
c71151f05   Weilong Chen   ipv4: fix all spa...
819
  	for (psf = pmc->sources; psf; psf = psf->sf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
821
  		if (scount == nsrcs)
  			break;
c71151f05   Weilong Chen   ipv4: fix all spa...
822
  		for (i = 0; i < nsrcs; i++) {
ad12583f4   David L Stevens   [IPV4]: Fix multi...
823
  			/* skip inactive filters */
e05c4ad3e   Yan, Zheng   mcast: Fix source...
824
  			if (psf->sf_count[MCAST_INCLUDE] ||
ad12583f4   David L Stevens   [IPV4]: Fix multi...
825
826
  			    pmc->sfcount[MCAST_EXCLUDE] !=
  			    psf->sf_count[MCAST_EXCLUDE])
ce713ee5a   RongQing.Li   net: replace cont...
827
  				break;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
828
829
830
831
832
833
834
835
836
837
838
  			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...
839
  static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
ad12583f4   David L Stevens   [IPV4]: Fix multi...
840
841
842
843
844
845
846
847
848
  {
  	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;
c71151f05   Weilong Chen   ipv4: fix all spa...
849
  	for (psf = pmc->sources; psf; psf = psf->sf_next) {
ad12583f4   David L Stevens   [IPV4]: Fix multi...
850
851
  		if (scount == nsrcs)
  			break;
c71151f05   Weilong Chen   ipv4: fix all spa...
852
  		for (i = 0; i < nsrcs; i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853
854
855
856
857
858
  			if (srcs[i] == psf->sf_inaddr) {
  				psf->sf_gsresp = 1;
  				scount++;
  				break;
  			}
  	}
ad12583f4   David L Stevens   [IPV4]: Fix multi...
859
860
861
862
863
864
  	if (!scount) {
  		pmc->gsquery = 0;
  		return 0;
  	}
  	pmc->gsquery = 1;
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
  }
d679c5324   Eric Dumazet   igmp: avoid drop_...
866
867
  /* return true if packet was dropped */
  static bool igmp_heard_report(struct in_device *in_dev, __be32 group)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
  {
  	struct ip_mc_list *im;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
870
  	struct net *net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
872
873
874
  
  	/* Timers are only set for non-local groups */
  
  	if (group == IGMP_ALL_HOSTS)
d679c5324   Eric Dumazet   igmp: avoid drop_...
875
  		return false;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
876
  	if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
877
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878

1d7138de8   Eric Dumazet   igmp: RCU convers...
879
880
  	rcu_read_lock();
  	for_each_pmc_rcu(in_dev, im) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
882
883
884
885
  		if (im->multiaddr == group) {
  			igmp_stop_timer(im);
  			break;
  		}
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
886
  	rcu_read_unlock();
d679c5324   Eric Dumazet   igmp: avoid drop_...
887
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
  }
d679c5324   Eric Dumazet   igmp: avoid drop_...
889
890
  /* return true if packet was dropped */
  static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891
892
  	int len)
  {
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
893
894
  	struct igmphdr 		*ih = igmp_hdr(skb);
  	struct igmpv3_query *ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895
  	struct ip_mc_list	*im;
63007727e   Al Viro   [IPV4]: trivial i...
896
  	__be32			group = ih->group;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
898
  	int			max_delay;
  	int			mark = 0;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
899
  	struct net		*net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900

5b7c84066   David Stevens   ipv4: correct IGM...
901
  	if (len == 8) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
  		if (ih->code == 0) {
  			/* Alas, old v1 router presents here. */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
904

436f7c206   Fabian Frederick   igmp: remove came...
905
  			max_delay = IGMP_QUERY_RESPONSE_INTERVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
  			in_dev->mr_v1_seen = jiffies +
436f7c206   Fabian Frederick   igmp: remove came...
907
  				IGMP_V1_ROUTER_PRESENT_TIMEOUT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
909
910
911
912
  			group = 0;
  		} else {
  			/* v2 router present */
  			max_delay = ih->code*(HZ/IGMP_TIMER_SCALE);
  			in_dev->mr_v2_seen = jiffies +
436f7c206   Fabian Frederick   igmp: remove came...
913
  				IGMP_V2_ROUTER_PRESENT_TIMEOUT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
915
916
917
918
919
920
921
  		}
  		/* 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) {
d679c5324   Eric Dumazet   igmp: avoid drop_...
922
  		return true;	/* ignore bogus packet; freed by caller */
5b7c84066   David Stevens   ipv4: correct IGM...
923
924
  	} else if (IGMP_V1_SEEN(in_dev)) {
  		/* This is a v3 query with v1 queriers present */
436f7c206   Fabian Frederick   igmp: remove came...
925
  		max_delay = IGMP_QUERY_RESPONSE_INTERVAL;
5b7c84066   David Stevens   ipv4: correct IGM...
926
927
928
929
930
931
932
933
934
  		group = 0;
  	} else if (IGMP_V2_SEEN(in_dev)) {
  		/* this is a v3 query with v2 queriers present;
  		 * Interpretation of the max_delay code is problematic here.
  		 * A real v2 host would use ih_code directly, while v3 has a
  		 * different encoding. We use the v3 encoding as more likely
  		 * to be intended in a v3 query.
  		 */
  		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
a8c1f65c7   Ben Hutchings   igmp: Avoid zero ...
935
936
  		if (!max_delay)
  			max_delay = 1;	/* can't mod w/ 0 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
938
  	} else { /* v3 */
  		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))
d679c5324   Eric Dumazet   igmp: avoid drop_...
939
  			return true;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
940

d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
941
  		ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
  		if (ih3->nsrcs) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
943
  			if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)
63007727e   Al Viro   [IPV4]: trivial i...
944
  					   + ntohs(ih3->nsrcs)*sizeof(__be32)))
d679c5324   Eric Dumazet   igmp: avoid drop_...
945
  				return true;
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
946
  			ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
949
950
951
952
953
954
955
956
  		}
  
  		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)
b47bd8d27   Daniel Borkmann   ipv4: igmp: fix v...
957
  				return true;	/* no sources allowed */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
  			igmp_gq_start_timer(in_dev);
d679c5324   Eric Dumazet   igmp: avoid drop_...
959
  			return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
  		}
  		/* 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
  	 */
1d7138de8   Eric Dumazet   igmp: RCU convers...
975
976
  	rcu_read_lock();
  	for_each_pmc_rcu(in_dev, im) {
ad12583f4   David L Stevens   [IPV4]: Fix multi...
977
  		int changed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978
979
980
981
  		if (group && group != im->multiaddr)
  			continue;
  		if (im->multiaddr == IGMP_ALL_HOSTS)
  			continue;
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
982
  		if (ipv4_is_local_multicast(im->multiaddr) &&
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
983
  		    !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
984
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
986
987
988
989
  		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...
990
  		changed = !im->gsquery ||
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
991
  			igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
  		spin_unlock_bh(&im->lock);
ad12583f4   David L Stevens   [IPV4]: Fix multi...
993
994
  		if (changed)
  			igmp_mod_timer(im, max_delay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
996
  	rcu_read_unlock();
d679c5324   Eric Dumazet   igmp: avoid drop_...
997
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
998
  }
9a57a9d29   Eric Dumazet   igmp: avoid two a...
999
  /* called in rcu_read_lock() section */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
1001
1002
1003
  int igmp_rcv(struct sk_buff *skb)
  {
  	/* This basically follows the spec line by line -- see RFC1112 */
  	struct igmphdr *ih;
c7b725be8   David Ahern   net: igmp: Use in...
1004
1005
  	struct net_device *dev = skb->dev;
  	struct in_device *in_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
  	int len = skb->len;
d679c5324   Eric Dumazet   igmp: avoid drop_...
1007
  	bool dropped = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008

c7b725be8   David Ahern   net: igmp: Use in...
1009
1010
1011
1012
1013
1014
1015
  	if (netif_is_l3_master(dev)) {
  		dev = dev_get_by_index_rcu(dev_net(dev), IPCB(skb)->iif);
  		if (!dev)
  			goto drop;
  	}
  
  	in_dev = __in_dev_get_rcu(dev);
51456b291   Ian Morris   ipv4: coding styl...
1016
  	if (!in_dev)
cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
1017
  		goto drop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018

fb286bb29   Herbert Xu   [NET]: Detect har...
1019
  	if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
9a57a9d29   Eric Dumazet   igmp: avoid two a...
1020
  		goto drop;
fb286bb29   Herbert Xu   [NET]: Detect har...
1021

de08dc1a8   Tom Herbert   igmp: Call skb_ch...
1022
1023
  	if (skb_checksum_simple_validate(skb))
  		goto drop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1024

d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1025
  	ih = igmp_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
1027
  	switch (ih->type) {
  	case IGMP_HOST_MEMBERSHIP_QUERY:
d679c5324   Eric Dumazet   igmp: avoid drop_...
1028
  		dropped = igmp_heard_query(in_dev, skb, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
1030
1031
  		break;
  	case IGMP_HOST_MEMBERSHIP_REPORT:
  	case IGMPV2_HOST_MEMBERSHIP_REPORT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032
  		/* Is it our report looped back? */
c75379676   David S. Miller   ipv4: Make rt->fl...
1033
  		if (rt_is_output_route(skb_rtable(skb)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1034
  			break;
24c692750   David Stevens   [IGMP]: workaroun...
1035
1036
1037
  		/* don't rely on MC router hearing unicast reports */
  		if (skb->pkt_type == PACKET_MULTICAST ||
  		    skb->pkt_type == PACKET_BROADCAST)
d679c5324   Eric Dumazet   igmp: avoid drop_...
1038
  			dropped = igmp_heard_report(in_dev, ih->group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
1040
1041
  		break;
  	case IGMP_PIM:
  #ifdef CONFIG_IP_PIMSM_V1
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042
1043
  		return pim_rcv_v1(skb);
  #endif
c6b471e64   Herbert Xu   inet: Remove bogu...
1044
  	case IGMPV3_HOST_MEMBERSHIP_REPORT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
1046
1047
1048
1049
1050
1051
  	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...
1052
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1053
  	}
fb286bb29   Herbert Xu   [NET]: Detect har...
1054

cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
1055
  drop:
d679c5324   Eric Dumazet   igmp: avoid drop_...
1056
1057
1058
1059
  	if (dropped)
  		kfree_skb(skb);
  	else
  		consume_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
1062
1063
1064
1065
1066
1067
1068
  	return 0;
  }
  
  #endif
  
  
  /*
   *	Add a filter to a device
   */
63007727e   Al Viro   [IPV4]: trivial i...
1069
  static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
1072
1073
1074
1075
  {
  	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
b81693d91   Jiri Pirko   net: remove ndo_s...
1076
  	   is changed. This check should be done in ndo_set_rx_mode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1077
1078
1079
1080
1081
  	   routine. Something sort of:
  	   if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
  	   --ANK
  	   */
  	if (arp_mc_map(addr, buf, dev, 0) == 0)
22bedad3c   Jiri Pirko   net: convert mult...
1082
  		dev_mc_add(dev, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
1084
1085
1086
1087
  }
  
  /*
   *	Remove a filter from a device
   */
63007727e   Al Viro   [IPV4]: trivial i...
1088
  static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
1090
1091
1092
1093
  {
  	char buf[MAX_ADDR_LEN];
  	struct net_device *dev = in_dev->dev;
  
  	if (arp_mc_map(addr, buf, dev, 0) == 0)
22bedad3c   Jiri Pirko   net: convert mult...
1094
  		dev_mc_del(dev, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
1097
1098
1099
1100
1101
1102
1103
  }
  
  #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;
165094afc   Nikolay Borisov   igmp: Namespacify...
1104
  	struct net *net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1105
1106
1107
1108
1109
1110
1111
  
  	/* 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...
1112
  	pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1113
1114
  	if (!pmc)
  		return;
b4846fc3c   WANG Cong   igmp: add a missi...
1115
  	spin_lock_init(&pmc->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1116
1117
1118
1119
  	spin_lock_bh(&im->lock);
  	pmc->interface = im->interface;
  	in_dev_hold(in_dev);
  	pmc->multiaddr = im->multiaddr;
165094afc   Nikolay Borisov   igmp: Namespacify...
1120
  	pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
1122
1123
1124
1125
1126
1127
  	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;
c71151f05   Weilong Chen   ipv4: fix all spa...
1128
  		for (psf = pmc->sources; psf; psf = psf->sf_next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
1130
1131
1132
1133
1134
1135
1136
1137
  			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);
  }
24803f38a   Hangbin Liu   igmp: do not remo...
1138
1139
1140
1141
  /*
   * restore ip_mc_list deleted records
   */
  static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1142
1143
  {
  	struct ip_mc_list *pmc, *pmc_prev;
24803f38a   Hangbin Liu   igmp: do not remo...
1144
1145
1146
  	struct ip_sf_list *psf;
  	struct net *net = dev_net(in_dev->dev);
  	__be32 multiaddr = im->multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1147
1148
1149
  
  	spin_lock_bh(&in_dev->mc_tomb_lock);
  	pmc_prev = NULL;
c71151f05   Weilong Chen   ipv4: fix all spa...
1150
  	for (pmc = in_dev->mc_tomb; pmc; pmc = pmc->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
  		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);
24803f38a   Hangbin Liu   igmp: do not remo...
1162
1163
  
  	spin_lock_bh(&im->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1164
  	if (pmc) {
24803f38a   Hangbin Liu   igmp: do not remo...
1165
1166
  		im->interface = pmc->interface;
  		im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
46f9e1d0b   Hangbin Liu   multicast: do not...
1167
  		if (im->sfmode == MCAST_INCLUDE) {
24803f38a   Hangbin Liu   igmp: do not remo...
1168
1169
1170
1171
  			im->tomb = pmc->tomb;
  			im->sources = pmc->sources;
  			for (psf = im->sources; psf; psf = psf->sf_next)
  				psf->sf_crcount = im->crcount;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1172
1173
  		}
  		in_dev_put(pmc->interface);
9c8bb163a   Hangbin Liu   igmp, mld: Fix me...
1174
  		kfree(pmc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1175
  	}
24803f38a   Hangbin Liu   igmp: do not remo...
1176
  	spin_unlock_bh(&im->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1177
  }
24803f38a   Hangbin Liu   igmp: do not remo...
1178
1179
1180
  /*
   * flush ip_mc_list deleted records
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
  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 */
1d7138de8   Eric Dumazet   igmp: RCU convers...
1197
1198
  	rcu_read_lock();
  	for_each_pmc_rcu(in_dev, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1199
1200
1201
1202
1203
1204
  		struct ip_sf_list *psf, *psf_next;
  
  		spin_lock_bh(&pmc->lock);
  		psf = pmc->tomb;
  		pmc->tomb = NULL;
  		spin_unlock_bh(&pmc->lock);
c71151f05   Weilong Chen   ipv4: fix all spa...
1205
  		for (; psf; psf = psf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1206
1207
1208
1209
  			psf_next = psf->sf_next;
  			kfree(psf);
  		}
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
1210
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1211
1212
1213
1214
1215
1216
1217
  }
  #endif
  
  static void igmp_group_dropped(struct ip_mc_list *im)
  {
  	struct in_device *in_dev = im->interface;
  #ifdef CONFIG_IP_MULTICAST
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
1218
  	struct net *net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
  	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;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
1230
  	if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
1231
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1232
1233
1234
1235
1236
1237
  
  	reporter = im->reporter;
  	igmp_stop_timer(im);
  
  	if (!in_dev->dead) {
  		if (IGMP_V1_SEEN(in_dev))
24cf3af3f   Veaceslav Falico   igmp: call ip_mc_...
1238
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1239
1240
1241
  		if (IGMP_V2_SEEN(in_dev)) {
  			if (reporter)
  				igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE);
24cf3af3f   Veaceslav Falico   igmp: call ip_mc_...
1242
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
1244
1245
1246
1247
1248
  		}
  		/* IGMPv3 */
  		igmpv3_add_delrec(in_dev, im);
  
  		igmp_ifc_event(in_dev);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1249
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1250
1251
1252
1253
1254
  }
  
  static void igmp_group_added(struct ip_mc_list *im)
  {
  	struct in_device *in_dev = im->interface;
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1255
  #ifdef CONFIG_IP_MULTICAST
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
1256
  	struct net *net = dev_net(in_dev->dev);
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1257
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1258
1259
1260
1261
1262
1263
1264
1265
1266
  
  	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;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
1267
  	if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
1268
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1269
1270
1271
1272
1273
  
  	if (in_dev->dead)
  		return;
  	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) {
  		spin_lock_bh(&im->lock);
436f7c206   Fabian Frederick   igmp: remove came...
1274
  		igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1275
1276
1277
1278
  		spin_unlock_bh(&im->lock);
  		return;
  	}
  	/* else, v3 */
165094afc   Nikolay Borisov   igmp: Namespacify...
1279
  	im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1280
1281
1282
1283
1284
1285
1286
1287
  	igmp_ifc_event(in_dev);
  #endif
  }
  
  
  /*
   *	Multicast list managers
   */
e98970713   Eric Dumazet   igmp: hash a hash...
1288
1289
  static u32 ip_mc_hash(const struct ip_mc_list *im)
  {
c70eba745   Eric Dumazet   igmp: fix new spa...
1290
  	return hash_32((__force u32)im->multiaddr, MC_HASH_SZ_LOG);
e98970713   Eric Dumazet   igmp: hash a hash...
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
  }
  
  static void ip_mc_hash_add(struct in_device *in_dev,
  			   struct ip_mc_list *im)
  {
  	struct ip_mc_list __rcu **mc_hash;
  	u32 hash;
  
  	mc_hash = rtnl_dereference(in_dev->mc_hash);
  	if (mc_hash) {
  		hash = ip_mc_hash(im);
c70eba745   Eric Dumazet   igmp: fix new spa...
1302
  		im->next_hash = mc_hash[hash];
e98970713   Eric Dumazet   igmp: hash a hash...
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
  		rcu_assign_pointer(mc_hash[hash], im);
  		return;
  	}
  
  	/* do not use a hash table for small number of items */
  	if (in_dev->mc_count < 4)
  		return;
  
  	mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG,
  			  GFP_KERNEL);
  	if (!mc_hash)
  		return;
  
  	for_each_pmc_rtnl(in_dev, im) {
  		hash = ip_mc_hash(im);
c70eba745   Eric Dumazet   igmp: fix new spa...
1318
  		im->next_hash = mc_hash[hash];
e98970713   Eric Dumazet   igmp: hash a hash...
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
  		RCU_INIT_POINTER(mc_hash[hash], im);
  	}
  
  	rcu_assign_pointer(in_dev->mc_hash, mc_hash);
  }
  
  static void ip_mc_hash_remove(struct in_device *in_dev,
  			      struct ip_mc_list *im)
  {
  	struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash);
  	struct ip_mc_list *aux;
  
  	if (!mc_hash)
  		return;
  	mc_hash += ip_mc_hash(im);
  	while ((aux = rtnl_dereference(*mc_hash)) != im)
  		mc_hash = &aux->next_hash;
  	*mc_hash = im->next_hash;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1338
1339
1340
1341
  
  /*
   *	A socket has joined a multicast group on device dev.
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1342
  void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1343
1344
  {
  	struct ip_mc_list *im;
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1345
  #ifdef CONFIG_IP_MULTICAST
165094afc   Nikolay Borisov   igmp: Namespacify...
1346
  	struct net *net = dev_net(in_dev->dev);
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1347
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1348
1349
  
  	ASSERT_RTNL();
1d7138de8   Eric Dumazet   igmp: RCU convers...
1350
  	for_each_pmc_rtnl(in_dev, im) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1351
1352
1353
1354
1355
1356
  		if (im->multiaddr == addr) {
  			im->users++;
  			ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0);
  			goto out;
  		}
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
1357
  	im = kzalloc(sizeof(*im), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1358
1359
  	if (!im)
  		goto out;
a7e9ff735   Jianjun Kong   net: clean up net...
1360
1361
  	im->users = 1;
  	im->interface = in_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1362
  	in_dev_hold(in_dev);
a7e9ff735   Jianjun Kong   net: clean up net...
1363
  	im->multiaddr = addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1364
1365
  	/* initial mode is (EX, empty) */
  	im->sfmode = MCAST_EXCLUDE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1366
  	im->sfcount[MCAST_EXCLUDE] = 1;
8851ab526   Reshetova, Elena   net: convert ip_m...
1367
  	refcount_set(&im->refcnt, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1368
1369
  	spin_lock_init(&im->lock);
  #ifdef CONFIG_IP_MULTICAST
179542a54   Himangi Saraogi   igmp: remove exce...
1370
  	setup_timer(&im->timer, igmp_timer_expire, (unsigned long)im);
165094afc   Nikolay Borisov   igmp: Namespacify...
1371
  	im->unsolicit_count = net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1372
  #endif
1d7138de8   Eric Dumazet   igmp: RCU convers...
1373
1374
  
  	im->next_rcu = in_dev->mc_list;
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
1375
  	in_dev->mc_count++;
cf778b00e   Eric Dumazet   net: reintroduce ...
1376
  	rcu_assign_pointer(in_dev->mc_list, im);
1d7138de8   Eric Dumazet   igmp: RCU convers...
1377

e98970713   Eric Dumazet   igmp: hash a hash...
1378
  	ip_mc_hash_add(in_dev, im);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1379
  #ifdef CONFIG_IP_MULTICAST
24803f38a   Hangbin Liu   igmp: do not remo...
1380
  	igmpv3_del_delrec(in_dev, im);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381
1382
1383
1384
1385
1386
1387
  #endif
  	igmp_group_added(im);
  	if (!in_dev->dead)
  		ip_rt_multicast_event(in_dev);
  out:
  	return;
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
1388
  EXPORT_SYMBOL(ip_mc_inc_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1389

9afd85c9e   Linus Lüssing   net: Export IGMP/...
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
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
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
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
  static int ip_mc_check_iphdr(struct sk_buff *skb)
  {
  	const struct iphdr *iph;
  	unsigned int len;
  	unsigned int offset = skb_network_offset(skb) + sizeof(*iph);
  
  	if (!pskb_may_pull(skb, offset))
  		return -EINVAL;
  
  	iph = ip_hdr(skb);
  
  	if (iph->version != 4 || ip_hdrlen(skb) < sizeof(*iph))
  		return -EINVAL;
  
  	offset += ip_hdrlen(skb) - sizeof(*iph);
  
  	if (!pskb_may_pull(skb, offset))
  		return -EINVAL;
  
  	iph = ip_hdr(skb);
  
  	if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
  		return -EINVAL;
  
  	len = skb_network_offset(skb) + ntohs(iph->tot_len);
  	if (skb->len < len || len < offset)
  		return -EINVAL;
  
  	skb_set_transport_header(skb, offset);
  
  	return 0;
  }
  
  static int ip_mc_check_igmp_reportv3(struct sk_buff *skb)
  {
  	unsigned int len = skb_transport_offset(skb);
  
  	len += sizeof(struct igmpv3_report);
  
  	return pskb_may_pull(skb, len) ? 0 : -EINVAL;
  }
  
  static int ip_mc_check_igmp_query(struct sk_buff *skb)
  {
  	unsigned int len = skb_transport_offset(skb);
  
  	len += sizeof(struct igmphdr);
  	if (skb->len < len)
  		return -EINVAL;
  
  	/* IGMPv{1,2}? */
  	if (skb->len != len) {
  		/* or IGMPv3? */
  		len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr);
  		if (skb->len < len || !pskb_may_pull(skb, len))
  			return -EINVAL;
  	}
  
  	/* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer
  	 * all-systems destination addresses (224.0.0.1) for general queries
  	 */
  	if (!igmp_hdr(skb)->group &&
  	    ip_hdr(skb)->daddr != htonl(INADDR_ALLHOSTS_GROUP))
  		return -EINVAL;
  
  	return 0;
  }
  
  static int ip_mc_check_igmp_msg(struct sk_buff *skb)
  {
  	switch (igmp_hdr(skb)->type) {
  	case IGMP_HOST_LEAVE_MESSAGE:
  	case IGMP_HOST_MEMBERSHIP_REPORT:
  	case IGMPV2_HOST_MEMBERSHIP_REPORT:
  		/* fall through */
  		return 0;
  	case IGMPV3_HOST_MEMBERSHIP_REPORT:
  		return ip_mc_check_igmp_reportv3(skb);
  	case IGMP_HOST_MEMBERSHIP_QUERY:
  		return ip_mc_check_igmp_query(skb);
  	default:
  		return -ENOMSG;
  	}
  }
  
  static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb)
  {
  	return skb_checksum_simple_validate(skb);
  }
  
  static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
  
  {
  	struct sk_buff *skb_chk;
  	unsigned int transport_len;
  	unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr);
a516993f0   Linus Lüssing   net: fix wrong sk...
1486
  	int ret = -EINVAL;
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1487
1488
  
  	transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1489
1490
1491
  	skb_chk = skb_checksum_trimmed(skb, transport_len,
  				       ip_mc_validate_checksum);
  	if (!skb_chk)
a516993f0   Linus Lüssing   net: fix wrong sk...
1492
  		goto err;
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1493

a516993f0   Linus Lüssing   net: fix wrong sk...
1494
1495
  	if (!pskb_may_pull(skb_chk, len))
  		goto err;
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1496
1497
  
  	ret = ip_mc_check_igmp_msg(skb_chk);
a516993f0   Linus Lüssing   net: fix wrong sk...
1498
1499
  	if (ret)
  		goto err;
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1500
1501
1502
  
  	if (skb_trimmed)
  		*skb_trimmed = skb_chk;
a516993f0   Linus Lüssing   net: fix wrong sk...
1503
1504
  	/* free now unneeded clone */
  	else if (skb_chk != skb)
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1505
  		kfree_skb(skb_chk);
a516993f0   Linus Lüssing   net: fix wrong sk...
1506
1507
1508
1509
1510
1511
1512
  	ret = 0;
  
  err:
  	if (ret && skb_chk && skb_chk != skb)
  		kfree_skb(skb_chk);
  
  	return ret;
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1513
1514
1515
1516
1517
1518
1519
1520
  }
  
  /**
   * ip_mc_check_igmp - checks whether this is a sane IGMP packet
   * @skb: the skb to validate
   * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional)
   *
   * Checks whether an IPv4 packet is a valid IGMP packet. If so sets
a516993f0   Linus Lüssing   net: fix wrong sk...
1521
   * skb transport header accordingly and returns zero.
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
   *
   * -EINVAL: A broken packet was detected, i.e. it violates some internet
   *  standard
   * -ENOMSG: IP header validation succeeded but it is not an IGMP packet.
   * -ENOMEM: A memory allocation failure happened.
   *
   * Optionally, an skb pointer might be provided via skb_trimmed (or set it
   * to NULL): After parsing an IGMP packet successfully it will point to
   * an skb which has its tail aligned to the IP packet end. This might
   * either be the originally provided skb or a trimmed, cloned version if
   * the skb frame had data beyond the IP packet. A cloned skb allows us
   * to leave the original skb and its full frame unchanged (which might be
   * desirable for layer 2 frame jugglers).
   *
a516993f0   Linus Lüssing   net: fix wrong sk...
1536
1537
   * Caller needs to set the skb network header and free any returned skb if it
   * differs from the provided skb.
9afd85c9e   Linus Lüssing   net: Export IGMP/...
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
   */
  int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
  {
  	int ret = ip_mc_check_iphdr(skb);
  
  	if (ret < 0)
  		return ret;
  
  	if (ip_hdr(skb)->protocol != IPPROTO_IGMP)
  		return -ENOMSG;
  
  	return __ip_mc_check_igmp(skb, skb_trimmed);
  }
  EXPORT_SYMBOL(ip_mc_check_igmp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1552
  /*
4aa5dee4d   Jiri Pirko   net: convert rese...
1553
   *	Resend IGMP JOIN report; used by netdev notifier.
a816c7c71   Jay Vosburgh   bonding: Improve ...
1554
   */
4aa5dee4d   Jiri Pirko   net: convert rese...
1555
  static void ip_mc_rejoin_groups(struct in_device *in_dev)
a816c7c71   Jay Vosburgh   bonding: Improve ...
1556
  {
08882669e   Geert Uytterhoeven   [IPV4]: Fix warni...
1557
  #ifdef CONFIG_IP_MULTICAST
866f3b25a   Eric Dumazet   bonding: IGMP han...
1558
1559
  	struct ip_mc_list *im;
  	int type;
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
1560
  	struct net *net = dev_net(in_dev->dev);
a816c7c71   Jay Vosburgh   bonding: Improve ...
1561

4aa5dee4d   Jiri Pirko   net: convert rese...
1562
1563
1564
  	ASSERT_RTNL();
  
  	for_each_pmc_rtnl(in_dev, im) {
866f3b25a   Eric Dumazet   bonding: IGMP han...
1565
1566
  		if (im->multiaddr == IGMP_ALL_HOSTS)
  			continue;
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
1567
  		if (ipv4_is_local_multicast(im->multiaddr) &&
87a8a2ae6   Nikolay Borisov   igmp: Namespaceif...
1568
  		    !net->ipv4.sysctl_igmp_llm_reports)
df2cf4a78   Philip Downey   IGMP: Inhibit rep...
1569
  			continue;
a816c7c71   Jay Vosburgh   bonding: Improve ...
1570

866f3b25a   Eric Dumazet   bonding: IGMP han...
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
  		/* a failover is happening and switches
  		 * must be notified immediately
  		 */
  		if (IGMP_V1_SEEN(in_dev))
  			type = IGMP_HOST_MEMBERSHIP_REPORT;
  		else if (IGMP_V2_SEEN(in_dev))
  			type = IGMPV2_HOST_MEMBERSHIP_REPORT;
  		else
  			type = IGMPV3_HOST_MEMBERSHIP_REPORT;
  		igmp_send_report(in_dev, im, type);
  	}
a816c7c71   Jay Vosburgh   bonding: Improve ...
1582
1583
1584
1585
  #endif
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1586
1587
   *	A socket has left a multicast group on device dev
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1588
  void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1589
  {
1d7138de8   Eric Dumazet   igmp: RCU convers...
1590
1591
  	struct ip_mc_list *i;
  	struct ip_mc_list __rcu **ip;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1592

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

1d7138de8   Eric Dumazet   igmp: RCU convers...
1595
1596
1597
  	for (ip = &in_dev->mc_list;
  	     (i = rtnl_dereference(*ip)) != NULL;
  	     ip = &i->next_rcu) {
a7e9ff735   Jianjun Kong   net: clean up net...
1598
  		if (i->multiaddr == addr) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1599
  			if (--i->users == 0) {
e98970713   Eric Dumazet   igmp: hash a hash...
1600
  				ip_mc_hash_remove(in_dev, i);
1d7138de8   Eric Dumazet   igmp: RCU convers...
1601
  				*ip = i->next_rcu;
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
1602
  				in_dev->mc_count--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1603
  				igmp_group_dropped(i);
24cf3af3f   Veaceslav Falico   igmp: call ip_mc_...
1604
  				ip_mc_clear_src(i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
  
  				if (!in_dev->dead)
  					ip_rt_multicast_event(in_dev);
  
  				ip_ma_put(i);
  				return;
  			}
  			break;
  		}
  	}
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
1616
  EXPORT_SYMBOL(ip_mc_dec_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1617

75c78500d   Moni Shoua   bonding: remap mu...
1618
1619
1620
1621
  /* Device changing type */
  
  void ip_mc_unmap(struct in_device *in_dev)
  {
1d7138de8   Eric Dumazet   igmp: RCU convers...
1622
  	struct ip_mc_list *pmc;
75c78500d   Moni Shoua   bonding: remap mu...
1623
1624
  
  	ASSERT_RTNL();
1d7138de8   Eric Dumazet   igmp: RCU convers...
1625
1626
  	for_each_pmc_rtnl(in_dev, pmc)
  		igmp_group_dropped(pmc);
75c78500d   Moni Shoua   bonding: remap mu...
1627
1628
1629
1630
  }
  
  void ip_mc_remap(struct in_device *in_dev)
  {
1d7138de8   Eric Dumazet   igmp: RCU convers...
1631
  	struct ip_mc_list *pmc;
75c78500d   Moni Shoua   bonding: remap mu...
1632
1633
  
  	ASSERT_RTNL();
24803f38a   Hangbin Liu   igmp: do not remo...
1634
1635
1636
1637
  	for_each_pmc_rtnl(in_dev, pmc) {
  #ifdef CONFIG_IP_MULTICAST
  		igmpv3_del_delrec(in_dev, pmc);
  #endif
1d7138de8   Eric Dumazet   igmp: RCU convers...
1638
  		igmp_group_added(pmc);
24803f38a   Hangbin Liu   igmp: do not remo...
1639
  	}
75c78500d   Moni Shoua   bonding: remap mu...
1640
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1641
1642
1643
1644
  /* Device going down */
  
  void ip_mc_down(struct in_device *in_dev)
  {
1d7138de8   Eric Dumazet   igmp: RCU convers...
1645
  	struct ip_mc_list *pmc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1646
1647
  
  	ASSERT_RTNL();
1d7138de8   Eric Dumazet   igmp: RCU convers...
1648
1649
  	for_each_pmc_rtnl(in_dev, pmc)
  		igmp_group_dropped(pmc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1650
1651
1652
1653
1654
1655
1656
1657
  
  #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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1658
1659
1660
1661
1662
1663
1664
  #endif
  
  	ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
  }
  
  void ip_mc_init_dev(struct in_device *in_dev)
  {
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1665
  #ifdef CONFIG_IP_MULTICAST
165094afc   Nikolay Borisov   igmp: Namespacify...
1666
  	struct net *net = dev_net(in_dev->dev);
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1667
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1668
  	ASSERT_RTNL();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1669
  #ifdef CONFIG_IP_MULTICAST
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1670
1671
  	setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire,
  			(unsigned long)in_dev);
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1672
1673
  	setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire,
  			(unsigned long)in_dev);
165094afc   Nikolay Borisov   igmp: Namespacify...
1674
  	in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1675
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1676
1677
1678
1679
1680
1681
1682
  	spin_lock_init(&in_dev->mc_tomb_lock);
  }
  
  /* Device going up */
  
  void ip_mc_up(struct in_device *in_dev)
  {
1d7138de8   Eric Dumazet   igmp: RCU convers...
1683
  	struct ip_mc_list *pmc;
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1684
  #ifdef CONFIG_IP_MULTICAST
165094afc   Nikolay Borisov   igmp: Namespacify...
1685
  	struct net *net = dev_net(in_dev->dev);
dcd87999d   Nikolay Borisov   igmp: net: Move i...
1686
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1687
1688
  
  	ASSERT_RTNL();
a9fe8e299   Hannes Frederic Sowa   ipv4: implement i...
1689
  #ifdef CONFIG_IP_MULTICAST
165094afc   Nikolay Borisov   igmp: Namespacify...
1690
  	in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv;
a9fe8e299   Hannes Frederic Sowa   ipv4: implement i...
1691
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1692
  	ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
24803f38a   Hangbin Liu   igmp: do not remo...
1693
1694
1695
1696
  	for_each_pmc_rtnl(in_dev, pmc) {
  #ifdef CONFIG_IP_MULTICAST
  		igmpv3_del_delrec(in_dev, pmc);
  #endif
1d7138de8   Eric Dumazet   igmp: RCU convers...
1697
  		igmp_group_added(pmc);
24803f38a   Hangbin Liu   igmp: do not remo...
1698
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
  }
  
  /*
   *	Device is about to be destroyed: clean up.
   */
  
  void ip_mc_destroy_dev(struct in_device *in_dev)
  {
  	struct ip_mc_list *i;
  
  	ASSERT_RTNL();
  
  	/* Deactivate timers */
  	ip_mc_down(in_dev);
24803f38a   Hangbin Liu   igmp: do not remo...
1713
1714
1715
  #ifdef CONFIG_IP_MULTICAST
  	igmpv3_clear_delrec(in_dev);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1716

1d7138de8   Eric Dumazet   igmp: RCU convers...
1717
1718
  	while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) {
  		in_dev->mc_list = i->next_rcu;
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
1719
  		in_dev->mc_count--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1720
  		ip_ma_put(i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1721
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1722
  }
9e917dca7   Eric Dumazet   net: avoid a dev ...
1723
  /* RTNL is locked */
877acedc0   Daniel Lezcano   netns: Fix crash ...
1724
  static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1725
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1726
1727
1728
1729
  	struct net_device *dev = NULL;
  	struct in_device *idev = NULL;
  
  	if (imr->imr_ifindex) {
877acedc0   Daniel Lezcano   netns: Fix crash ...
1730
  		idev = inetdev_by_index(net, imr->imr_ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1731
1732
1733
  		return idev;
  	}
  	if (imr->imr_address.s_addr) {
9e917dca7   Eric Dumazet   net: avoid a dev ...
1734
  		dev = __ip_dev_find(net, imr->imr_address.s_addr, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1735
1736
  		if (!dev)
  			return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1737
  	}
b23dd4fe4   David S. Miller   ipv4: Make output...
1738
  	if (!dev) {
78fbfd8a6   David S. Miller   ipv4: Create and ...
1739
1740
1741
  		struct rtable *rt = ip_route_output(net,
  						    imr->imr_multiaddr.s_addr,
  						    0, 0, 0);
b23dd4fe4   David S. Miller   ipv4: Make output...
1742
1743
1744
1745
  		if (!IS_ERR(rt)) {
  			dev = rt->dst.dev;
  			ip_rt_put(rt);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1746
1747
1748
  	}
  	if (dev) {
  		imr->imr_ifindex = dev->ifindex;
e5ed63991   Herbert Xu   [IPV4]: Replace _...
1749
  		idev = __in_dev_get_rtnl(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1750
1751
1752
1753
1754
1755
1756
  	}
  	return idev;
  }
  
  /*
   *	Join a socket to a group
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1757
1758
  
  static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1759
  	__be32 *psfsrc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1760
1761
1762
1763
1764
  {
  	struct ip_sf_list *psf, *psf_prev;
  	int rv = 0;
  
  	psf_prev = NULL;
c71151f05   Weilong Chen   ipv4: fix all spa...
1765
  	for (psf = pmc->sources; psf; psf = psf->sf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
  		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;
165094afc   Nikolay Borisov   igmp: Namespacify...
1781
  		struct net *net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
  #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)) {
165094afc   Nikolay Borisov   igmp: Namespacify...
1792
  			psf->sf_crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
  			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...
1806
1807
  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
1808
1809
1810
1811
1812
1813
1814
  {
  	struct ip_mc_list *pmc;
  	int	changerec = 0;
  	int	i, err;
  
  	if (!in_dev)
  		return -ENODEV;
1d7138de8   Eric Dumazet   igmp: RCU convers...
1815
1816
  	rcu_read_lock();
  	for_each_pmc_rcu(in_dev, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1817
1818
1819
1820
1821
  		if (*pmca == pmc->multiaddr)
  			break;
  	}
  	if (!pmc) {
  		/* MCA not found?? bug */
1d7138de8   Eric Dumazet   igmp: RCU convers...
1822
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1823
1824
1825
  		return -ESRCH;
  	}
  	spin_lock_bh(&pmc->lock);
1d7138de8   Eric Dumazet   igmp: RCU convers...
1826
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
  #ifdef CONFIG_IP_MULTICAST
  	sf_markstate(pmc);
  #endif
  	if (!delta) {
  		err = -EINVAL;
  		if (!pmc->sfcount[sfmode])
  			goto out_unlock;
  		pmc->sfcount[sfmode]--;
  	}
  	err = 0;
c71151f05   Weilong Chen   ipv4: fix all spa...
1837
  	for (i = 0; i < sfcount; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
  		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;
165094afc   Nikolay Borisov   igmp: Namespacify...
1849
  		struct net *net = dev_net(in_dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1850
1851
1852
1853
1854
  #endif
  
  		/* filter mode change */
  		pmc->sfmode = MCAST_INCLUDE;
  #ifdef CONFIG_IP_MULTICAST
165094afc   Nikolay Borisov   igmp: Namespacify...
1855
  		pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1856
  		in_dev->mr_ifc_count = pmc->crcount;
c71151f05   Weilong Chen   ipv4: fix all spa...
1857
  		for (psf = pmc->sources; psf; psf = psf->sf_next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
  			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,
5eb81e891   Jun Zhao   ipv4 : igmp : Del...
1873
  	__be32 *psfsrc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1874
1875
1876
1877
  {
  	struct ip_sf_list *psf, *psf_prev;
  
  	psf_prev = NULL;
c71151f05   Weilong Chen   ipv4: fix all spa...
1878
  	for (psf = pmc->sources; psf; psf = psf->sf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1879
1880
1881
1882
1883
  		if (psf->sf_inaddr == *psfsrc)
  			break;
  		psf_prev = psf;
  	}
  	if (!psf) {
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
1884
  		psf = kzalloc(sizeof(*psf), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1885
1886
  		if (!psf)
  			return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
  		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];
c71151f05   Weilong Chen   ipv4: fix all spa...
1905
  	for (psf = pmc->sources; psf; psf = psf->sf_next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
  		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...
1916
  	struct ip_sf_list *psf, *dpsf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1917
1918
1919
1920
1921
  	int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
  	int qrv = pmc->interface->mr_qrv;
  	int new_in, rv;
  
  	rv = 0;
c71151f05   Weilong Chen   ipv4: fix all spa...
1922
  	for (psf = pmc->sources; psf; psf = psf->sf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1923
1924
1925
1926
1927
  		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...
1928
1929
  		if (new_in) {
  			if (!psf->sf_oldin) {
76edc6051   Al Viro   [PATCH] ipv4 NULL...
1930
  				struct ip_sf_list *prev = NULL;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1931

c71151f05   Weilong Chen   ipv4: fix all spa...
1932
  				for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) {
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
  					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
  			 */
c71151f05   Weilong Chen   ipv4: fix all spa...
1954
  			for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next)
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1955
1956
1957
  				if (dpsf->sf_inaddr == psf->sf_inaddr)
  					break;
  			if (!dpsf) {
3ed37a6fa   Joe Perches   net/ipv4/igmp.c: ...
1958
  				dpsf = kmalloc(sizeof(*dpsf), GFP_ATOMIC);
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1959
1960
1961
1962
1963
1964
1965
1966
  				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
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
  			rv++;
  		}
  	}
  	return rv;
  }
  #endif
  
  /*
   * Add multicast source filter list to the interface list
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1977
1978
  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
1979
1980
1981
1982
1983
1984
1985
  {
  	struct ip_mc_list *pmc;
  	int	isexclude;
  	int	i, err;
  
  	if (!in_dev)
  		return -ENODEV;
1d7138de8   Eric Dumazet   igmp: RCU convers...
1986
1987
  	rcu_read_lock();
  	for_each_pmc_rcu(in_dev, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1988
1989
1990
1991
1992
  		if (*pmca == pmc->multiaddr)
  			break;
  	}
  	if (!pmc) {
  		/* MCA not found?? bug */
1d7138de8   Eric Dumazet   igmp: RCU convers...
1993
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1994
1995
1996
  		return -ESRCH;
  	}
  	spin_lock_bh(&pmc->lock);
1d7138de8   Eric Dumazet   igmp: RCU convers...
1997
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1998
1999
2000
2001
2002
2003
2004
2005
  
  #ifdef CONFIG_IP_MULTICAST
  	sf_markstate(pmc);
  #endif
  	isexclude = pmc->sfmode == MCAST_EXCLUDE;
  	if (!delta)
  		pmc->sfcount[sfmode]++;
  	err = 0;
c71151f05   Weilong Chen   ipv4: fix all spa...
2006
  	for (i = 0; i < sfcount; i++) {
5eb81e891   Jun Zhao   ipv4 : igmp : Del...
2007
  		err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2008
2009
2010
2011
2012
  		if (err)
  			break;
  	}
  	if (err) {
  		int j;
685f94e6d   Jun Zhao   ipv4 : igmp : fix...
2013
2014
  		if (!delta)
  			pmc->sfcount[sfmode]--;
c71151f05   Weilong Chen   ipv4: fix all spa...
2015
  		for (j = 0; j < i; j++)
a1889c0d2   Julia Lawall   net: adjust array...
2016
  			(void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2017
2018
  	} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
  #ifdef CONFIG_IP_MULTICAST
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2019
  		struct ip_sf_list *psf;
165094afc   Nikolay Borisov   igmp: Namespacify...
2020
  		struct net *net = dev_net(pmc->interface->dev);
cfcabdcc2   Stephen Hemminger   [NET]: sparse war...
2021
  		in_dev = pmc->interface;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2022
2023
2024
2025
2026
2027
2028
2029
2030
  #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 */
165094afc   Nikolay Borisov   igmp: Namespacify...
2031
  		pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2032
  		in_dev->mr_ifc_count = pmc->crcount;
c71151f05   Weilong Chen   ipv4: fix all spa...
2033
  		for (psf = pmc->sources; psf; psf = psf->sf_next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
  			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)
  {
c38b7d327   WANG Cong   igmp: acquire pmc...
2046
  	struct ip_sf_list *psf, *nextpsf, *tomb, *sources;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2047

c38b7d327   WANG Cong   igmp: acquire pmc...
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
  	spin_lock_bh(&pmc->lock);
  	tomb = pmc->tomb;
  	pmc->tomb = NULL;
  	sources = pmc->sources;
  	pmc->sources = NULL;
  	pmc->sfmode = MCAST_EXCLUDE;
  	pmc->sfcount[MCAST_INCLUDE] = 0;
  	pmc->sfcount[MCAST_EXCLUDE] = 1;
  	spin_unlock_bh(&pmc->lock);
  
  	for (psf = tomb; psf; psf = nextpsf) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2059
2060
2061
  		nextpsf = psf->sf_next;
  		kfree(psf);
  	}
c38b7d327   WANG Cong   igmp: acquire pmc...
2062
  	for (psf = sources; psf; psf = nextpsf) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2063
2064
2065
  		nextpsf = psf->sf_next;
  		kfree(psf);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2066
  }
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
2067
2068
2069
2070
  /* Join a multicast group
   */
  
  int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2071
  {
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
2072
  	__be32 addr = imr->imr_multiaddr.s_addr;
959d10f6b   Eric Dumazet   igmp: add __ip_mc...
2073
  	struct ip_mc_socklist *iml, *i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2074
2075
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
877acedc0   Daniel Lezcano   netns: Fix crash ...
2076
  	struct net *net = sock_net(sk);
ca9b907d1   David L Stevens   [IPV4]: multicast...
2077
  	int ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2078
  	int count = 0;
959d10f6b   Eric Dumazet   igmp: add __ip_mc...
2079
2080
2081
  	int err;
  
  	ASSERT_RTNL();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2082

f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2083
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2084
  		return -EINVAL;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2085
  	in_dev = ip_mc_find_dev(net, imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2086
2087
  
  	if (!in_dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2088
2089
2090
  		err = -ENODEV;
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2091
  	err = -EADDRINUSE;
ca9b907d1   David L Stevens   [IPV4]: multicast...
2092
  	ifindex = imr->imr_ifindex;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2093
  	for_each_pmc_rtnl(inet, i) {
ca9b907d1   David L Stevens   [IPV4]: multicast...
2094
2095
  		if (i->multi.imr_multiaddr.s_addr == addr &&
  		    i->multi.imr_ifindex == ifindex)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2096
  			goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2097
2098
2099
  		count++;
  	}
  	err = -ENOBUFS;
815c52700   Nikolay Borisov   igmp: Namespaceif...
2100
  	if (count >= net->ipv4.sysctl_igmp_max_memberships)
ca9b907d1   David L Stevens   [IPV4]: multicast...
2101
  		goto done;
a7e9ff735   Jianjun Kong   net: clean up net...
2102
  	iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
51456b291   Ian Morris   ipv4: coding styl...
2103
  	if (!iml)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2104
  		goto done;
ca9b907d1   David L Stevens   [IPV4]: multicast...
2105

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2106
  	memcpy(&iml->multi, imr, sizeof(*imr));
1d7138de8   Eric Dumazet   igmp: RCU convers...
2107
  	iml->next_rcu = inet->mc_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2108
2109
  	iml->sflist = NULL;
  	iml->sfmode = MCAST_EXCLUDE;
cf778b00e   Eric Dumazet   net: reintroduce ...
2110
  	rcu_assign_pointer(inet->mc_list, iml);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2111
  	ip_mc_inc_group(in_dev, addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2112
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2113
  done:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2114
2115
  	return err;
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
2116
  EXPORT_SYMBOL(ip_mc_join_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2117
2118
2119
2120
  
  static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
  			   struct in_device *in_dev)
  {
1d7138de8   Eric Dumazet   igmp: RCU convers...
2121
  	struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2122
  	int err;
51456b291   Ian Morris   ipv4: coding styl...
2123
  	if (!psf) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2124
2125
2126
2127
2128
  		/* any-source empty exclude case */
  		return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
  			iml->sfmode, 0, NULL, 0);
  	}
  	err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2129
  			iml->sfmode, psf->sl_count, psf->sl_addr, 0);
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
2130
  	RCU_INIT_POINTER(iml->sflist, NULL);
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2131
2132
  	/* decrease mem now to avoid the memleak warning */
  	atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc);
7519cce48   Lai Jiangshan   net,rcu: convert ...
2133
  	kfree_rcu(psf, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2134
2135
  	return err;
  }
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
2136
  int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2137
2138
  {
  	struct inet_sock *inet = inet_sk(sk);
1d7138de8   Eric Dumazet   igmp: RCU convers...
2139
2140
  	struct ip_mc_socklist *iml;
  	struct ip_mc_socklist __rcu **imlp;
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
2141
  	struct in_device *in_dev;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2142
  	struct net *net = sock_net(sk);
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
2143
  	__be32 group = imr->imr_multiaddr.s_addr;
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
2144
  	u32 ifindex;
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
2145
  	int ret = -EADDRNOTAVAIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2146

959d10f6b   Eric Dumazet   igmp: add __ip_mc...
2147
  	ASSERT_RTNL();
877acedc0   Daniel Lezcano   netns: Fix crash ...
2148
  	in_dev = ip_mc_find_dev(net, imr);
4eba7bb1d   Andrew Lunn   ipv4: igmp: Allow...
2149
  	if (!imr->imr_ifindex && !imr->imr_address.s_addr && !in_dev) {
52ad353a5   dingtianhong   igmp: fix the pro...
2150
2151
2152
  		ret = -ENODEV;
  		goto out;
  	}
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
2153
  	ifindex = imr->imr_ifindex;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2154
2155
2156
  	for (imlp = &inet->mc_list;
  	     (iml = rtnl_dereference(*imlp)) != NULL;
  	     imlp = &iml->next_rcu) {
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
2157
2158
2159
2160
2161
2162
2163
2164
  		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
2165

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

1d7138de8   Eric Dumazet   igmp: RCU convers...
2168
  		*imlp = iml->next_rcu;
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
2169

4eba7bb1d   Andrew Lunn   ipv4: igmp: Allow...
2170
2171
  		if (in_dev)
  			ip_mc_dec_group(in_dev, group);
959d10f6b   Eric Dumazet   igmp: add __ip_mc...
2172

c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2173
2174
  		/* decrease mem now to avoid the memleak warning */
  		atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
10d50e748   Lai Jiangshan   net,rcu: convert ...
2175
  		kfree_rcu(iml, rcu);
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
2176
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2177
  	}
52ad353a5   dingtianhong   igmp: fix the pro...
2178
  out:
959d10f6b   Eric Dumazet   igmp: add __ip_mc...
2179
2180
  	return ret;
  }
193ba9245   stephen hemminger   igmp: export symb...
2181
  EXPORT_SYMBOL(ip_mc_leave_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2182
2183
2184
2185
2186
2187
  
  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...
2188
  	__be32 addr = mreqs->imr_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2189
2190
2191
2192
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev = NULL;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *psl;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2193
  	struct net *net = sock_net(sk);
8cdaaa15d   David L Stevens   [IPV4]: multicast...
2194
  	int leavegroup = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2195
  	int i, j, rv;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2196
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2197
  		return -EINVAL;
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
2198
  	ASSERT_RTNL();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2199
2200
2201
2202
  
  	imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr;
  	imr.imr_address.s_addr = mreqs->imr_interface;
  	imr.imr_ifindex = ifindex;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2203
  	in_dev = ip_mc_find_dev(net, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2204
2205
2206
2207
2208
2209
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
  	err = -EADDRNOTAVAIL;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2210
  	for_each_pmc_rtnl(inet, pmc) {
f64f9e719   Joe Perches   net: Move && and ...
2211
2212
2213
  		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
2214
2215
  			break;
  	}
917f2f105   David L Stevens   [IPV4]: multicast...
2216
2217
  	if (!pmc) {		/* must have a prior join */
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2218
  		goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
2219
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2220
2221
  	/* if a source filter was set, must be the same mode as before */
  	if (pmc->sflist) {
917f2f105   David L Stevens   [IPV4]: multicast...
2222
2223
  		if (pmc->sfmode != omode) {
  			err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2224
  			goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
2225
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2226
2227
2228
  	} 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...
2229
  		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2230
2231
2232
  			NULL, 0);
  		pmc->sfmode = omode;
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
2233
  	psl = rtnl_dereference(pmc->sflist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2234
2235
  	if (!add) {
  		if (!psl)
917f2f105   David L Stevens   [IPV4]: multicast...
2236
  			goto done;	/* err = -EADDRNOTAVAIL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2237
  		rv = !0;
c71151f05   Weilong Chen   ipv4: fix all spa...
2238
  		for (i = 0; i < psl->sl_count; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2239
  			rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
63007727e   Al Viro   [IPV4]: trivial i...
2240
  				sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2241
2242
2243
2244
  			if (rv == 0)
  				break;
  		}
  		if (rv)		/* source not found */
917f2f105   David L Stevens   [IPV4]: multicast...
2245
  			goto done;	/* err = -EADDRNOTAVAIL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2246

8cdaaa15d   David L Stevens   [IPV4]: multicast...
2247
2248
2249
2250
2251
  		/* 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
2252
  		/* update the interface filter */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2253
  		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2254
  			&mreqs->imr_sourceaddr, 1);
c71151f05   Weilong Chen   ipv4: fix all spa...
2255
  		for (j = i+1; j < psl->sl_count; j++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2256
2257
2258
2259
2260
2261
  			psl->sl_addr[j-1] = psl->sl_addr[j];
  		psl->sl_count--;
  		err = 0;
  		goto done;
  	}
  	/* else, add a new source to the filter */
166b6b2d6   Nikolay Borisov   igmp: Namespaceif...
2262
  	if (psl && psl->sl_count >= net->ipv4.sysctl_igmp_max_msf) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2263
2264
2265
2266
2267
2268
2269
2270
2271
  		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...
2272
  		newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2273
2274
2275
2276
2277
2278
2279
  		if (!newpsl) {
  			err = -ENOBUFS;
  			goto done;
  		}
  		newpsl->sl_max = count;
  		newpsl->sl_count = count - IP_SFBLOCK;
  		if (psl) {
c71151f05   Weilong Chen   ipv4: fix all spa...
2280
  			for (i = 0; i < psl->sl_count; i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2281
  				newpsl->sl_addr[i] = psl->sl_addr[i];
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2282
2283
  			/* decrease mem now to avoid the memleak warning */
  			atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
7519cce48   Lai Jiangshan   net,rcu: convert ...
2284
  			kfree_rcu(psl, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2285
  		}
cf778b00e   Eric Dumazet   net: reintroduce ...
2286
  		rcu_assign_pointer(pmc->sflist, newpsl);
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2287
  		psl = newpsl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2288
2289
  	}
  	rv = 1;	/* > 0 for insert logic below if sl_count is 0 */
c71151f05   Weilong Chen   ipv4: fix all spa...
2290
  	for (i = 0; i < psl->sl_count; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2291
  		rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
63007727e   Al Viro   [IPV4]: trivial i...
2292
  			sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2293
2294
2295
2296
2297
  		if (rv == 0)
  			break;
  	}
  	if (rv == 0)		/* address already there is an error */
  		goto done;
c71151f05   Weilong Chen   ipv4: fix all spa...
2298
  	for (j = psl->sl_count-1; j >= i; j--)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2299
2300
2301
2302
2303
  		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...
2304
  	ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2305
2306
  		&mreqs->imr_sourceaddr, 1);
  done:
8cdaaa15d   David L Stevens   [IPV4]: multicast...
2307
  	if (leavegroup)
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
2308
  		err = ip_mc_leave_group(sk, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2309
2310
2311
2312
2313
  	return err;
  }
  
  int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
  {
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2314
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2315
  	struct ip_mreqn	imr;
63007727e   Al Viro   [IPV4]: trivial i...
2316
  	__be32 addr = msf->imsf_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2317
2318
2319
2320
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *newpsl, *psl;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2321
  	struct net *net = sock_net(sk);
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2322
  	int leavegroup = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2323

f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2324
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2325
2326
2327
2328
  		return -EINVAL;
  	if (msf->imsf_fmode != MCAST_INCLUDE &&
  	    msf->imsf_fmode != MCAST_EXCLUDE)
  		return -EINVAL;
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
2329
  	ASSERT_RTNL();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2330
2331
2332
2333
  
  	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
  	imr.imr_address.s_addr = msf->imsf_interface;
  	imr.imr_ifindex = ifindex;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2334
  	in_dev = ip_mc_find_dev(net, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2335
2336
2337
2338
2339
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2340

9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2341
2342
2343
2344
2345
  	/* special case - (INCLUDE, empty) == LEAVE_GROUP */
  	if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) {
  		leavegroup = 1;
  		goto done;
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
2346
  	for_each_pmc_rtnl(inet, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2347
2348
2349
2350
  		if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr &&
  		    pmc->multi.imr_ifindex == imr.imr_ifindex)
  			break;
  	}
917f2f105   David L Stevens   [IPV4]: multicast...
2351
2352
  	if (!pmc) {		/* must have a prior join */
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2353
  		goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
2354
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2355
  	if (msf->imsf_numsrc) {
8b3a70058   Kris Katterjohn   [NET]: Remove mor...
2356
2357
  		newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc),
  							   GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
  		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...
2371
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2372
  		newpsl = NULL;
8713dbf05   Yan Zheng   [MCAST]: ip[6]_mc...
2373
2374
2375
  		(void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr,
  				     msf->imsf_fmode, 0, NULL, 0);
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
2376
  	psl = rtnl_dereference(pmc->sflist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2377
2378
2379
  	if (psl) {
  		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
  			psl->sl_count, psl->sl_addr, 0);
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2380
2381
  		/* decrease mem now to avoid the memleak warning */
  		atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
7519cce48   Lai Jiangshan   net,rcu: convert ...
2382
  		kfree_rcu(psl, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2383
2384
2385
  	} else
  		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
  			0, NULL, 0);
cf778b00e   Eric Dumazet   net: reintroduce ...
2386
  	rcu_assign_pointer(pmc->sflist, newpsl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2387
  	pmc->sfmode = msf->imsf_fmode;
917f2f105   David L Stevens   [IPV4]: multicast...
2388
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2389
  done:
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2390
2391
  	if (leavegroup)
  		err = ip_mc_leave_group(sk, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2392
2393
2394
2395
2396
2397
2398
2399
  	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...
2400
  	__be32 addr = msf->imsf_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2401
2402
2403
2404
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *psl;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2405
  	struct net *net = sock_net(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2406

87e9f0315   WANG Cong   ipv4: fix a poten...
2407
  	ASSERT_RTNL();
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2408
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2409
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2410
2411
2412
  	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
  	imr.imr_address.s_addr = msf->imsf_interface;
  	imr.imr_ifindex = 0;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2413
  	in_dev = ip_mc_find_dev(net, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2414
2415
2416
2417
2418
2419
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
  	err = -EADDRNOTAVAIL;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2420
  	for_each_pmc_rtnl(inet, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2421
2422
2423
2424
2425
2426
2427
  		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;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2428
  	psl = rtnl_dereference(pmc->sflist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
  	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:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2447
2448
2449
2450
2451
2452
2453
2454
  	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...
2455
  	__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2456
2457
2458
  	struct ip_mc_socklist *pmc;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *psl;
87e9f0315   WANG Cong   ipv4: fix a poten...
2459
  	ASSERT_RTNL();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2460
2461
2462
2463
  	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: ...
2464
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2465
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2466
  	err = -EADDRNOTAVAIL;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2467
  	for_each_pmc_rtnl(inet, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2468
2469
2470
2471
2472
2473
2474
  		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;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2475
  	psl = rtnl_dereference(pmc->sflist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2476
2477
2478
2479
2480
2481
2482
  	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;
  	}
c71151f05   Weilong Chen   ipv4: fix all spa...
2483
  	for (i = 0; i < copycount; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
  		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:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2495
2496
2497
2498
2499
2500
  	return err;
  }
  
  /*
   * check if a multicast source filter allows delivery for a given <src,dst,intf>
   */
60d9b0314   David Ahern   net: ipv4: add se...
2501
2502
  int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr,
  		   int dif, int sdif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2503
2504
2505
2506
2507
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_mc_socklist *pmc;
  	struct ip_sf_socklist *psl;
  	int i;
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2508
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2509

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

c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2514
  	rcu_read_lock();
1d7138de8   Eric Dumazet   igmp: RCU convers...
2515
  	for_each_pmc_rcu(inet, pmc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2516
  		if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
60d9b0314   David Ahern   net: ipv4: add se...
2517
2518
  		    (pmc->multi.imr_ifindex == dif ||
  		     (sdif && pmc->multi.imr_ifindex == sdif)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2519
2520
  			break;
  	}
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2521
  	ret = inet->mc_all;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2522
  	if (!pmc)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2523
  		goto unlock;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2524
  	psl = rcu_dereference(pmc->sflist);
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2525
  	ret = (pmc->sfmode == MCAST_EXCLUDE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2526
  	if (!psl)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2527
  		goto unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2528

c71151f05   Weilong Chen   ipv4: fix all spa...
2529
  	for (i = 0; i < psl->sl_count; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2530
2531
2532
  		if (psl->sl_addr[i] == rmt_addr)
  			break;
  	}
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2533
  	ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2534
  	if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2535
  		goto unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2536
  	if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2537
2538
2539
2540
2541
2542
  		goto unlock;
  	ret = 1;
  unlock:
  	rcu_read_unlock();
  out:
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
  }
  
  /*
   *	A socket is closing.
   */
  
  void ip_mc_drop_socket(struct sock *sk)
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_mc_socklist *iml;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2553
  	struct net *net = sock_net(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2554

51456b291   Ian Morris   ipv4: coding styl...
2555
  	if (!inet->mc_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2556
2557
2558
  		return;
  
  	rtnl_lock();
1d7138de8   Eric Dumazet   igmp: RCU convers...
2559
  	while ((iml = rtnl_dereference(inet->mc_list)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2560
  		struct in_device *in_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2561

1d7138de8   Eric Dumazet   igmp: RCU convers...
2562
  		inet->mc_list = iml->next_rcu;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2563
  		in_dev = inetdev_by_index(net, iml->multi.imr_ifindex);
bb699cbca   Michal Ruzicka   [IPV4]: Possible ...
2564
  		(void) ip_mc_leave_src(sk, iml, in_dev);
00db41243   Ian Morris   ipv4: coding styl...
2565
  		if (in_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2566
  			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2567
2568
  		/* decrease mem now to avoid the memleak warning */
  		atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
10d50e748   Lai Jiangshan   net,rcu: convert ...
2569
  		kfree_rcu(iml, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2570
2571
2572
  	}
  	rtnl_unlock();
  }
dbdd9a52e   David S. Miller   ipv4: Remove redu...
2573
  /* called with rcu_read_lock() */
2094acbb7   Alexander Duyck   net/ipv4: Pass pr...
2574
  int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u8 proto)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2575
2576
  {
  	struct ip_mc_list *im;
e98970713   Eric Dumazet   igmp: hash a hash...
2577
  	struct ip_mc_list __rcu **mc_hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2578
2579
  	struct ip_sf_list *psf;
  	int rv = 0;
e98970713   Eric Dumazet   igmp: hash a hash...
2580
2581
  	mc_hash = rcu_dereference(in_dev->mc_hash);
  	if (mc_hash) {
c70eba745   Eric Dumazet   igmp: fix new spa...
2582
  		u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG);
e98970713   Eric Dumazet   igmp: hash a hash...
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
  
  		for (im = rcu_dereference(mc_hash[hash]);
  		     im != NULL;
  		     im = rcu_dereference(im->next_hash)) {
  			if (im->multiaddr == mc_addr)
  				break;
  		}
  	} else {
  		for_each_pmc_rcu(in_dev, im) {
  			if (im->multiaddr == mc_addr)
  				break;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2595
2596
2597
2598
2599
  	}
  	if (im && proto == IPPROTO_IGMP) {
  		rv = 1;
  	} else if (im) {
  		if (src_addr) {
c71151f05   Weilong Chen   ipv4: fix all spa...
2600
  			for (psf = im->sources; psf; psf = psf->sf_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
  				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 */
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2613
2614
2615
2616
2617
  	return rv;
  }
  
  #if defined(CONFIG_PROC_FS)
  struct igmp_mc_iter_state {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2618
  	struct seq_net_private p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2619
2620
2621
2622
2623
2624
2625
2626
  	struct net_device *dev;
  	struct in_device *in_dev;
  };
  
  #define	igmp_mc_seq_private(seq)	((struct igmp_mc_iter_state *)(seq)->private)
  
  static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2627
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2628
2629
  	struct ip_mc_list *im = NULL;
  	struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
7562f876c   Pavel Emelianov   [NET]: Rework dev...
2630
  	state->in_dev = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2631
  	for_each_netdev_rcu(net, state->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2632
  		struct in_device *in_dev;
6baff1503   Eric Dumazet   igmp: Use next_ne...
2633
2634
  
  		in_dev = __in_dev_get_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2635
2636
  		if (!in_dev)
  			continue;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2637
  		im = rcu_dereference(in_dev->mc_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2638
2639
2640
2641
  		if (im) {
  			state->in_dev = in_dev;
  			break;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2642
2643
2644
2645
2646
2647
2648
  	}
  	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);
6baff1503   Eric Dumazet   igmp: Use next_ne...
2649

1d7138de8   Eric Dumazet   igmp: RCU convers...
2650
2651
  	im = rcu_dereference(im->next_rcu);
  	while (!im) {
6baff1503   Eric Dumazet   igmp: Use next_ne...
2652
  		state->dev = next_net_device_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2653
2654
2655
2656
  		if (!state->dev) {
  			state->in_dev = NULL;
  			break;
  		}
6baff1503   Eric Dumazet   igmp: Use next_ne...
2657
  		state->in_dev = __in_dev_get_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2658
2659
  		if (!state->in_dev)
  			continue;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2660
  		im = rcu_dereference(state->in_dev->mc_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
  	}
  	return im;
  }
  
  static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos)
  {
  	struct ip_mc_list *im = igmp_mc_get_first(seq);
  	if (im)
  		while (pos && (im = igmp_mc_get_next(seq, im)) != NULL)
  			--pos;
  	return pos ? NULL : im;
  }
  
  static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2675
  	__acquires(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2676
  {
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2677
  	rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
  	return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
  }
  
  static void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct ip_mc_list *im;
  	if (v == SEQ_START_TOKEN)
  		im = igmp_mc_get_first(seq);
  	else
  		im = igmp_mc_get_next(seq, v);
  	++*pos;
  	return im;
  }
  
  static void igmp_mc_seq_stop(struct seq_file *seq, void *v)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2693
  	__releases(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2694
2695
  {
  	struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
1d7138de8   Eric Dumazet   igmp: RCU convers...
2696
2697
  
  	state->in_dev = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2698
  	state->dev = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2699
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2700
2701
2702
2703
2704
  }
  
  static int igmp_mc_seq_show(struct seq_file *seq, void *v)
  {
  	if (v == SEQ_START_TOKEN)
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2705
  		seq_puts(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2706
2707
2708
2709
2710
2711
  			 "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;
a399a8053   Eric Dumazet   time: jiffies_del...
2712
  		long delta;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2713
2714
2715
2716
2717
2718
2719
  #ifdef CONFIG_IP_MULTICAST
  		querier = IGMP_V1_SEEN(state->in_dev) ? "V1" :
  			  IGMP_V2_SEEN(state->in_dev) ? "V2" :
  			  "V3";
  #else
  		querier = "NONE";
  #endif
e6b688838   Andreea-Cristina Bernat   net/ipv4/igmp.c: ...
2720
  		if (rcu_access_pointer(state->in_dev->mc_list) == im) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2721
2722
  			seq_printf(seq, "%d\t%-10s: %5d %7s
  ",
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
2723
  				   state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2724
  		}
a399a8053   Eric Dumazet   time: jiffies_del...
2725
  		delta = im->timer.expires - jiffies;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2726
  		seq_printf(seq,
338fcf988   Alexey Dobriyan   [IPV4] igmp: Fixu...
2727
2728
  			   "\t\t\t\t%08X %5d %d:%08lX\t\t%d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2729
  			   im->multiaddr, im->users,
a399a8053   Eric Dumazet   time: jiffies_del...
2730
2731
  			   im->tm_running,
  			   im->tm_running ? jiffies_delta_to_clock_t(delta) : 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2732
2733
2734
2735
  			   im->reporter);
  	}
  	return 0;
  }
f690808e1   Stephen Hemminger   [NET]: make seq_o...
2736
  static const struct seq_operations igmp_mc_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2737
2738
2739
2740
2741
2742
2743
2744
  	.start	=	igmp_mc_seq_start,
  	.next	=	igmp_mc_seq_next,
  	.stop	=	igmp_mc_seq_stop,
  	.show	=	igmp_mc_seq_show,
  };
  
  static int igmp_mc_seq_open(struct inode *inode, struct file *file)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2745
  	return seq_open_net(inode, file, &igmp_mc_seq_ops,
cf7732e4c   Pavel Emelyanov   [NET]: Make core ...
2746
  			sizeof(struct igmp_mc_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2747
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2748
  static const struct file_operations igmp_mc_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2749
2750
2751
2752
  	.owner		=	THIS_MODULE,
  	.open		=	igmp_mc_seq_open,
  	.read		=	seq_read,
  	.llseek		=	seq_lseek,
7091e728c   Alexey Dobriyan   netns: igmp: make...
2753
  	.release	=	seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2754
2755
2756
  };
  
  struct igmp_mcf_iter_state {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2757
  	struct seq_net_private p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2758
2759
2760
2761
2762
2763
2764
2765
2766
  	struct net_device *dev;
  	struct in_device *idev;
  	struct ip_mc_list *im;
  };
  
  #define igmp_mcf_seq_private(seq)	((struct igmp_mcf_iter_state *)(seq)->private)
  
  static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2767
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2768
2769
2770
  	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...
2771
2772
  	state->idev = NULL;
  	state->im = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2773
  	for_each_netdev_rcu(net, state->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2774
  		struct in_device *idev;
6baff1503   Eric Dumazet   igmp: Use next_ne...
2775
  		idev = __in_dev_get_rcu(state->dev);
51456b291   Ian Morris   ipv4: coding styl...
2776
  		if (unlikely(!idev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2777
  			continue;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2778
  		im = rcu_dereference(idev->mc_list);
00db41243   Ian Morris   ipv4: coding styl...
2779
  		if (likely(im)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2780
2781
  			spin_lock_bh(&im->lock);
  			psf = im->sources;
00db41243   Ian Morris   ipv4: coding styl...
2782
  			if (likely(psf)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2783
2784
2785
2786
2787
2788
  				state->im = im;
  				state->idev = idev;
  				break;
  			}
  			spin_unlock_bh(&im->lock);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
  	}
  	return psf;
  }
  
  static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf)
  {
  	struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
  
  	psf = psf->sf_next;
  	while (!psf) {
  		spin_unlock_bh(&state->im->lock);
  		state->im = state->im->next;
  		while (!state->im) {
6baff1503   Eric Dumazet   igmp: Use next_ne...
2802
  			state->dev = next_net_device_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2803
2804
2805
2806
  			if (!state->dev) {
  				state->idev = NULL;
  				goto out;
  			}
6baff1503   Eric Dumazet   igmp: Use next_ne...
2807
  			state->idev = __in_dev_get_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2808
2809
  			if (!state->idev)
  				continue;
1d7138de8   Eric Dumazet   igmp: RCU convers...
2810
  			state->im = rcu_dereference(state->idev->mc_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
  		}
  		if (!state->im)
  			break;
  		spin_lock_bh(&state->im->lock);
  		psf = state->im->sources;
  	}
  out:
  	return psf;
  }
  
  static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos)
  {
  	struct ip_sf_list *psf = igmp_mcf_get_first(seq);
  	if (psf)
  		while (pos && (psf = igmp_mcf_get_next(seq, psf)) != NULL)
  			--pos;
  	return pos ? NULL : psf;
  }
  
  static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2831
  	__acquires(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2832
  {
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2833
  	rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
  	return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
  }
  
  static void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct ip_sf_list *psf;
  	if (v == SEQ_START_TOKEN)
  		psf = igmp_mcf_get_first(seq);
  	else
  		psf = igmp_mcf_get_next(seq, v);
  	++*pos;
  	return psf;
  }
  
  static void igmp_mcf_seq_stop(struct seq_file *seq, void *v)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2849
  	__releases(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2850
2851
  {
  	struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
00db41243   Ian Morris   ipv4: coding styl...
2852
  	if (likely(state->im)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2853
2854
2855
  		spin_unlock_bh(&state->im->lock);
  		state->im = NULL;
  	}
1d7138de8   Eric Dumazet   igmp: RCU convers...
2856
  	state->idev = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2857
  	state->dev = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2858
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2859
2860
2861
2862
2863
2864
2865
2866
  }
  
  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) {
1744bea1f   Joe Perches   net: Convert SEQ_...
2867
2868
  		seq_puts(seq, "Idx Device        MCA        SRC    INC    EXC
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2869
2870
2871
  	} else {
  		seq_printf(seq,
  			   "%3d %6.6s 0x%08x "
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2872
2873
2874
  			   "0x%08x %6lu %6lu
  ",
  			   state->dev->ifindex, state->dev->name,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2875
2876
2877
2878
2879
2880
2881
  			   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...
2882
  static const struct seq_operations igmp_mcf_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2883
2884
2885
2886
2887
2888
2889
2890
  	.start	=	igmp_mcf_seq_start,
  	.next	=	igmp_mcf_seq_next,
  	.stop	=	igmp_mcf_seq_stop,
  	.show	=	igmp_mcf_seq_show,
  };
  
  static int igmp_mcf_seq_open(struct inode *inode, struct file *file)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2891
  	return seq_open_net(inode, file, &igmp_mcf_seq_ops,
cf7732e4c   Pavel Emelyanov   [NET]: Make core ...
2892
  			sizeof(struct igmp_mcf_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2893
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2894
  static const struct file_operations igmp_mcf_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2895
2896
2897
2898
  	.owner		=	THIS_MODULE,
  	.open		=	igmp_mcf_seq_open,
  	.read		=	seq_read,
  	.llseek		=	seq_lseek,
7091e728c   Alexey Dobriyan   netns: igmp: make...
2899
  	.release	=	seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2900
  };
2c8c1e729   Alexey Dobriyan   net: spread __net...
2901
  static int __net_init igmp_net_init(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2902
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2903
  	struct proc_dir_entry *pde;
93a714d6b   Madhu Challa   multicast: Extend...
2904
  	int err;
7091e728c   Alexey Dobriyan   netns: igmp: make...
2905

d4beaa66a   Gao feng   net: proc: change...
2906
  	pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops);
7091e728c   Alexey Dobriyan   netns: igmp: make...
2907
2908
  	if (!pde)
  		goto out_igmp;
d4beaa66a   Gao feng   net: proc: change...
2909
2910
  	pde = proc_create("mcfilter", S_IRUGO, net->proc_net,
  			  &igmp_mcf_seq_fops);
7091e728c   Alexey Dobriyan   netns: igmp: make...
2911
2912
  	if (!pde)
  		goto out_mcfilter;
93a714d6b   Madhu Challa   multicast: Extend...
2913
2914
2915
2916
2917
2918
2919
2920
  	err = inet_ctl_sock_create(&net->ipv4.mc_autojoin_sk, AF_INET,
  				   SOCK_DGRAM, 0, net);
  	if (err < 0) {
  		pr_err("Failed to initialize the IGMP autojoin socket (err %d)
  ",
  		       err);
  		goto out_sock;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2921
  	return 0;
7091e728c   Alexey Dobriyan   netns: igmp: make...
2922

93a714d6b   Madhu Challa   multicast: Extend...
2923
2924
  out_sock:
  	remove_proc_entry("mcfilter", net->proc_net);
7091e728c   Alexey Dobriyan   netns: igmp: make...
2925
  out_mcfilter:
ece31ffd5   Gao feng   net: proc: change...
2926
  	remove_proc_entry("igmp", net->proc_net);
7091e728c   Alexey Dobriyan   netns: igmp: make...
2927
2928
2929
  out_igmp:
  	return -ENOMEM;
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
2930
  static void __net_exit igmp_net_exit(struct net *net)
7091e728c   Alexey Dobriyan   netns: igmp: make...
2931
  {
ece31ffd5   Gao feng   net: proc: change...
2932
2933
  	remove_proc_entry("mcfilter", net->proc_net);
  	remove_proc_entry("igmp", net->proc_net);
93a714d6b   Madhu Challa   multicast: Extend...
2934
  	inet_ctl_sock_destroy(net->ipv4.mc_autojoin_sk);
7091e728c   Alexey Dobriyan   netns: igmp: make...
2935
2936
2937
2938
2939
2940
  }
  
  static struct pernet_operations igmp_net_ops = {
  	.init = igmp_net_init,
  	.exit = igmp_net_exit,
  };
72c1d3bdd   WANG Cong   ipv4: register ig...
2941
  #endif
7091e728c   Alexey Dobriyan   netns: igmp: make...
2942

4aa5dee4d   Jiri Pirko   net: convert rese...
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
  static int igmp_netdev_event(struct notifier_block *this,
  			     unsigned long event, void *ptr)
  {
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
  	struct in_device *in_dev;
  
  	switch (event) {
  	case NETDEV_RESEND_IGMP:
  		in_dev = __in_dev_get_rtnl(dev);
  		if (in_dev)
  			ip_mc_rejoin_groups(in_dev);
  		break;
  	default:
  		break;
  	}
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block igmp_notifier = {
  	.notifier_call = igmp_netdev_event,
  };
72c1d3bdd   WANG Cong   ipv4: register ig...
2964
  int __init igmp_mc_init(void)
7091e728c   Alexey Dobriyan   netns: igmp: make...
2965
  {
72c1d3bdd   WANG Cong   ipv4: register ig...
2966
  #if defined(CONFIG_PROC_FS)
4aa5dee4d   Jiri Pirko   net: convert rese...
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
  	int err;
  
  	err = register_pernet_subsys(&igmp_net_ops);
  	if (err)
  		return err;
  	err = register_netdevice_notifier(&igmp_notifier);
  	if (err)
  		goto reg_notif_fail;
  	return 0;
  
  reg_notif_fail:
  	unregister_pernet_subsys(&igmp_net_ops);
  	return err;
72c1d3bdd   WANG Cong   ipv4: register ig...
2980
2981
  #else
  	return register_netdevice_notifier(&igmp_notifier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2982
  #endif
72c1d3bdd   WANG Cong   ipv4: register ig...
2983
  }