Blame view

net/ipv4/ipmr.c 57.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   *	IP multicast routing support for mrouted 3.6/3.8
   *
113aa838e   Alan Cox   net: Rationalise ...
4
   *		(c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
10
11
   *	  Linux Consultancy and Custom Driver Development
   *
   *	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.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
19
20
21
22
23
24
   *	Fixes:
   *	Michael Chastain	:	Incorrect size of copying.
   *	Alan Cox		:	Added the cache manager code
   *	Alan Cox		:	Fixed the clone/copy bug and device race.
   *	Mike McLagan		:	Routing by source
   *	Malcolm Beattie		:	Buffer handling fixes.
   *	Alexey Kuznetsov	:	Double buffer free and other fixes.
   *	SVR Anand		:	Fixed several multicast bugs and problems.
   *	Alexey Kuznetsov	:	Status, optimisations and more.
   *	Brad Parker		:	Better behaviour on mrouted upcall
   *					overflow.
   *      Carlos Picoto           :       PIMv1 Support
   *	Pavlin Ivanov Radoslavov:	PIMv2 Registers must checksum only PIM header
f77f13e22   Gilles Espinasse   Fix comment and K...
25
   *					Relax this requirement to work with older peers.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
  #include <asm/system.h>
  #include <asm/uaccess.h>
  #include <linux/types.h>
4fc268d24   Randy Dunlap   [PATCH] capable/c...
31
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  #include <linux/errno.h>
  #include <linux/timer.h>
  #include <linux/mm.h>
  #include <linux/kernel.h>
  #include <linux/fcntl.h>
  #include <linux/stat.h>
  #include <linux/socket.h>
  #include <linux/in.h>
  #include <linux/inet.h>
  #include <linux/netdevice.h>
  #include <linux/inetdevice.h>
  #include <linux/igmp.h>
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
  #include <linux/mroute.h>
  #include <linux/init.h>
46f25dffb   Kris Katterjohn   [NET]: Change 150...
48
  #include <linux/if_ether.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
49
  #include <linux/slab.h>
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
50
  #include <net/net_namespace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
  #include <net/ip.h>
  #include <net/protocol.h>
  #include <linux/skbuff.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
54
  #include <net/route.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
59
60
61
  #include <net/sock.h>
  #include <net/icmp.h>
  #include <net/udp.h>
  #include <net/raw.h>
  #include <linux/notifier.h>
  #include <linux/if_arp.h>
  #include <linux/netfilter_ipv4.h>
709b46e8d   Eric W. Biederman   net: Add compat i...
62
  #include <linux/compat.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
63
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
  #include <net/ipip.h>
  #include <net/checksum.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
66
  #include <net/netlink.h>
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
67
  #include <net/fib_rules.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
  
  #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
  #define CONFIG_IP_PIMSM	1
  #endif
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
72
  struct mr_table {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
73
  	struct list_head	list;
8de53dfbf   Patrick McHardy   ipv4: ipmr: fix N...
74
75
76
  #ifdef CONFIG_NET_NS
  	struct net		*net;
  #endif
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
77
  	u32			id;
4c9687098   Eric Dumazet   ipmr: RCU convers...
78
  	struct sock __rcu	*mroute_sk;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
79
80
81
82
83
84
85
86
87
88
89
90
  	struct timer_list	ipmr_expire_timer;
  	struct list_head	mfc_unres_queue;
  	struct list_head	mfc_cache_array[MFC_LINES];
  	struct vif_device	vif_table[MAXVIFS];
  	int			maxvif;
  	atomic_t		cache_resolve_queue_len;
  	int			mroute_do_assert;
  	int			mroute_do_pim;
  #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
  	int			mroute_reg_vif_num;
  #endif
  };
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
91
92
93
94
95
96
97
  struct ipmr_rule {
  	struct fib_rule		common;
  };
  
  struct ipmr_result {
  	struct mr_table		*mrt;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  /* Big lock, protecting vif table, mrt cache and mroute socket state.
a8cb16dd9   Eric Dumazet   ipmr: cleanups
99
   * Note that the changes are semaphored via rtnl_lock.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
106
   */
  
  static DEFINE_RWLOCK(mrt_lock);
  
  /*
   *	Multicast router control variables
   */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
107
  #define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
  
  /* Special spinlock for queue of unresolved entries */
  static DEFINE_SPINLOCK(mfc_unres_lock);
  
  /* We return to original Alan's scheme. Hash table of resolved
a8cb16dd9   Eric Dumazet   ipmr: cleanups
113
114
115
116
117
   * entries is changed only in process context and protected
   * with weak lock mrt_lock. Queue of unresolved entries is protected
   * with strong spinlock mfc_unres_lock.
   *
   * In this case data path is free of exclusive locks at all.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
   */
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
119
  static struct kmem_cache *mrt_cachep __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
121
  static struct mr_table *ipmr_new_table(struct net *net, u32 id);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
122
123
124
125
  static int ip_mr_forward(struct net *net, struct mr_table *mrt,
  			 struct sk_buff *skb, struct mfc_cache *cache,
  			 int local);
  static int ipmr_cache_report(struct mr_table *mrt,
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
126
  			     struct sk_buff *pkt, vifi_t vifi, int assert);
cb6a4e461   Patrick McHardy   net: ipmr: add su...
127
128
  static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
  			      struct mfc_cache *c, struct rtmsg *rtm);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  static void ipmr_expire_process(unsigned long arg);
  
  #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
  #define ipmr_for_each_table(mrt, net) \
  	list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list)
  
  static struct mr_table *ipmr_get_table(struct net *net, u32 id)
  {
  	struct mr_table *mrt;
  
  	ipmr_for_each_table(mrt, net) {
  		if (mrt->id == id)
  			return mrt;
  	}
  	return NULL;
  }
da91981be   David S. Miller   ipv4: Use flowi4 ...
145
  static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
146
147
148
149
150
  			   struct mr_table **mrt)
  {
  	struct ipmr_result res;
  	struct fib_lookup_arg arg = { .result = &res, };
  	int err;
da91981be   David S. Miller   ipv4: Use flowi4 ...
151
152
  	err = fib_rules_lookup(net->ipv4.mr_rules_ops,
  			       flowi4_to_flowi(flp4), 0, &arg);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
153
154
155
156
157
158
159
160
161
162
163
  	if (err < 0)
  		return err;
  	*mrt = res.mrt;
  	return 0;
  }
  
  static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp,
  			    int flags, struct fib_lookup_arg *arg)
  {
  	struct ipmr_result *res = arg->result;
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  	switch (rule->action) {
  	case FR_ACT_TO_TBL:
  		break;
  	case FR_ACT_UNREACHABLE:
  		return -ENETUNREACH;
  	case FR_ACT_PROHIBIT:
  		return -EACCES;
  	case FR_ACT_BLACKHOLE:
  	default:
  		return -EINVAL;
  	}
  
  	mrt = ipmr_get_table(rule->fr_net, rule->table);
  	if (mrt == NULL)
  		return -EAGAIN;
  	res->mrt = mrt;
  	return 0;
  }
  
  static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
  {
  	return 1;
  }
  
  static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = {
  	FRA_GENERIC_POLICY,
  };
  
  static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
  			       struct fib_rule_hdr *frh, struct nlattr **tb)
  {
  	return 0;
  }
  
  static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
  			     struct nlattr **tb)
  {
  	return 1;
  }
  
  static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
  			  struct fib_rule_hdr *frh)
  {
  	frh->dst_len = 0;
  	frh->src_len = 0;
  	frh->tos     = 0;
  	return 0;
  }
3d0c9c4eb   Patrick McHardy   net: fib_rules: m...
213
  static const struct fib_rules_ops __net_initdata ipmr_rules_ops_template = {
25239cee7   Patrick McHardy   net: rtnetlink: d...
214
  	.family		= RTNL_FAMILY_IPMR,
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  	.rule_size	= sizeof(struct ipmr_rule),
  	.addr_size	= sizeof(u32),
  	.action		= ipmr_rule_action,
  	.match		= ipmr_rule_match,
  	.configure	= ipmr_rule_configure,
  	.compare	= ipmr_rule_compare,
  	.default_pref	= fib_default_rule_pref,
  	.fill		= ipmr_rule_fill,
  	.nlgroup	= RTNLGRP_IPV4_RULE,
  	.policy		= ipmr_rule_policy,
  	.owner		= THIS_MODULE,
  };
  
  static int __net_init ipmr_rules_init(struct net *net)
  {
  	struct fib_rules_ops *ops;
  	struct mr_table *mrt;
  	int err;
  
  	ops = fib_rules_register(&ipmr_rules_ops_template, net);
  	if (IS_ERR(ops))
  		return PTR_ERR(ops);
  
  	INIT_LIST_HEAD(&net->ipv4.mr_tables);
  
  	mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
  	if (mrt == NULL) {
  		err = -ENOMEM;
  		goto err1;
  	}
  
  	err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0);
  	if (err < 0)
  		goto err2;
  
  	net->ipv4.mr_rules_ops = ops;
  	return 0;
  
  err2:
  	kfree(mrt);
  err1:
  	fib_rules_unregister(ops);
  	return err;
  }
  
  static void __net_exit ipmr_rules_exit(struct net *net)
  {
  	struct mr_table *mrt, *next;
035320d54   Eric Dumazet   ipmr: dont corrup...
263
264
  	list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
  		list_del(&mrt->list);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
265
  		kfree(mrt);
035320d54   Eric Dumazet   ipmr: dont corrup...
266
  	}
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
267
268
269
270
271
272
273
274
275
276
  	fib_rules_unregister(net->ipv4.mr_rules_ops);
  }
  #else
  #define ipmr_for_each_table(mrt, net) \
  	for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
  
  static struct mr_table *ipmr_get_table(struct net *net, u32 id)
  {
  	return net->ipv4.mrt;
  }
da91981be   David S. Miller   ipv4: Use flowi4 ...
277
  static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  			   struct mr_table **mrt)
  {
  	*mrt = net->ipv4.mrt;
  	return 0;
  }
  
  static int __net_init ipmr_rules_init(struct net *net)
  {
  	net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
  	return net->ipv4.mrt ? 0 : -ENOMEM;
  }
  
  static void __net_exit ipmr_rules_exit(struct net *net)
  {
  	kfree(net->ipv4.mrt);
  }
  #endif
  
  static struct mr_table *ipmr_new_table(struct net *net, u32 id)
  {
  	struct mr_table *mrt;
  	unsigned int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
301
302
303
304
305
306
307
  	mrt = ipmr_get_table(net, id);
  	if (mrt != NULL)
  		return mrt;
  
  	mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
  	if (mrt == NULL)
  		return NULL;
8de53dfbf   Patrick McHardy   ipv4: ipmr: fix N...
308
  	write_pnet(&mrt->net, net);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  	mrt->id = id;
  
  	/* Forwarding cache */
  	for (i = 0; i < MFC_LINES; i++)
  		INIT_LIST_HEAD(&mrt->mfc_cache_array[i]);
  
  	INIT_LIST_HEAD(&mrt->mfc_unres_queue);
  
  	setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process,
  		    (unsigned long)mrt);
  
  #ifdef CONFIG_IP_PIMSM
  	mrt->mroute_reg_vif_num = -1;
  #endif
  #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
  	list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables);
  #endif
  	return mrt;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
  
  /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
d607032db   Wang Chen   ipv4: Check retur...
330
331
  static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
  {
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
332
  	struct net *net = dev_net(dev);
d607032db   Wang Chen   ipv4: Check retur...
333
  	dev_close(dev);
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
334
  	dev = __dev_get_by_name(net, "tunl0");
d607032db   Wang Chen   ipv4: Check retur...
335
  	if (dev) {
5bc3eb7e2   Stephen Hemminger   ip: convert to ne...
336
  		const struct net_device_ops *ops = dev->netdev_ops;
d607032db   Wang Chen   ipv4: Check retur...
337
  		struct ifreq ifr;
d607032db   Wang Chen   ipv4: Check retur...
338
339
340
341
342
343
344
345
346
347
  		struct ip_tunnel_parm p;
  
  		memset(&p, 0, sizeof(p));
  		p.iph.daddr = v->vifc_rmt_addr.s_addr;
  		p.iph.saddr = v->vifc_lcl_addr.s_addr;
  		p.iph.version = 4;
  		p.iph.ihl = 5;
  		p.iph.protocol = IPPROTO_IPIP;
  		sprintf(p.name, "dvmrp%d", v->vifc_vifi);
  		ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
5bc3eb7e2   Stephen Hemminger   ip: convert to ne...
348
349
350
351
352
353
354
  		if (ops->ndo_do_ioctl) {
  			mm_segment_t oldfs = get_fs();
  
  			set_fs(KERNEL_DS);
  			ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL);
  			set_fs(oldfs);
  		}
d607032db   Wang Chen   ipv4: Check retur...
355
356
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  static
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
358
  struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
  {
  	struct net_device  *dev;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
361
  	dev = __dev_get_by_name(net, "tunl0");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
363
  
  	if (dev) {
5bc3eb7e2   Stephen Hemminger   ip: convert to ne...
364
  		const struct net_device_ops *ops = dev->netdev_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
  		int err;
  		struct ifreq ifr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
371
372
373
374
375
376
  		struct ip_tunnel_parm p;
  		struct in_device  *in_dev;
  
  		memset(&p, 0, sizeof(p));
  		p.iph.daddr = v->vifc_rmt_addr.s_addr;
  		p.iph.saddr = v->vifc_lcl_addr.s_addr;
  		p.iph.version = 4;
  		p.iph.ihl = 5;
  		p.iph.protocol = IPPROTO_IPIP;
  		sprintf(p.name, "dvmrp%d", v->vifc_vifi);
ba93ef746   Stephen Hemminger   [IPV4]: ipmr spar...
377
  		ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378

5bc3eb7e2   Stephen Hemminger   ip: convert to ne...
379
380
381
382
383
384
  		if (ops->ndo_do_ioctl) {
  			mm_segment_t oldfs = get_fs();
  
  			set_fs(KERNEL_DS);
  			err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
  			set_fs(oldfs);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
385
  		} else {
5bc3eb7e2   Stephen Hemminger   ip: convert to ne...
386
  			err = -EOPNOTSUPP;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
387
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
  		dev = NULL;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
389
390
  		if (err == 0 &&
  		    (dev = __dev_get_by_name(net, p.name)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
  			dev->flags |= IFF_MULTICAST;
e5ed63991   Herbert Xu   [IPV4]: Replace _...
392
  			in_dev = __in_dev_get_rtnl(dev);
71e27da96   Herbert Xu   [IPV4]: Restore o...
393
  			if (in_dev == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
  				goto failure;
71e27da96   Herbert Xu   [IPV4]: Restore o...
395
396
397
  
  			ipv4_devconf_setall(in_dev);
  			IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
  
  			if (dev_open(dev))
  				goto failure;
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
401
  			dev_hold(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
404
405
406
407
408
409
410
411
412
413
414
415
  		}
  	}
  	return dev;
  
  failure:
  	/* allow the register to be completed before unregistering. */
  	rtnl_unlock();
  	rtnl_lock();
  
  	unregister_netdevice(dev);
  	return NULL;
  }
  
  #ifdef CONFIG_IP_PIMSM
6fef4c0c8   Stephen Hemminger   netdev: convert p...
416
  static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
  {
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
418
  	struct net *net = dev_net(dev);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
419
  	struct mr_table *mrt;
da91981be   David S. Miller   ipv4: Use flowi4 ...
420
421
422
423
  	struct flowi4 fl4 = {
  		.flowi4_oif	= dev->ifindex,
  		.flowi4_iif	= skb->skb_iif,
  		.flowi4_mark	= skb->mark,
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
424
425
  	};
  	int err;
da91981be   David S. Miller   ipv4: Use flowi4 ...
426
  	err = ipmr_fib_lookup(net, &fl4, &mrt);
e40dbc51f   Ben Greear   ipmr: Don't leak ...
427
428
  	if (err < 0) {
  		kfree_skb(skb);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
429
  		return err;
e40dbc51f   Ben Greear   ipmr: Don't leak ...
430
  	}
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
431

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  	read_lock(&mrt_lock);
cf3677ae1   Pavel Emelyanov   ipmr: Use on-devi...
433
434
  	dev->stats.tx_bytes += skb->len;
  	dev->stats.tx_packets++;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
435
  	ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
437
  	read_unlock(&mrt_lock);
  	kfree_skb(skb);
6ed106549   Patrick McHardy   net: use NETDEV_T...
438
  	return NETDEV_TX_OK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
  }
007c3838d   Stephen Hemminger   ipmr: convert ipm...
440
441
442
  static const struct net_device_ops reg_vif_netdev_ops = {
  	.ndo_start_xmit	= reg_vif_xmit,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
  static void reg_vif_setup(struct net_device *dev)
  {
  	dev->type		= ARPHRD_PIMREG;
46f25dffb   Kris Katterjohn   [NET]: Change 150...
446
  	dev->mtu		= ETH_DATA_LEN - sizeof(struct iphdr) - 8;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  	dev->flags		= IFF_NOARP;
007c3838d   Stephen Hemminger   ipmr: convert ipm...
448
  	dev->netdev_ops		= &reg_vif_netdev_ops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
  	dev->destructor		= free_netdev;
403dbb97f   Tom Goff   PIM-SM: namespace...
450
  	dev->features		|= NETIF_F_NETNS_LOCAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
  }
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
452
  static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
455
  {
  	struct net_device *dev;
  	struct in_device *in_dev;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
456
  	char name[IFNAMSIZ];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
458
459
460
461
  	if (mrt->id == RT_TABLE_DEFAULT)
  		sprintf(name, "pimreg");
  	else
  		sprintf(name, "pimreg%u", mrt->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
463
  	dev = alloc_netdev(0, name, reg_vif_setup);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
  
  	if (dev == NULL)
  		return NULL;
403dbb97f   Tom Goff   PIM-SM: namespace...
467
  	dev_net_set(dev, net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
470
471
472
  	if (register_netdevice(dev)) {
  		free_netdev(dev);
  		return NULL;
  	}
  	dev->iflink = 0;
71e27da96   Herbert Xu   [IPV4]: Restore o...
473
  	rcu_read_lock();
a8cb16dd9   Eric Dumazet   ipmr: cleanups
474
475
  	in_dev = __in_dev_get_rcu(dev);
  	if (!in_dev) {
71e27da96   Herbert Xu   [IPV4]: Restore o...
476
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
  		goto failure;
71e27da96   Herbert Xu   [IPV4]: Restore o...
478
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479

71e27da96   Herbert Xu   [IPV4]: Restore o...
480
481
482
  	ipv4_devconf_setall(in_dev);
  	IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
485
  
  	if (dev_open(dev))
  		goto failure;
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
486
  	dev_hold(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
490
491
492
493
494
495
496
497
498
499
500
  	return dev;
  
  failure:
  	/* allow the register to be completed before unregistering. */
  	rtnl_unlock();
  	rtnl_lock();
  
  	unregister_netdevice(dev);
  	return NULL;
  }
  #endif
  
  /*
   *	Delete a VIF entry
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
501
   *	@notify: Set to 1, if the caller is a notifier_call
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
503

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
504
  static int vif_delete(struct mr_table *mrt, int vifi, int notify,
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
505
  		      struct list_head *head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
509
  {
  	struct vif_device *v;
  	struct net_device *dev;
  	struct in_device *in_dev;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
510
  	if (vifi < 0 || vifi >= mrt->maxvif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
  		return -EADDRNOTAVAIL;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
512
  	v = &mrt->vif_table[vifi];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
518
519
520
521
522
523
  
  	write_lock_bh(&mrt_lock);
  	dev = v->dev;
  	v->dev = NULL;
  
  	if (!dev) {
  		write_unlock_bh(&mrt_lock);
  		return -EADDRNOTAVAIL;
  	}
  
  #ifdef CONFIG_IP_PIMSM
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
524
525
  	if (vifi == mrt->mroute_reg_vif_num)
  		mrt->mroute_reg_vif_num = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  #endif
a8cb16dd9   Eric Dumazet   ipmr: cleanups
527
  	if (vifi + 1 == mrt->maxvif) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  		int tmp;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
529
530
  
  		for (tmp = vifi - 1; tmp >= 0; tmp--) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
531
  			if (VIF_EXISTS(mrt, tmp))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
  				break;
  		}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
534
  		mrt->maxvif = tmp+1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
539
  	}
  
  	write_unlock_bh(&mrt_lock);
  
  	dev_set_allmulti(dev, -1);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
540
541
  	in_dev = __in_dev_get_rtnl(dev);
  	if (in_dev) {
42f811b8b   Herbert Xu   [IPV4]: Convert I...
542
  		IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
  		ip_rt_multicast_event(in_dev);
  	}
a8cb16dd9   Eric Dumazet   ipmr: cleanups
545
  	if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify)
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
546
  		unregister_netdevice_queue(dev, head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
549
550
  
  	dev_put(dev);
  	return 0;
  }
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
551
  static void ipmr_cache_free_rcu(struct rcu_head *head)
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
552
  {
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
553
  	struct mfc_cache *c = container_of(head, struct mfc_cache, rcu);
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
554
555
  	kmem_cache_free(mrt_cachep, c);
  }
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
556
557
558
559
  static inline void ipmr_cache_free(struct mfc_cache *c)
  {
  	call_rcu(&c->rcu, ipmr_cache_free_rcu);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
  /* Destroy an unresolved cache entry, killing queued skbs
a8cb16dd9   Eric Dumazet   ipmr: cleanups
561
   * and reporting error to netlink readers.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
   */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
563
  static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
  {
8de53dfbf   Patrick McHardy   ipv4: ipmr: fix N...
565
  	struct net *net = read_pnet(&mrt->net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
  	struct sk_buff *skb;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
567
  	struct nlmsgerr *e;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
569
  	atomic_dec(&mrt->cache_resolve_queue_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570

c354e1246   Jianjun Kong   net: clean up net...
571
  	while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
572
  		if (ip_hdr(skb)->version == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
575
576
  			struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
  			nlh->nlmsg_type = NLMSG_ERROR;
  			nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
  			skb_trim(skb, nlh->nlmsg_len);
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
577
578
579
  			e = NLMSG_DATA(nlh);
  			e->error = -ETIMEDOUT;
  			memset(&e->msg, 0, sizeof(e->msg));
2942e9005   Thomas Graf   [RTNETLINK]: Use ...
580

4feb88e5c   Benjamin Thery   netns: ipmr: enab...
581
  			rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
582
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  			kfree_skb(skb);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
584
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
  	}
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
586
  	ipmr_cache_free(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
  }
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
588
  /* Timer process for the unresolved queue. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589

e258beb22   Patrick McHardy   ipv4: ipmr: move ...
590
  static void ipmr_expire_process(unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
  {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
592
  	struct mr_table *mrt = (struct mr_table *)arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
  	unsigned long now;
  	unsigned long expires;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
595
  	struct mfc_cache *c, *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
  
  	if (!spin_trylock(&mfc_unres_lock)) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
598
  		mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
600
  		return;
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
601
  	if (list_empty(&mrt->mfc_unres_queue))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
604
605
  		goto out;
  
  	now = jiffies;
  	expires = 10*HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
607
  	list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
611
  		if (time_after(c->mfc_un.unres.expires, now)) {
  			unsigned long interval = c->mfc_un.unres.expires - now;
  			if (interval < expires)
  				expires = interval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
  			continue;
  		}
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
614
  		list_del(&c->list);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
615
  		ipmr_destroy_unres(mrt, c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
617
618
  	if (!list_empty(&mrt->mfc_unres_queue))
  		mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
622
623
624
  
  out:
  	spin_unlock(&mfc_unres_lock);
  }
  
  /* Fill oifs list. It is called under write locked mrt_lock. */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
625
  static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
626
  				   unsigned char *ttls)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
629
630
631
632
  {
  	int vifi;
  
  	cache->mfc_un.res.minvif = MAXVIFS;
  	cache->mfc_un.res.maxvif = 0;
  	memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
633
634
  	for (vifi = 0; vifi < mrt->maxvif; vifi++) {
  		if (VIF_EXISTS(mrt, vifi) &&
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
635
  		    ttls[vifi] && ttls[vifi] < 255) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
640
641
642
643
  			cache->mfc_un.res.ttls[vifi] = ttls[vifi];
  			if (cache->mfc_un.res.minvif > vifi)
  				cache->mfc_un.res.minvif = vifi;
  			if (cache->mfc_un.res.maxvif <= vifi)
  				cache->mfc_un.res.maxvif = vifi + 1;
  		}
  	}
  }
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
644
645
  static int vif_add(struct net *net, struct mr_table *mrt,
  		   struct vifctl *vifc, int mrtsock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646
647
  {
  	int vifi = vifc->vifc_vifi;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
648
  	struct vif_device *v = &mrt->vif_table[vifi];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
  	struct net_device *dev;
  	struct in_device *in_dev;
d607032db   Wang Chen   ipv4: Check retur...
651
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
653
  
  	/* Is vif busy ? */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
654
  	if (VIF_EXISTS(mrt, vifi))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
659
660
661
662
663
  		return -EADDRINUSE;
  
  	switch (vifc->vifc_flags) {
  #ifdef CONFIG_IP_PIMSM
  	case VIFF_REGISTER:
  		/*
  		 * Special Purpose VIF in PIM
  		 * All the packets will be sent to the daemon
  		 */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
664
  		if (mrt->mroute_reg_vif_num >= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
  			return -EADDRINUSE;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
666
  		dev = ipmr_reg_vif(net, mrt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
  		if (!dev)
  			return -ENOBUFS;
d607032db   Wang Chen   ipv4: Check retur...
669
670
671
  		err = dev_set_allmulti(dev, 1);
  		if (err) {
  			unregister_netdevice(dev);
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
672
  			dev_put(dev);
d607032db   Wang Chen   ipv4: Check retur...
673
674
  			return err;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
  		break;
  #endif
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
677
  	case VIFF_TUNNEL:
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
678
  		dev = ipmr_new_tunnel(net, vifc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
680
  		if (!dev)
  			return -ENOBUFS;
d607032db   Wang Chen   ipv4: Check retur...
681
682
683
  		err = dev_set_allmulti(dev, 1);
  		if (err) {
  			ipmr_del_tunnel(dev, vifc);
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
684
  			dev_put(dev);
d607032db   Wang Chen   ipv4: Check retur...
685
686
  			return err;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
  		break;
ee5e81f00   Ilia K   add vif using loc...
688
689
  
  	case VIFF_USE_IFINDEX:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
  	case 0:
ee5e81f00   Ilia K   add vif using loc...
691
692
  		if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
  			dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex);
95ae6b228   Eric Dumazet   ipv4: ip_ptr clea...
693
  			if (dev && __in_dev_get_rtnl(dev) == NULL) {
ee5e81f00   Ilia K   add vif using loc...
694
695
696
  				dev_put(dev);
  				return -EADDRNOTAVAIL;
  			}
a8cb16dd9   Eric Dumazet   ipmr: cleanups
697
  		} else {
ee5e81f00   Ilia K   add vif using loc...
698
  			dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
699
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
701
  		if (!dev)
  			return -EADDRNOTAVAIL;
d607032db   Wang Chen   ipv4: Check retur...
702
  		err = dev_set_allmulti(dev, 1);
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
703
704
  		if (err) {
  			dev_put(dev);
d607032db   Wang Chen   ipv4: Check retur...
705
  			return err;
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
706
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
  		break;
  	default:
  		return -EINVAL;
  	}
a8cb16dd9   Eric Dumazet   ipmr: cleanups
711
712
  	in_dev = __in_dev_get_rtnl(dev);
  	if (!in_dev) {
d0490cfdf   Dan Carpenter   ipmr: missing dev...
713
  		dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
  		return -EADDRNOTAVAIL;
d0490cfdf   Dan Carpenter   ipmr: missing dev...
715
  	}
42f811b8b   Herbert Xu   [IPV4]: Convert I...
716
  	IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  	ip_rt_multicast_event(in_dev);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
718
  	/* Fill in the VIF structures */
c354e1246   Jianjun Kong   net: clean up net...
719
720
721
722
  	v->rate_limit = vifc->vifc_rate_limit;
  	v->local = vifc->vifc_lcl_addr.s_addr;
  	v->remote = vifc->vifc_rmt_addr.s_addr;
  	v->flags = vifc->vifc_flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
  	if (!mrtsock)
  		v->flags |= VIFF_STATIC;
c354e1246   Jianjun Kong   net: clean up net...
725
  	v->threshold = vifc->vifc_threshold;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
727
728
729
730
  	v->bytes_in = 0;
  	v->bytes_out = 0;
  	v->pkt_in = 0;
  	v->pkt_out = 0;
  	v->link = dev->ifindex;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
731
  	if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
735
  		v->link = dev->iflink;
  
  	/* And finish update writing critical data */
  	write_lock_bh(&mrt_lock);
c354e1246   Jianjun Kong   net: clean up net...
736
  	v->dev = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
  #ifdef CONFIG_IP_PIMSM
a8cb16dd9   Eric Dumazet   ipmr: cleanups
738
  	if (v->flags & VIFF_REGISTER)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
739
  		mrt->mroute_reg_vif_num = vifi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
  #endif
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
741
742
  	if (vifi+1 > mrt->maxvif)
  		mrt->maxvif = vifi+1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
744
745
  	write_unlock_bh(&mrt_lock);
  	return 0;
  }
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
746
  /* called with rcu_read_lock() */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
747
  static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
748
749
  					 __be32 origin,
  					 __be32 mcastgrp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
  {
c354e1246   Jianjun Kong   net: clean up net...
751
  	int line = MFC_HASH(mcastgrp, origin);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
  	struct mfc_cache *c;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
753
  	list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
754
755
  		if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp)
  			return c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
  	}
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
757
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
760
761
762
  }
  
  /*
   *	Allocate a multicast cache entry
   */
d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
763
  static struct mfc_cache *ipmr_cache_alloc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
  {
c354e1246   Jianjun Kong   net: clean up net...
765
  	struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
766
767
768
  
  	if (c)
  		c->mfc_un.res.minvif = MAXVIFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
  	return c;
  }
d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
771
  static struct mfc_cache *ipmr_cache_alloc_unres(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
  {
c354e1246   Jianjun Kong   net: clean up net...
773
  	struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
774
775
776
777
778
  
  	if (c) {
  		skb_queue_head_init(&c->mfc_un.unres.unresolved);
  		c->mfc_un.unres.expires = jiffies + 10*HZ;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
783
784
  	return c;
  }
  
  /*
   *	A cache entry has gone into a resolved state from queued
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
785

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
786
787
  static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
  			       struct mfc_cache *uc, struct mfc_cache *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  {
  	struct sk_buff *skb;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
790
  	struct nlmsgerr *e;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791

a8cb16dd9   Eric Dumazet   ipmr: cleanups
792
  	/* Play the pending entries through our router */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793

c354e1246   Jianjun Kong   net: clean up net...
794
  	while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
795
  		if (ip_hdr(skb)->version == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796
  			struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
cb6a4e461   Patrick McHardy   net: ipmr: add su...
797
  			if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
a8cb16dd9   Eric Dumazet   ipmr: cleanups
798
799
  				nlh->nlmsg_len = skb_tail_pointer(skb) -
  						 (u8 *)nlh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
803
  			} else {
  				nlh->nlmsg_type = NLMSG_ERROR;
  				nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
  				skb_trim(skb, nlh->nlmsg_len);
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
804
805
806
  				e = NLMSG_DATA(nlh);
  				e->error = -EMSGSIZE;
  				memset(&e->msg, 0, sizeof(e->msg));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
  			}
2942e9005   Thomas Graf   [RTNETLINK]: Use ...
808

d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
809
  			rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
810
  		} else {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
811
  			ip_mr_forward(net, mrt, skb, c, 0);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
812
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
814
815
816
817
818
819
820
821
  	}
  }
  
  /*
   *	Bounce a cache query up to mrouted. We could use netlink for this but mrouted
   *	expects the following bizarre scheme.
   *
   *	Called under mrt_lock.
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
822

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
823
  static int ipmr_cache_report(struct mr_table *mrt,
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
824
  			     struct sk_buff *pkt, vifi_t vifi, int assert)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
826
  {
  	struct sk_buff *skb;
c9bdd4b52   Arnaldo Carvalho de Melo   [IP]: Introduce i...
827
  	const int ihl = ip_hdrlen(pkt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
  	struct igmphdr *igmp;
  	struct igmpmsg *msg;
4c9687098   Eric Dumazet   ipmr: RCU convers...
830
  	struct sock *mroute_sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
833
834
835
836
837
838
  	int ret;
  
  #ifdef CONFIG_IP_PIMSM
  	if (assert == IGMPMSG_WHOLEPKT)
  		skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
  	else
  #endif
  		skb = alloc_skb(128, GFP_ATOMIC);
132adf546   Stephen Hemminger   [IPV4]: cleanup
839
  	if (!skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
841
842
843
844
  		return -ENOBUFS;
  
  #ifdef CONFIG_IP_PIMSM
  	if (assert == IGMPMSG_WHOLEPKT) {
  		/* Ugly, but we have no choice with this interface.
a8cb16dd9   Eric Dumazet   ipmr: cleanups
845
846
847
  		 * Duplicate old header, fix ihl, length etc.
  		 * And all this only to mangle msg->im_msgtype and
  		 * to set msg->im_mbz to "mbz" :-)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  		 */
878c81450   Arnaldo Carvalho de Melo   [SK_BUFF] ipmr: A...
849
850
  		skb_push(skb, sizeof(struct iphdr));
  		skb_reset_network_header(skb);
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
851
  		skb_reset_transport_header(skb);
0272ffc46   Arnaldo Carvalho de Melo   [SK_BUFF] ipmr: M...
852
  		msg = (struct igmpmsg *)skb_network_header(skb);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
853
  		memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
855
  		msg->im_msgtype = IGMPMSG_WHOLEPKT;
  		msg->im_mbz = 0;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
856
  		msg->im_vif = mrt->mroute_reg_vif_num;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
857
858
859
  		ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
  		ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
  					     sizeof(struct iphdr));
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
860
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
  #endif
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
862
  	{
a8cb16dd9   Eric Dumazet   ipmr: cleanups
863
  	/* Copy the IP header */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
865
  	skb->network_header = skb->tail;
ddc7b8e32   Arnaldo Carvalho de Melo   [SK_BUFF]: Some m...
866
  	skb_put(skb, ihl);
27d7ff46a   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
867
  	skb_copy_to_linear_data(skb, pkt->data, ihl);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
868
  	ip_hdr(skb)->protocol = 0;	/* Flag to the kernel this is a route add */
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
869
  	msg = (struct igmpmsg *)skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
870
  	msg->im_vif = vifi;
adf30907d   Eric Dumazet   net: skb->dst acc...
871
  	skb_dst_set(skb, dst_clone(skb_dst(pkt)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872

a8cb16dd9   Eric Dumazet   ipmr: cleanups
873
  	/* Add our header */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874

a8cb16dd9   Eric Dumazet   ipmr: cleanups
875
  	igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
  	igmp->type	=
  	msg->im_msgtype = assert;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
878
879
  	igmp->code	= 0;
  	ip_hdr(skb)->tot_len = htons(skb->len);		/* Fix the length */
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
880
  	skb->transport_header = skb->network_header;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
881
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882

4c9687098   Eric Dumazet   ipmr: RCU convers...
883
884
885
886
  	rcu_read_lock();
  	mroute_sk = rcu_dereference(mrt->mroute_sk);
  	if (mroute_sk == NULL) {
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
889
  		kfree_skb(skb);
  		return -EINVAL;
  	}
a8cb16dd9   Eric Dumazet   ipmr: cleanups
890
  	/* Deliver to mrouted */
4c9687098   Eric Dumazet   ipmr: RCU convers...
891
892
  	ret = sock_queue_rcv_skb(mroute_sk, skb);
  	rcu_read_unlock();
70a269e6c   Benjamin Thery   netns: ipmr: allo...
893
  	if (ret < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
896
897
898
899
900
901
902
903
904
905
  		if (net_ratelimit())
  			printk(KERN_WARNING "mroute: pending queue full, dropping entries.
  ");
  		kfree_skb(skb);
  	}
  
  	return ret;
  }
  
  /*
   *	Queue a packet for resolution. It gets locked cache entry!
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
906

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
  static int
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
908
  ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909
  {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
910
  	bool found = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
  	int err;
  	struct mfc_cache *c;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
913
  	const struct iphdr *iph = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
915
  
  	spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
916
  	list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
917
  		if (c->mfc_mcastgrp == iph->daddr &&
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
918
919
  		    c->mfc_origin == iph->saddr) {
  			found = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
  			break;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
921
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922
  	}
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
923
  	if (!found) {
a8cb16dd9   Eric Dumazet   ipmr: cleanups
924
  		/* Create a new entry if allowable */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
926
  		if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
927
  		    (c = ipmr_cache_alloc_unres()) == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
930
931
932
  			spin_unlock_bh(&mfc_unres_lock);
  
  			kfree_skb(skb);
  			return -ENOBUFS;
  		}
a8cb16dd9   Eric Dumazet   ipmr: cleanups
933
  		/* Fill in the new cache entry */
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
934
935
936
  		c->mfc_parent	= -1;
  		c->mfc_origin	= iph->saddr;
  		c->mfc_mcastgrp	= iph->daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937

a8cb16dd9   Eric Dumazet   ipmr: cleanups
938
  		/* Reflect first query at mrouted. */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
939
  		err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
940
  		if (err < 0) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
941
  			/* If the report failed throw the cache entry
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
944
  			   out - Brad Parker
  			 */
  			spin_unlock_bh(&mfc_unres_lock);
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
945
  			ipmr_cache_free(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946
947
948
  			kfree_skb(skb);
  			return err;
  		}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
949
950
  		atomic_inc(&mrt->cache_resolve_queue_len);
  		list_add(&c->list, &mrt->mfc_unres_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
951

278554bd6   David S. Miller   Merge branch 'mas...
952
953
  		if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
  			mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
954
  	}
a8cb16dd9   Eric Dumazet   ipmr: cleanups
955
956
957
  	/* See if we can append the packet */
  
  	if (c->mfc_un.unres.unresolved.qlen > 3) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
959
960
  		kfree_skb(skb);
  		err = -ENOBUFS;
  	} else {
c354e1246   Jianjun Kong   net: clean up net...
961
  		skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
963
964
965
966
967
968
969
970
971
  		err = 0;
  	}
  
  	spin_unlock_bh(&mfc_unres_lock);
  	return err;
  }
  
  /*
   *	MFC cache manipulation by user space mroute daemon
   */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
972
  static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
973
974
  {
  	int line;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
975
  	struct mfc_cache *c, *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976

c354e1246   Jianjun Kong   net: clean up net...
977
  	line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
979
  	list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
981
  		if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
  		    c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
982
  			list_del_rcu(&c->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
983

5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
984
  			ipmr_cache_free(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
986
987
988
989
  			return 0;
  		}
  	}
  	return -ENOENT;
  }
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
990
991
  static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
  			struct mfcctl *mfc, int mrtsock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
  {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
993
  	bool found = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
  	int line;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
995
  	struct mfc_cache *uc, *c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
996

a50436f2c   Patrick McHardy   net: ipmr/ip6mr: ...
997
998
  	if (mfc->mfcc_parent >= MAXVIFS)
  		return -ENFILE;
c354e1246   Jianjun Kong   net: clean up net...
999
  	line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1001
  	list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002
  		if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1003
1004
  		    c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
  			found = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
  			break;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1006
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007
  	}
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1008
  	if (found) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009
1010
  		write_lock_bh(&mrt_lock);
  		c->mfc_parent = mfc->mfcc_parent;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1011
  		ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1012
1013
1014
1015
1016
  		if (!mrtsock)
  			c->mfc_flags |= MFC_STATIC;
  		write_unlock_bh(&mrt_lock);
  		return 0;
  	}
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1017
  	if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
  		return -EINVAL;
d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
1019
  	c = ipmr_cache_alloc();
c354e1246   Jianjun Kong   net: clean up net...
1020
  	if (c == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
  		return -ENOMEM;
c354e1246   Jianjun Kong   net: clean up net...
1022
1023
1024
  	c->mfc_origin = mfc->mfcc_origin.s_addr;
  	c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
  	c->mfc_parent = mfc->mfcc_parent;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1025
  	ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
1027
  	if (!mrtsock)
  		c->mfc_flags |= MFC_STATIC;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1028
  	list_add_rcu(&c->list, &mrt->mfc_cache_array[line]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
1030
1031
1032
1033
  
  	/*
  	 *	Check to see if we resolved a queued list. If so we
  	 *	need to send on the frames and tidy up.
  	 */
b0ebb739a   Patrick McHardy   ipv4: ipmr: fix i...
1034
  	found = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
  	spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1036
  	list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
1037
  		if (uc->mfc_origin == c->mfc_origin &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038
  		    uc->mfc_mcastgrp == c->mfc_mcastgrp) {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1039
  			list_del(&uc->list);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1040
  			atomic_dec(&mrt->cache_resolve_queue_len);
b0ebb739a   Patrick McHardy   ipv4: ipmr: fix i...
1041
  			found = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042
1043
1044
  			break;
  		}
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1045
1046
  	if (list_empty(&mrt->mfc_unres_queue))
  		del_timer(&mrt->ipmr_expire_timer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1047
  	spin_unlock_bh(&mfc_unres_lock);
b0ebb739a   Patrick McHardy   ipv4: ipmr: fix i...
1048
  	if (found) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1049
  		ipmr_cache_resolve(net, mrt, uc, c);
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
1050
  		ipmr_cache_free(uc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
1054
1055
1056
1057
  	}
  	return 0;
  }
  
  /*
   *	Close the multicast socket, and clear the vif tables etc
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1058

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1059
  static void mroute_clean_tables(struct mr_table *mrt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
  {
  	int i;
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
1062
  	LIST_HEAD(list);
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1063
  	struct mfc_cache *c, *next;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1064

a8cb16dd9   Eric Dumazet   ipmr: cleanups
1065
  	/* Shut down all active vif entries */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1066
  	for (i = 0; i < mrt->maxvif; i++) {
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1067
  		if (!(mrt->vif_table[i].flags & VIFF_STATIC))
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1068
  			vif_delete(mrt, i, 0, &list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1069
  	}
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
1070
  	unregister_netdevice_many(&list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071

a8cb16dd9   Eric Dumazet   ipmr: cleanups
1072
  	/* Wipe the cache */
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1073
  	for (i = 0; i < MFC_LINES; i++) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1074
  		list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1075
  			if (c->mfc_flags & MFC_STATIC)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
  				continue;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1077
  			list_del_rcu(&c->list);
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
1078
  			ipmr_cache_free(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1079
1080
  		}
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1081
  	if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
  		spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1083
  		list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1084
  			list_del(&c->list);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1085
  			ipmr_destroy_unres(mrt, c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
1087
1088
1089
  		}
  		spin_unlock_bh(&mfc_unres_lock);
  	}
  }
4c9687098   Eric Dumazet   ipmr: RCU convers...
1090
1091
1092
  /* called from ip_ra_control(), before an RCU grace period,
   * we dont need to call synchronize_rcu() here
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1093
1094
  static void mrtsock_destruct(struct sock *sk)
  {
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1095
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1096
  	struct mr_table *mrt;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1097

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098
  	rtnl_lock();
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1099
  	ipmr_for_each_table(mrt, net) {
4c9687098   Eric Dumazet   ipmr: RCU convers...
1100
  		if (sk == rtnl_dereference(mrt->mroute_sk)) {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1101
  			IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
1102
  			RCU_INIT_POINTER(mrt->mroute_sk, NULL);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1103
1104
  			mroute_clean_tables(mrt);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
  	}
  	rtnl_unlock();
  }
  
  /*
   *	Socket options and virtual interface manipulation. The whole
   *	virtual interface system is a complete heap, but unfortunately
   *	that's how BSD mrouted happens to think. Maybe one day with a proper
   *	MOSPF/PIM router set up we can clean this up.
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1115

b7058842c   David S. Miller   net: Make setsock...
1116
  int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1117
1118
1119
1120
  {
  	int ret;
  	struct vifctl vif;
  	struct mfcctl mfc;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1121
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1122
1123
1124
1125
1126
  	struct mr_table *mrt;
  
  	mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return -ENOENT;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1127

132adf546   Stephen Hemminger   [IPV4]: cleanup
1128
  	if (optname != MRT_INIT) {
33d480ce6   Eric Dumazet   net: cleanup some...
1129
  		if (sk != rcu_access_pointer(mrt->mroute_sk) &&
4c9687098   Eric Dumazet   ipmr: RCU convers...
1130
  		    !capable(CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1131
1132
  			return -EACCES;
  	}
132adf546   Stephen Hemminger   [IPV4]: cleanup
1133
1134
1135
  	switch (optname) {
  	case MRT_INIT:
  		if (sk->sk_type != SOCK_RAW ||
c720c7e83   Eric Dumazet   inet: rename some...
1136
  		    inet_sk(sk)->inet_num != IPPROTO_IGMP)
132adf546   Stephen Hemminger   [IPV4]: cleanup
1137
  			return -EOPNOTSUPP;
c354e1246   Jianjun Kong   net: clean up net...
1138
  		if (optlen != sizeof(int))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1139
  			return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1140

132adf546   Stephen Hemminger   [IPV4]: cleanup
1141
  		rtnl_lock();
4c9687098   Eric Dumazet   ipmr: RCU convers...
1142
  		if (rtnl_dereference(mrt->mroute_sk)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1143
  			rtnl_unlock();
132adf546   Stephen Hemminger   [IPV4]: cleanup
1144
1145
1146
1147
1148
  			return -EADDRINUSE;
  		}
  
  		ret = ip_ra_control(sk, 1, mrtsock_destruct);
  		if (ret == 0) {
cf778b00e   Eric Dumazet   net: reintroduce ...
1149
  			rcu_assign_pointer(mrt->mroute_sk, sk);
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1150
  			IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
132adf546   Stephen Hemminger   [IPV4]: cleanup
1151
1152
1153
1154
  		}
  		rtnl_unlock();
  		return ret;
  	case MRT_DONE:
33d480ce6   Eric Dumazet   net: cleanup some...
1155
  		if (sk != rcu_access_pointer(mrt->mroute_sk))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1156
1157
1158
1159
  			return -EACCES;
  		return ip_ra_control(sk, 0, NULL);
  	case MRT_ADD_VIF:
  	case MRT_DEL_VIF:
c354e1246   Jianjun Kong   net: clean up net...
1160
  		if (optlen != sizeof(vif))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1161
  			return -EINVAL;
c354e1246   Jianjun Kong   net: clean up net...
1162
  		if (copy_from_user(&vif, optval, sizeof(vif)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1163
1164
1165
1166
  			return -EFAULT;
  		if (vif.vifc_vifi >= MAXVIFS)
  			return -ENFILE;
  		rtnl_lock();
c354e1246   Jianjun Kong   net: clean up net...
1167
  		if (optname == MRT_ADD_VIF) {
4c9687098   Eric Dumazet   ipmr: RCU convers...
1168
1169
  			ret = vif_add(net, mrt, &vif,
  				      sk == rtnl_dereference(mrt->mroute_sk));
132adf546   Stephen Hemminger   [IPV4]: cleanup
1170
  		} else {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1171
  			ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
132adf546   Stephen Hemminger   [IPV4]: cleanup
1172
1173
1174
  		}
  		rtnl_unlock();
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1175
1176
1177
1178
1179
  
  		/*
  		 *	Manipulate the forwarding caches. These live
  		 *	in a sort of kernel/user symbiosis.
  		 */
132adf546   Stephen Hemminger   [IPV4]: cleanup
1180
1181
  	case MRT_ADD_MFC:
  	case MRT_DEL_MFC:
c354e1246   Jianjun Kong   net: clean up net...
1182
  		if (optlen != sizeof(mfc))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1183
  			return -EINVAL;
c354e1246   Jianjun Kong   net: clean up net...
1184
  		if (copy_from_user(&mfc, optval, sizeof(mfc)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1185
1186
  			return -EFAULT;
  		rtnl_lock();
c354e1246   Jianjun Kong   net: clean up net...
1187
  		if (optname == MRT_DEL_MFC)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1188
  			ret = ipmr_mfc_delete(mrt, &mfc);
132adf546   Stephen Hemminger   [IPV4]: cleanup
1189
  		else
4c9687098   Eric Dumazet   ipmr: RCU convers...
1190
1191
  			ret = ipmr_mfc_add(net, mrt, &mfc,
  					   sk == rtnl_dereference(mrt->mroute_sk));
132adf546   Stephen Hemminger   [IPV4]: cleanup
1192
1193
  		rtnl_unlock();
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1194
1195
1196
  		/*
  		 *	Control PIM assert.
  		 */
132adf546   Stephen Hemminger   [IPV4]: cleanup
1197
1198
1199
  	case MRT_ASSERT:
  	{
  		int v;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1200
  		if (get_user(v, (int __user *)optval))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1201
  			return -EFAULT;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1202
  		mrt->mroute_do_assert = (v) ? 1 : 0;
132adf546   Stephen Hemminger   [IPV4]: cleanup
1203
1204
  		return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1205
  #ifdef CONFIG_IP_PIMSM
132adf546   Stephen Hemminger   [IPV4]: cleanup
1206
1207
  	case MRT_PIM:
  	{
ba93ef746   Stephen Hemminger   [IPV4]: ipmr spar...
1208
  		int v;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1209
  		if (get_user(v, (int __user *)optval))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1210
  			return -EFAULT;
ba93ef746   Stephen Hemminger   [IPV4]: ipmr spar...
1211
  		v = (v) ? 1 : 0;
132adf546   Stephen Hemminger   [IPV4]: cleanup
1212
1213
  		rtnl_lock();
  		ret = 0;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1214
1215
1216
  		if (v != mrt->mroute_do_pim) {
  			mrt->mroute_do_pim = v;
  			mrt->mroute_do_assert = v;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1217
  		}
132adf546   Stephen Hemminger   [IPV4]: cleanup
1218
1219
1220
  		rtnl_unlock();
  		return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1221
  #endif
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1222
1223
1224
1225
1226
1227
1228
1229
1230
  #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
  	case MRT_TABLE:
  	{
  		u32 v;
  
  		if (optlen != sizeof(u32))
  			return -EINVAL;
  		if (get_user(v, (u32 __user *)optval))
  			return -EFAULT;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1231
1232
1233
  
  		rtnl_lock();
  		ret = 0;
4c9687098   Eric Dumazet   ipmr: RCU convers...
1234
1235
1236
1237
1238
1239
1240
  		if (sk == rtnl_dereference(mrt->mroute_sk)) {
  			ret = -EBUSY;
  		} else {
  			if (!ipmr_new_table(net, v))
  				ret = -ENOMEM;
  			raw_sk(sk)->ipmr_table = v;
  		}
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1241
1242
1243
1244
  		rtnl_unlock();
  		return ret;
  	}
  #endif
132adf546   Stephen Hemminger   [IPV4]: cleanup
1245
1246
1247
1248
1249
1250
  	/*
  	 *	Spurious command, or MRT_VERSION which you cannot
  	 *	set.
  	 */
  	default:
  		return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1251
1252
1253
1254
1255
1256
  	}
  }
  
  /*
   *	Getsock opt support for the multicast routing system.
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1257

c354e1246   Jianjun Kong   net: clean up net...
1258
  int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1259
1260
1261
  {
  	int olr;
  	int val;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1262
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1263
1264
1265
1266
1267
  	struct mr_table *mrt;
  
  	mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1268

c354e1246   Jianjun Kong   net: clean up net...
1269
  	if (optname != MRT_VERSION &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1270
  #ifdef CONFIG_IP_PIMSM
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1271
  	   optname != MRT_PIM &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1272
  #endif
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1273
  	   optname != MRT_ASSERT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1274
1275
1276
1277
1278
1279
1280
1281
  		return -ENOPROTOOPT;
  
  	if (get_user(olr, optlen))
  		return -EFAULT;
  
  	olr = min_t(unsigned int, olr, sizeof(int));
  	if (olr < 0)
  		return -EINVAL;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1282

c354e1246   Jianjun Kong   net: clean up net...
1283
  	if (put_user(olr, optlen))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1284
  		return -EFAULT;
c354e1246   Jianjun Kong   net: clean up net...
1285
1286
  	if (optname == MRT_VERSION)
  		val = 0x0305;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
  #ifdef CONFIG_IP_PIMSM
c354e1246   Jianjun Kong   net: clean up net...
1288
  	else if (optname == MRT_PIM)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1289
  		val = mrt->mroute_do_pim;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1290
1291
  #endif
  	else
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1292
  		val = mrt->mroute_do_assert;
c354e1246   Jianjun Kong   net: clean up net...
1293
  	if (copy_to_user(optval, &val, olr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
1295
1296
1297
1298
1299
1300
  		return -EFAULT;
  	return 0;
  }
  
  /*
   *	The IP multicast ioctl support routines.
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1301

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1302
1303
1304
1305
1306
1307
  int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
  {
  	struct sioc_sg_req sr;
  	struct sioc_vif_req vr;
  	struct vif_device *vif;
  	struct mfc_cache *c;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1308
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1309
1310
1311
1312
1313
  	struct mr_table *mrt;
  
  	mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return -ENOENT;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1314

132adf546   Stephen Hemminger   [IPV4]: cleanup
1315
1316
  	switch (cmd) {
  	case SIOCGETVIFCNT:
c354e1246   Jianjun Kong   net: clean up net...
1317
  		if (copy_from_user(&vr, arg, sizeof(vr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1318
  			return -EFAULT;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1319
  		if (vr.vifi >= mrt->maxvif)
132adf546   Stephen Hemminger   [IPV4]: cleanup
1320
1321
  			return -EINVAL;
  		read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1322
1323
  		vif = &mrt->vif_table[vr.vifi];
  		if (VIF_EXISTS(mrt, vr.vifi)) {
c354e1246   Jianjun Kong   net: clean up net...
1324
1325
1326
1327
  			vr.icount = vif->pkt_in;
  			vr.ocount = vif->pkt_out;
  			vr.ibytes = vif->bytes_in;
  			vr.obytes = vif->bytes_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1328
  			read_unlock(&mrt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1329

c354e1246   Jianjun Kong   net: clean up net...
1330
  			if (copy_to_user(arg, &vr, sizeof(vr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1331
1332
1333
1334
1335
1336
  				return -EFAULT;
  			return 0;
  		}
  		read_unlock(&mrt_lock);
  		return -EADDRNOTAVAIL;
  	case SIOCGETSGCNT:
c354e1246   Jianjun Kong   net: clean up net...
1337
  		if (copy_from_user(&sr, arg, sizeof(sr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1338
  			return -EFAULT;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1339
  		rcu_read_lock();
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1340
  		c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
132adf546   Stephen Hemminger   [IPV4]: cleanup
1341
1342
1343
1344
  		if (c) {
  			sr.pktcnt = c->mfc_un.res.pkt;
  			sr.bytecnt = c->mfc_un.res.bytes;
  			sr.wrong_if = c->mfc_un.res.wrong_if;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1345
  			rcu_read_unlock();
132adf546   Stephen Hemminger   [IPV4]: cleanup
1346

c354e1246   Jianjun Kong   net: clean up net...
1347
  			if (copy_to_user(arg, &sr, sizeof(sr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1348
1349
1350
  				return -EFAULT;
  			return 0;
  		}
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1351
  		rcu_read_unlock();
132adf546   Stephen Hemminger   [IPV4]: cleanup
1352
1353
1354
  		return -EADDRNOTAVAIL;
  	default:
  		return -ENOIOCTLCMD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1355
1356
  	}
  }
709b46e8d   Eric W. Biederman   net: Add compat i...
1357
1358
1359
1360
1361
1362
1363
1364
  #ifdef CONFIG_COMPAT
  struct compat_sioc_sg_req {
  	struct in_addr src;
  	struct in_addr grp;
  	compat_ulong_t pktcnt;
  	compat_ulong_t bytecnt;
  	compat_ulong_t wrong_if;
  };
ca6b8bb09   David S. Miller   net: Support comp...
1365
1366
1367
1368
1369
1370
1371
  struct compat_sioc_vif_req {
  	vifi_t	vifi;		/* Which iface */
  	compat_ulong_t icount;
  	compat_ulong_t ocount;
  	compat_ulong_t ibytes;
  	compat_ulong_t obytes;
  };
709b46e8d   Eric W. Biederman   net: Add compat i...
1372
1373
  int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
  {
0033d5ad2   David S. Miller   net: Fix bug in c...
1374
  	struct compat_sioc_sg_req sr;
ca6b8bb09   David S. Miller   net: Support comp...
1375
1376
  	struct compat_sioc_vif_req vr;
  	struct vif_device *vif;
709b46e8d   Eric W. Biederman   net: Add compat i...
1377
1378
1379
1380
1381
1382
1383
1384
1385
  	struct mfc_cache *c;
  	struct net *net = sock_net(sk);
  	struct mr_table *mrt;
  
  	mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return -ENOENT;
  
  	switch (cmd) {
ca6b8bb09   David S. Miller   net: Support comp...
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
  	case SIOCGETVIFCNT:
  		if (copy_from_user(&vr, arg, sizeof(vr)))
  			return -EFAULT;
  		if (vr.vifi >= mrt->maxvif)
  			return -EINVAL;
  		read_lock(&mrt_lock);
  		vif = &mrt->vif_table[vr.vifi];
  		if (VIF_EXISTS(mrt, vr.vifi)) {
  			vr.icount = vif->pkt_in;
  			vr.ocount = vif->pkt_out;
  			vr.ibytes = vif->bytes_in;
  			vr.obytes = vif->bytes_out;
  			read_unlock(&mrt_lock);
  
  			if (copy_to_user(arg, &vr, sizeof(vr)))
  				return -EFAULT;
  			return 0;
  		}
  		read_unlock(&mrt_lock);
  		return -EADDRNOTAVAIL;
709b46e8d   Eric W. Biederman   net: Add compat i...
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
  	case SIOCGETSGCNT:
  		if (copy_from_user(&sr, arg, sizeof(sr)))
  			return -EFAULT;
  
  		rcu_read_lock();
  		c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
  		if (c) {
  			sr.pktcnt = c->mfc_un.res.pkt;
  			sr.bytecnt = c->mfc_un.res.bytes;
  			sr.wrong_if = c->mfc_un.res.wrong_if;
  			rcu_read_unlock();
  
  			if (copy_to_user(arg, &sr, sizeof(sr)))
  				return -EFAULT;
  			return 0;
  		}
  		rcu_read_unlock();
  		return -EADDRNOTAVAIL;
  	default:
  		return -ENOIOCTLCMD;
  	}
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1429
1430
1431
  
  static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
  {
e9dc86534   Eric W. Biederman   [NET]: Make devic...
1432
  	struct net_device *dev = ptr;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1433
  	struct net *net = dev_net(dev);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1434
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1435
1436
  	struct vif_device *v;
  	int ct;
e9dc86534   Eric W. Biederman   [NET]: Make devic...
1437

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1438
1439
  	if (event != NETDEV_UNREGISTER)
  		return NOTIFY_DONE;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1440
1441
1442
1443
1444
  
  	ipmr_for_each_table(mrt, net) {
  		v = &mrt->vif_table[0];
  		for (ct = 0; ct < mrt->maxvif; ct++, v++) {
  			if (v->dev == dev)
e92036a65   RongQing.Li   ipv4: remove usel...
1445
  				vif_delete(mrt, ct, 1, NULL);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1446
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1447
1448
1449
  	}
  	return NOTIFY_DONE;
  }
c354e1246   Jianjun Kong   net: clean up net...
1450
  static struct notifier_block ip_mr_notifier = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1451
1452
1453
1454
  	.notifier_call = ipmr_device_event,
  };
  
  /*
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1455
   *	Encapsulate a packet by attaching a valid IPIP header to it.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1456
1457
1458
   *	This avoids tunnel drivers and other mess and gives us the speed so
   *	important for multicast video.
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1459

114c7844f   Al Viro   [IPV4]: mroute an...
1460
  static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1461
  {
8856dfa3e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1462
  	struct iphdr *iph;
b71d1d426   Eric Dumazet   inet: constify ip...
1463
  	const struct iphdr *old_iph = ip_hdr(skb);
8856dfa3e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1464
1465
  
  	skb_push(skb, sizeof(struct iphdr));
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
1466
  	skb->transport_header = skb->network_header;
8856dfa3e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1467
  	skb_reset_network_header(skb);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1468
  	iph = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1469

a8cb16dd9   Eric Dumazet   ipmr: cleanups
1470
  	iph->version	=	4;
e023dd643   Arnaldo Carvalho de Melo   [IPMR]: Fix bug i...
1471
1472
  	iph->tos	=	old_iph->tos;
  	iph->ttl	=	old_iph->ttl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1473
1474
1475
1476
1477
1478
  	iph->frag_off	=	0;
  	iph->daddr	=	daddr;
  	iph->saddr	=	saddr;
  	iph->protocol	=	IPPROTO_IPIP;
  	iph->ihl	=	5;
  	iph->tot_len	=	htons(skb->len);
adf30907d   Eric Dumazet   net: skb->dst acc...
1479
  	ip_select_ident(iph, skb_dst(skb), NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1480
  	ip_send_check(iph);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1481
1482
1483
1484
1485
1486
  	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
  	nf_reset(skb);
  }
  
  static inline int ipmr_forward_finish(struct sk_buff *skb)
  {
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1487
  	struct ip_options *opt = &(IPCB(skb)->opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1488

adf30907d   Eric Dumazet   net: skb->dst acc...
1489
  	IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
  
  	if (unlikely(opt->optlen))
  		ip_forward_options(skb);
  
  	return dst_output(skb);
  }
  
  /*
   *	Processing handlers for ipmr_forward
   */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1500
1501
  static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
  			    struct sk_buff *skb, struct mfc_cache *c, int vifi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1502
  {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1503
  	const struct iphdr *iph = ip_hdr(skb);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1504
  	struct vif_device *vif = &mrt->vif_table[vifi];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1505
1506
  	struct net_device *dev;
  	struct rtable *rt;
31e4543db   David S. Miller   ipv4: Make caller...
1507
  	struct flowi4 fl4;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1508
1509
1510
1511
1512
1513
1514
1515
  	int    encap = 0;
  
  	if (vif->dev == NULL)
  		goto out_free;
  
  #ifdef CONFIG_IP_PIMSM
  	if (vif->flags & VIFF_REGISTER) {
  		vif->pkt_out++;
c354e1246   Jianjun Kong   net: clean up net...
1516
  		vif->bytes_out += skb->len;
cf3677ae1   Pavel Emelyanov   ipmr: Use on-devi...
1517
1518
  		vif->dev->stats.tx_bytes += skb->len;
  		vif->dev->stats.tx_packets++;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1519
  		ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
69ebbf58f   Ilpo Järvinen   ipmr: use goto to...
1520
  		goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1521
1522
  	}
  #endif
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1523
  	if (vif->flags & VIFF_TUNNEL) {
31e4543db   David S. Miller   ipv4: Make caller...
1524
  		rt = ip_route_output_ports(net, &fl4, NULL,
78fbfd8a6   David S. Miller   ipv4: Create and ...
1525
1526
1527
1528
  					   vif->remote, vif->local,
  					   0, 0,
  					   IPPROTO_IPIP,
  					   RT_TOS(iph->tos), vif->link);
b23dd4fe4   David S. Miller   ipv4: Make output...
1529
  		if (IS_ERR(rt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1530
1531
1532
  			goto out_free;
  		encap = sizeof(struct iphdr);
  	} else {
31e4543db   David S. Miller   ipv4: Make caller...
1533
  		rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0,
78fbfd8a6   David S. Miller   ipv4: Create and ...
1534
1535
1536
  					   0, 0,
  					   IPPROTO_IPIP,
  					   RT_TOS(iph->tos), vif->link);
b23dd4fe4   David S. Miller   ipv4: Make output...
1537
  		if (IS_ERR(rt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1538
1539
  			goto out_free;
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
1540
  	dev = rt->dst.dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1541

d8d1f30b9   Changli Gao   net-next: remove ...
1542
  	if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1543
  		/* Do not fragment multicasts. Alas, IPv4 does not
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1544
1545
  		 * allow to send ICMP, so that packets will disappear
  		 * to blackhole.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1546
  		 */
7c73a6faf   Pavel Emelyanov   mib: add net to I...
1547
  		IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1548
1549
1550
  		ip_rt_put(rt);
  		goto out_free;
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
1551
  	encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1552
1553
  
  	if (skb_cow(skb, encap)) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1554
  		ip_rt_put(rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1555
1556
1557
1558
  		goto out_free;
  	}
  
  	vif->pkt_out++;
c354e1246   Jianjun Kong   net: clean up net...
1559
  	vif->bytes_out += skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1560

adf30907d   Eric Dumazet   net: skb->dst acc...
1561
  	skb_dst_drop(skb);
d8d1f30b9   Changli Gao   net-next: remove ...
1562
  	skb_dst_set(skb, &rt->dst);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1563
  	ip_decrease_ttl(ip_hdr(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1564
1565
  
  	/* FIXME: forward and output firewalls used to be called here.
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1566
1567
  	 * What do we do with netfilter? -- RR
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1568
1569
1570
  	if (vif->flags & VIFF_TUNNEL) {
  		ip_encap(skb, vif->local, vif->remote);
  		/* FIXME: extra output firewall step used to be here. --RR */
2f4c02d40   Pavel Emelyanov   ipmr: Ipip tunnel...
1571
1572
  		vif->dev->stats.tx_packets++;
  		vif->dev->stats.tx_bytes += skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
  	}
  
  	IPCB(skb)->flags |= IPSKB_FORWARDED;
  
  	/*
  	 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
  	 * not only before forwarding, but after forwarding on all output
  	 * interfaces. It is clear, if mrouter runs a multicasting
  	 * program, it should receive packets not depending to what interface
  	 * program is joined.
  	 * If we will not make it, the program will have to join on all
  	 * interfaces. On the other hand, multihoming host (or router, but
  	 * not mrouter) cannot join to more than one interface - it will
  	 * result in receiving multiple packets.
  	 */
9bbc768aa   Jan Engelhardt   netfilter: ipv4: ...
1588
  	NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1589
1590
1591
1592
1593
  		ipmr_forward_finish);
  	return;
  
  out_free:
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1594
  }
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1595
  static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1596
1597
  {
  	int ct;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1598
1599
1600
  
  	for (ct = mrt->maxvif-1; ct >= 0; ct--) {
  		if (mrt->vif_table[ct].dev == dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1601
1602
1603
1604
1605
1606
  			break;
  	}
  	return ct;
  }
  
  /* "local" means that we should preserve one skb (for local delivery) */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1607
1608
1609
  static int ip_mr_forward(struct net *net, struct mr_table *mrt,
  			 struct sk_buff *skb, struct mfc_cache *cache,
  			 int local)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
  {
  	int psend = -1;
  	int vif, ct;
  
  	vif = cache->mfc_parent;
  	cache->mfc_un.res.pkt++;
  	cache->mfc_un.res.bytes += skb->len;
  
  	/*
  	 * Wrong interface: drop packet and (maybe) send PIM assert.
  	 */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1621
  	if (mrt->vif_table[vif].dev != skb->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1622
  		int true_vifi;
c75379676   David S. Miller   ipv4: Make rt->fl...
1623
  		if (rt_is_output_route(skb_rtable(skb))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1624
  			/* It is our own packet, looped back.
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1625
1626
1627
1628
1629
1630
1631
1632
1633
  			 * Very complicated situation...
  			 *
  			 * The best workaround until routing daemons will be
  			 * fixed is not to redistribute packet, if it was
  			 * send through wrong interface. It means, that
  			 * multicast applications WILL NOT work for
  			 * (S,G), which have default multicast route pointing
  			 * to wrong oif. In any case, it is not a good
  			 * idea to use multicasting applications on router.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1634
1635
1636
1637
1638
  			 */
  			goto dont_forward;
  		}
  
  		cache->mfc_un.res.wrong_if++;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1639
  		true_vifi = ipmr_find_vif(mrt, skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1640

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1641
  		if (true_vifi >= 0 && mrt->mroute_do_assert &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1642
  		    /* pimsm uses asserts, when switching from RPT to SPT,
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1643
1644
1645
  		     * so that we cannot check that packet arrived on an oif.
  		     * It is bad, but otherwise we would need to move pretty
  		     * large chunk of pimd to kernel. Ough... --ANK
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1646
  		     */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1647
  		    (mrt->mroute_do_pim ||
6f9374a93   Benjamin Thery   netns: ipmr: decl...
1648
  		     cache->mfc_un.res.ttls[true_vifi] < 255) &&
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1649
  		    time_after(jiffies,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1650
1651
  			       cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
  			cache->mfc_un.res.last_assert = jiffies;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1652
  			ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1653
1654
1655
  		}
  		goto dont_forward;
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1656
1657
  	mrt->vif_table[vif].pkt_in++;
  	mrt->vif_table[vif].bytes_in += skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1658
1659
1660
1661
  
  	/*
  	 *	Forward the frame
  	 */
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1662
1663
  	for (ct = cache->mfc_un.res.maxvif - 1;
  	     ct >= cache->mfc_un.res.minvif; ct--) {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1664
  		if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1665
1666
  			if (psend != -1) {
  				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1667

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1668
  				if (skb2)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1669
1670
  					ipmr_queue_xmit(net, mrt, skb2, cache,
  							psend);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1671
  			}
c354e1246   Jianjun Kong   net: clean up net...
1672
  			psend = ct;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1673
1674
1675
1676
1677
  		}
  	}
  	if (psend != -1) {
  		if (local) {
  			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1678

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1679
  			if (skb2)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1680
  				ipmr_queue_xmit(net, mrt, skb2, cache, psend);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1681
  		} else {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1682
  			ipmr_queue_xmit(net, mrt, skb, cache, psend);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1683
1684
1685
1686
1687
1688
1689
1690
1691
  			return 0;
  		}
  	}
  
  dont_forward:
  	if (!local)
  		kfree_skb(skb);
  	return 0;
  }
417da66fa   David S. Miller   ipv4: Rework ipmr...
1692
  static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb)
ee3f1aaf9   David S. Miller   ipv4: Lookup mult...
1693
  {
417da66fa   David S. Miller   ipv4: Rework ipmr...
1694
1695
  	struct rtable *rt = skb_rtable(skb);
  	struct iphdr *iph = ip_hdr(skb);
da91981be   David S. Miller   ipv4: Use flowi4 ...
1696
  	struct flowi4 fl4 = {
417da66fa   David S. Miller   ipv4: Rework ipmr...
1697
1698
  		.daddr = iph->daddr,
  		.saddr = iph->saddr,
b0fe4a318   Julian Anastasov   ipv4: use RT_TOS ...
1699
  		.flowi4_tos = RT_TOS(iph->tos),
da91981be   David S. Miller   ipv4: Use flowi4 ...
1700
1701
1702
  		.flowi4_oif = rt->rt_oif,
  		.flowi4_iif = rt->rt_iif,
  		.flowi4_mark = rt->rt_mark,
ee3f1aaf9   David S. Miller   ipv4: Lookup mult...
1703
1704
1705
  	};
  	struct mr_table *mrt;
  	int err;
da91981be   David S. Miller   ipv4: Use flowi4 ...
1706
  	err = ipmr_fib_lookup(net, &fl4, &mrt);
ee3f1aaf9   David S. Miller   ipv4: Lookup mult...
1707
1708
1709
1710
  	if (err)
  		return ERR_PTR(err);
  	return mrt;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1711
1712
1713
  
  /*
   *	Multicast packets for forwarding arrive here
4c9687098   Eric Dumazet   ipmr: RCU convers...
1714
   *	Called with rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1715
1716
1717
1718
1719
   */
  
  int ip_mr_input(struct sk_buff *skb)
  {
  	struct mfc_cache *cache;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1720
  	struct net *net = dev_net(skb->dev);
511c3f92a   Eric Dumazet   net: skb->rtable ...
1721
  	int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1722
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1723
1724
  
  	/* Packet is looped back after forward, it should not be
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1725
  	 * forwarded second time, but still can be delivered locally.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1726
  	 */
4c9687098   Eric Dumazet   ipmr: RCU convers...
1727
  	if (IPCB(skb)->flags & IPSKB_FORWARDED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1728
  		goto dont_forward;
417da66fa   David S. Miller   ipv4: Rework ipmr...
1729
  	mrt = ipmr_rt_fib_lookup(net, skb);
ee3f1aaf9   David S. Miller   ipv4: Lookup mult...
1730
1731
1732
  	if (IS_ERR(mrt)) {
  		kfree_skb(skb);
  		return PTR_ERR(mrt);
e40dbc51f   Ben Greear   ipmr: Don't leak ...
1733
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1734
  	if (!local) {
4c9687098   Eric Dumazet   ipmr: RCU convers...
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
  		if (IPCB(skb)->opt.router_alert) {
  			if (ip_call_ra_chain(skb))
  				return 0;
  		} else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) {
  			/* IGMPv1 (and broken IGMPv2 implementations sort of
  			 * Cisco IOS <= 11.2(8)) do not put router alert
  			 * option to IGMP packets destined to routable
  			 * groups. It is very bad, because it means
  			 * that we can forward NO IGMP messages.
  			 */
  			struct sock *mroute_sk;
  
  			mroute_sk = rcu_dereference(mrt->mroute_sk);
  			if (mroute_sk) {
  				nf_reset(skb);
  				raw_rcv(mroute_sk, skb);
  				return 0;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1753
1754
  		    }
  	}
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1755
  	/* already under rcu_read_lock() */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1756
  	cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1757
1758
1759
1760
  
  	/*
  	 *	No usable cache entry
  	 */
c354e1246   Jianjun Kong   net: clean up net...
1761
  	if (cache == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1762
1763
1764
1765
1766
  		int vif;
  
  		if (local) {
  			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
  			ip_local_deliver(skb);
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1767
  			if (skb2 == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1768
  				return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1769
1770
  			skb = skb2;
  		}
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1771
  		read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1772
  		vif = ipmr_find_vif(mrt, skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1773
  		if (vif >= 0) {
0eae88f31   Eric Dumazet   net: Fix various ...
1774
  			int err2 = ipmr_cache_unresolved(mrt, vif, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1775
  			read_unlock(&mrt_lock);
0eae88f31   Eric Dumazet   net: Fix various ...
1776
  			return err2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1777
1778
1779
1780
1781
  		}
  		read_unlock(&mrt_lock);
  		kfree_skb(skb);
  		return -ENODEV;
  	}
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1782
  	read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1783
  	ip_mr_forward(net, mrt, skb, cache, local);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
  	read_unlock(&mrt_lock);
  
  	if (local)
  		return ip_local_deliver(skb);
  
  	return 0;
  
  dont_forward:
  	if (local)
  		return ip_local_deliver(skb);
  	kfree_skb(skb);
  	return 0;
  }
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1797
  #ifdef CONFIG_IP_PIMSM
55747a0a7   Eric Dumazet   ipmr: __pim_rcv()...
1798
  /* called with rcu_read_lock() */
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1799
1800
  static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
  		     unsigned int pimlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1801
  {
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1802
1803
  	struct net_device *reg_dev = NULL;
  	struct iphdr *encap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1804

b1879204d   Ilpo Järvinen   ipmr: merge commo...
1805
  	encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1806
  	/*
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1807
1808
1809
1810
  	 * Check that:
  	 * a. packet is really sent to a multicast group
  	 * b. packet is not a NULL-REGISTER
  	 * c. packet is not truncated
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1811
  	 */
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1812
  	if (!ipv4_is_multicast(encap->daddr) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1813
  	    encap->tot_len == 0 ||
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1814
1815
  	    ntohs(encap->tot_len) + pimlen > skb->len)
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1816
1817
  
  	read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1818
1819
  	if (mrt->mroute_reg_vif_num >= 0)
  		reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1820
  	read_unlock(&mrt_lock);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1821
  	if (reg_dev == NULL)
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1822
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1823

b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
1824
  	skb->mac_header = skb->network_header;
55747a0a7   Eric Dumazet   ipmr: __pim_rcv()...
1825
  	skb_pull(skb, (u8 *)encap - skb->data);
31c7711b5   Arnaldo Carvalho de Melo   [SK_BUFF]: Some m...
1826
  	skb_reset_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1827
  	skb->protocol = htons(ETH_P_IP);
55747a0a7   Eric Dumazet   ipmr: __pim_rcv()...
1828
  	skb->ip_summed = CHECKSUM_NONE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1829
  	skb->pkt_type = PACKET_HOST;
d19d56ddc   Eric Dumazet   net: Introduce sk...
1830
1831
  
  	skb_tunnel_rx(skb, reg_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1832
  	netif_rx(skb);
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1833

55747a0a7   Eric Dumazet   ipmr: __pim_rcv()...
1834
  	return NET_RX_SUCCESS;
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1835
1836
1837
1838
1839
1840
1841
  }
  #endif
  
  #ifdef CONFIG_IP_PIMSM_V1
  /*
   * Handle IGMP messages of PIMv1
   */
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1842
  int pim_rcv_v1(struct sk_buff *skb)
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1843
1844
  {
  	struct igmphdr *pim;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1845
  	struct net *net = dev_net(skb->dev);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1846
  	struct mr_table *mrt;
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1847
1848
1849
1850
1851
  
  	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
  		goto drop;
  
  	pim = igmp_hdr(skb);
417da66fa   David S. Miller   ipv4: Rework ipmr...
1852
  	mrt = ipmr_rt_fib_lookup(net, skb);
ee3f1aaf9   David S. Miller   ipv4: Lookup mult...
1853
1854
  	if (IS_ERR(mrt))
  		goto drop;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1855
  	if (!mrt->mroute_do_pim ||
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1856
1857
  	    pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
  		goto drop;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1858
  	if (__pim_rcv(mrt, skb, sizeof(*pim))) {
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1859
1860
1861
  drop:
  		kfree_skb(skb);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1862
1863
1864
1865
1866
  	return 0;
  }
  #endif
  
  #ifdef CONFIG_IP_PIMSM_V2
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1867
  static int pim_rcv(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1868
1869
  {
  	struct pimreghdr *pim;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1870
1871
  	struct net *net = dev_net(skb->dev);
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1872

b1879204d   Ilpo Järvinen   ipmr: merge commo...
1873
  	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1874
  		goto drop;
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1875
  	pim = (struct pimreghdr *)skb_transport_header(skb);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1876
1877
  	if (pim->type != ((PIM_VERSION << 4) | (PIM_REGISTER)) ||
  	    (pim->flags & PIM_NULL_REGISTER) ||
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1878
  	    (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
d3bc23e7e   Al Viro   [NET]: Annotate c...
1879
  	     csum_fold(skb_checksum(skb, 0, skb->len, 0))))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1880
  		goto drop;
417da66fa   David S. Miller   ipv4: Rework ipmr...
1881
  	mrt = ipmr_rt_fib_lookup(net, skb);
ee3f1aaf9   David S. Miller   ipv4: Lookup mult...
1882
1883
  	if (IS_ERR(mrt))
  		goto drop;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1884
  	if (__pim_rcv(mrt, skb, sizeof(*pim))) {
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1885
1886
1887
  drop:
  		kfree_skb(skb);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1888
1889
1890
  	return 0;
  }
  #endif
cb6a4e461   Patrick McHardy   net: ipmr: add su...
1891
1892
  static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
  			      struct mfc_cache *c, struct rtmsg *rtm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1893
1894
1895
  {
  	int ct;
  	struct rtnexthop *nhp;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1896
  	u8 *b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1897
  	struct rtattr *mp_head;
7438189ba   Nicolas Dichtel   net: ipmr/ip6mr: ...
1898
  	/* If cache is unresolved, don't try to parse IIF and OIF */
ed0f160ad   Dan Carpenter   ipmr: off by one ...
1899
  	if (c->mfc_parent >= MAXVIFS)
7438189ba   Nicolas Dichtel   net: ipmr/ip6mr: ...
1900
  		return -ENOENT;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1901
1902
  	if (VIF_EXISTS(mrt, c->mfc_parent))
  		RTA_PUT(skb, RTA_IIF, 4, &mrt->vif_table[c->mfc_parent].dev->ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1903

c354e1246   Jianjun Kong   net: clean up net...
1904
  	mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1905
1906
  
  	for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1907
  		if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1908
1909
  			if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
  				goto rtattr_failure;
c354e1246   Jianjun Kong   net: clean up net...
1910
  			nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1911
1912
  			nhp->rtnh_flags = 0;
  			nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1913
  			nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1914
1915
1916
1917
  			nhp->rtnh_len = sizeof(*nhp);
  		}
  	}
  	mp_head->rta_type = RTA_MULTIPATH;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1918
  	mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1919
1920
1921
1922
  	rtm->rtm_type = RTN_MULTICAST;
  	return 1;
  
  rtattr_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
1923
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1924
1925
  	return -EMSGSIZE;
  }
9a1b9496c   David S. Miller   ipv4: Pass explic...
1926
1927
1928
  int ipmr_get_route(struct net *net, struct sk_buff *skb,
  		   __be32 saddr, __be32 daddr,
  		   struct rtmsg *rtm, int nowait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1929
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1930
  	struct mfc_cache *cache;
9a1b9496c   David S. Miller   ipv4: Pass explic...
1931
1932
  	struct mr_table *mrt;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1933

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1934
1935
1936
  	mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return -ENOENT;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1937
  	rcu_read_lock();
9a1b9496c   David S. Miller   ipv4: Pass explic...
1938
  	cache = ipmr_cache_find(mrt, saddr, daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1939

c354e1246   Jianjun Kong   net: clean up net...
1940
  	if (cache == NULL) {
722874909   Alexey Kuznetsov   [IPV4] ipmr: ip m...
1941
  		struct sk_buff *skb2;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1942
  		struct iphdr *iph;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1943
  		struct net_device *dev;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1944
  		int vif = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1945
1946
  
  		if (nowait) {
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1947
  			rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1948
1949
1950
1951
  			return -EAGAIN;
  		}
  
  		dev = skb->dev;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1952
  		read_lock(&mrt_lock);
a8cb16dd9   Eric Dumazet   ipmr: cleanups
1953
1954
1955
  		if (dev)
  			vif = ipmr_find_vif(mrt, dev);
  		if (vif < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1956
  			read_unlock(&mrt_lock);
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1957
  			rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1958
1959
  			return -ENODEV;
  		}
722874909   Alexey Kuznetsov   [IPV4] ipmr: ip m...
1960
1961
1962
  		skb2 = skb_clone(skb, GFP_ATOMIC);
  		if (!skb2) {
  			read_unlock(&mrt_lock);
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1963
  			rcu_read_unlock();
722874909   Alexey Kuznetsov   [IPV4] ipmr: ip m...
1964
1965
  			return -ENOMEM;
  		}
e2d1bca7e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1966
1967
  		skb_push(skb2, sizeof(struct iphdr));
  		skb_reset_network_header(skb2);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1968
1969
  		iph = ip_hdr(skb2);
  		iph->ihl = sizeof(struct iphdr) >> 2;
9a1b9496c   David S. Miller   ipv4: Pass explic...
1970
1971
  		iph->saddr = saddr;
  		iph->daddr = daddr;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1972
  		iph->version = 0;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1973
  		err = ipmr_cache_unresolved(mrt, vif, skb2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1974
  		read_unlock(&mrt_lock);
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1975
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1976
1977
  		return err;
  	}
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1978
1979
  	read_lock(&mrt_lock);
  	if (!nowait && (rtm->rtm_flags & RTM_F_NOTIFY))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1980
  		cache->mfc_flags |= MFC_NOTIFY;
cb6a4e461   Patrick McHardy   net: ipmr: add su...
1981
  	err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1982
  	read_unlock(&mrt_lock);
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
1983
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1984
1985
  	return err;
  }
cb6a4e461   Patrick McHardy   net: ipmr: add su...
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
  static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
  			    u32 pid, u32 seq, struct mfc_cache *c)
  {
  	struct nlmsghdr *nlh;
  	struct rtmsg *rtm;
  
  	nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
  	if (nlh == NULL)
  		return -EMSGSIZE;
  
  	rtm = nlmsg_data(nlh);
  	rtm->rtm_family   = RTNL_FAMILY_IPMR;
  	rtm->rtm_dst_len  = 32;
  	rtm->rtm_src_len  = 32;
  	rtm->rtm_tos      = 0;
  	rtm->rtm_table    = mrt->id;
  	NLA_PUT_U32(skb, RTA_TABLE, mrt->id);
  	rtm->rtm_type     = RTN_MULTICAST;
  	rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
  	rtm->rtm_protocol = RTPROT_UNSPEC;
  	rtm->rtm_flags    = 0;
  
  	NLA_PUT_BE32(skb, RTA_SRC, c->mfc_origin);
  	NLA_PUT_BE32(skb, RTA_DST, c->mfc_mcastgrp);
  
  	if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0)
  		goto nla_put_failure;
  
  	return nlmsg_end(skb, nlh);
  
  nla_put_failure:
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
  }
  
  static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
  {
  	struct net *net = sock_net(skb->sk);
  	struct mr_table *mrt;
  	struct mfc_cache *mfc;
  	unsigned int t = 0, s_t;
  	unsigned int h = 0, s_h;
  	unsigned int e = 0, s_e;
  
  	s_t = cb->args[0];
  	s_h = cb->args[1];
  	s_e = cb->args[2];
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2033
  	rcu_read_lock();
cb6a4e461   Patrick McHardy   net: ipmr: add su...
2034
2035
2036
2037
2038
2039
  	ipmr_for_each_table(mrt, net) {
  		if (t < s_t)
  			goto next_table;
  		if (t > s_t)
  			s_h = 0;
  		for (h = s_h; h < MFC_LINES; h++) {
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2040
  			list_for_each_entry_rcu(mfc, &mrt->mfc_cache_array[h], list) {
cb6a4e461   Patrick McHardy   net: ipmr: add su...
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
  				if (e < s_e)
  					goto next_entry;
  				if (ipmr_fill_mroute(mrt, skb,
  						     NETLINK_CB(cb->skb).pid,
  						     cb->nlh->nlmsg_seq,
  						     mfc) < 0)
  					goto done;
  next_entry:
  				e++;
  			}
  			e = s_e = 0;
  		}
  		s_h = 0;
  next_table:
  		t++;
  	}
  done:
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2058
  	rcu_read_unlock();
cb6a4e461   Patrick McHardy   net: ipmr: add su...
2059
2060
2061
2062
2063
2064
2065
  
  	cb->args[2] = e;
  	cb->args[1] = h;
  	cb->args[0] = t;
  
  	return skb->len;
  }
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2066
  #ifdef CONFIG_PROC_FS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2067
  /*
a8cb16dd9   Eric Dumazet   ipmr: cleanups
2068
2069
   *	The /proc interfaces to multicast routing :
   *	/proc/net/ip_mr_cache & /proc/net/ip_mr_vif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2070
2071
   */
  struct ipmr_vif_iter {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2072
  	struct seq_net_private p;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2073
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2074
2075
  	int ct;
  };
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2076
2077
  static struct vif_device *ipmr_vif_seq_idx(struct net *net,
  					   struct ipmr_vif_iter *iter,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2078
2079
  					   loff_t pos)
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2080
  	struct mr_table *mrt = iter->mrt;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2081
2082
2083
  
  	for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) {
  		if (!VIF_EXISTS(mrt, iter->ct))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2084
  			continue;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2085
  		if (pos-- == 0)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2086
  			return &mrt->vif_table[iter->ct];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2087
2088
2089
2090
2091
  	}
  	return NULL;
  }
  
  static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
ba93ef746   Stephen Hemminger   [IPV4]: ipmr spar...
2092
  	__acquires(mrt_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2093
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2094
  	struct ipmr_vif_iter *iter = seq->private;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2095
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2096
2097
2098
2099
2100
2101
2102
  	struct mr_table *mrt;
  
  	mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return ERR_PTR(-ENOENT);
  
  	iter->mrt = mrt;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2103

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2104
  	read_lock(&mrt_lock);
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2105
  	return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2106
2107
2108
2109
2110
2111
  		: SEQ_START_TOKEN;
  }
  
  static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct ipmr_vif_iter *iter = seq->private;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2112
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2113
  	struct mr_table *mrt = iter->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2114
2115
2116
  
  	++*pos;
  	if (v == SEQ_START_TOKEN)
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2117
  		return ipmr_vif_seq_idx(net, iter, 0);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2118

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2119
2120
  	while (++iter->ct < mrt->maxvif) {
  		if (!VIF_EXISTS(mrt, iter->ct))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2121
  			continue;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2122
  		return &mrt->vif_table[iter->ct];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2123
2124
2125
2126
2127
  	}
  	return NULL;
  }
  
  static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
ba93ef746   Stephen Hemminger   [IPV4]: ipmr spar...
2128
  	__releases(mrt_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2129
2130
2131
2132
2133
2134
  {
  	read_unlock(&mrt_lock);
  }
  
  static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2135
2136
  	struct ipmr_vif_iter *iter = seq->private;
  	struct mr_table *mrt = iter->mrt;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2137

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2138
  	if (v == SEQ_START_TOKEN) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2139
  		seq_puts(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2140
2141
2142
2143
2144
2145
2146
2147
2148
  			 "Interface      BytesIn  PktsIn  BytesOut PktsOut Flags Local    Remote
  ");
  	} else {
  		const struct vif_device *vif = v;
  		const char *name =  vif->dev ? vif->dev->name : "none";
  
  		seq_printf(seq,
  			   "%2Zd %-10s %8ld %7ld  %8ld %7ld %05X %08X %08X
  ",
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2149
  			   vif - mrt->vif_table,
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2150
  			   name, vif->bytes_in, vif->pkt_in,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2151
2152
2153
2154
2155
  			   vif->bytes_out, vif->pkt_out,
  			   vif->flags, vif->local, vif->remote);
  	}
  	return 0;
  }
f690808e1   Stephen Hemminger   [NET]: make seq_o...
2156
  static const struct seq_operations ipmr_vif_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2157
2158
2159
2160
2161
2162
2163
2164
  	.start = ipmr_vif_seq_start,
  	.next  = ipmr_vif_seq_next,
  	.stop  = ipmr_vif_seq_stop,
  	.show  = ipmr_vif_seq_show,
  };
  
  static int ipmr_vif_open(struct inode *inode, struct file *file)
  {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2165
2166
  	return seq_open_net(inode, file, &ipmr_vif_seq_ops,
  			    sizeof(struct ipmr_vif_iter));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2167
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2168
  static const struct file_operations ipmr_vif_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2169
2170
2171
2172
  	.owner	 = THIS_MODULE,
  	.open    = ipmr_vif_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2173
  	.release = seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2174
2175
2176
  };
  
  struct ipmr_mfc_iter {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2177
  	struct seq_net_private p;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2178
  	struct mr_table *mrt;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2179
  	struct list_head *cache;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2180
2181
  	int ct;
  };
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2182
2183
  static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
  					  struct ipmr_mfc_iter *it, loff_t pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2184
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2185
  	struct mr_table *mrt = it->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2186
  	struct mfc_cache *mfc;
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2187
  	rcu_read_lock();
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2188
  	for (it->ct = 0; it->ct < MFC_LINES; it->ct++) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2189
  		it->cache = &mrt->mfc_cache_array[it->ct];
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2190
  		list_for_each_entry_rcu(mfc, it->cache, list)
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2191
  			if (pos-- == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2192
  				return mfc;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2193
  	}
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2194
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2195

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2196
  	spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2197
  	it->cache = &mrt->mfc_unres_queue;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2198
  	list_for_each_entry(mfc, it->cache, list)
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
2199
  		if (pos-- == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
  			return mfc;
  	spin_unlock_bh(&mfc_unres_lock);
  
  	it->cache = NULL;
  	return NULL;
  }
  
  
  static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
  {
  	struct ipmr_mfc_iter *it = seq->private;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2211
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2212
  	struct mr_table *mrt;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2213

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2214
2215
2216
  	mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return ERR_PTR(-ENOENT);
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2217

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2218
  	it->mrt = mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2219
2220
  	it->cache = NULL;
  	it->ct = 0;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2221
  	return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2222
2223
2224
2225
2226
2227
2228
  		: SEQ_START_TOKEN;
  }
  
  static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct mfc_cache *mfc = v;
  	struct ipmr_mfc_iter *it = seq->private;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2229
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2230
  	struct mr_table *mrt = it->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2231
2232
2233
2234
  
  	++*pos;
  
  	if (v == SEQ_START_TOKEN)
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2235
  		return ipmr_mfc_seq_idx(net, seq->private, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2236

862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2237
2238
  	if (mfc->list.next != it->cache)
  		return list_entry(mfc->list.next, struct mfc_cache, list);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2239

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2240
  	if (it->cache == &mrt->mfc_unres_queue)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2241
  		goto end_of_list;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2242
  	BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2243
2244
  
  	while (++it->ct < MFC_LINES) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2245
  		it->cache = &mrt->mfc_cache_array[it->ct];
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2246
2247
2248
  		if (list_empty(it->cache))
  			continue;
  		return list_first_entry(it->cache, struct mfc_cache, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2249
2250
2251
  	}
  
  	/* exhausted cache_array, show unresolved */
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2252
  	rcu_read_unlock();
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2253
  	it->cache = &mrt->mfc_unres_queue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2254
  	it->ct = 0;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2255

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2256
  	spin_lock_bh(&mfc_unres_lock);
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2257
2258
  	if (!list_empty(it->cache))
  		return list_first_entry(it->cache, struct mfc_cache, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2259

a8cb16dd9   Eric Dumazet   ipmr: cleanups
2260
  end_of_list:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2261
2262
2263
2264
2265
2266
2267
2268
2269
  	spin_unlock_bh(&mfc_unres_lock);
  	it->cache = NULL;
  
  	return NULL;
  }
  
  static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
  {
  	struct ipmr_mfc_iter *it = seq->private;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2270
  	struct mr_table *mrt = it->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2271

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2272
  	if (it->cache == &mrt->mfc_unres_queue)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2273
  		spin_unlock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2274
  	else if (it->cache == &mrt->mfc_cache_array[it->ct])
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2275
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2276
2277
2278
2279
2280
2281
2282
  }
  
  static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
  {
  	int n;
  
  	if (v == SEQ_START_TOKEN) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2283
  		seq_puts(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2284
2285
2286
2287
2288
  		 "Group    Origin   Iif     Pkts    Bytes    Wrong Oifs
  ");
  	} else {
  		const struct mfc_cache *mfc = v;
  		const struct ipmr_mfc_iter *it = seq->private;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2289
  		const struct mr_table *mrt = it->mrt;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2290

0eae88f31   Eric Dumazet   net: Fix various ...
2291
2292
2293
  		seq_printf(seq, "%08X %08X %-3hd",
  			   (__force u32) mfc->mfc_mcastgrp,
  			   (__force u32) mfc->mfc_origin,
1ea472e2d   Benjamin Thery   net: fix /proc/ne...
2294
  			   mfc->mfc_parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2295

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2296
  		if (it->cache != &mrt->mfc_unres_queue) {
1ea472e2d   Benjamin Thery   net: fix /proc/ne...
2297
2298
2299
2300
  			seq_printf(seq, " %8lu %8lu %8lu",
  				   mfc->mfc_un.res.pkt,
  				   mfc->mfc_un.res.bytes,
  				   mfc->mfc_un.res.wrong_if);
132adf546   Stephen Hemminger   [IPV4]: cleanup
2301
  			for (n = mfc->mfc_un.res.minvif;
a8cb16dd9   Eric Dumazet   ipmr: cleanups
2302
  			     n < mfc->mfc_un.res.maxvif; n++) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2303
  				if (VIF_EXISTS(mrt, n) &&
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2304
2305
  				    mfc->mfc_un.res.ttls[n] < 255)
  					seq_printf(seq,
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2306
  					   " %2d:%-3d",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2307
2308
  					   n, mfc->mfc_un.res.ttls[n]);
  			}
1ea472e2d   Benjamin Thery   net: fix /proc/ne...
2309
2310
2311
2312
2313
  		} else {
  			/* unresolved mfc_caches don't contain
  			 * pkt, bytes and wrong_if values
  			 */
  			seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2314
2315
2316
2317
2318
2319
  		}
  		seq_putc(seq, '
  ');
  	}
  	return 0;
  }
f690808e1   Stephen Hemminger   [NET]: make seq_o...
2320
  static const struct seq_operations ipmr_mfc_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2321
2322
2323
2324
2325
2326
2327
2328
  	.start = ipmr_mfc_seq_start,
  	.next  = ipmr_mfc_seq_next,
  	.stop  = ipmr_mfc_seq_stop,
  	.show  = ipmr_mfc_seq_show,
  };
  
  static int ipmr_mfc_open(struct inode *inode, struct file *file)
  {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2329
2330
  	return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
  			    sizeof(struct ipmr_mfc_iter));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2331
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2332
  static const struct file_operations ipmr_mfc_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2333
2334
2335
2336
  	.owner	 = THIS_MODULE,
  	.open    = ipmr_mfc_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2337
  	.release = seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2338
  };
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2339
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2340
2341
  
  #ifdef CONFIG_IP_PIMSM_V2
32613090a   Alexey Dobriyan   net: constify str...
2342
  static const struct net_protocol pim_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2343
  	.handler	=	pim_rcv,
403dbb97f   Tom Goff   PIM-SM: namespace...
2344
  	.netns_ok	=	1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2345
2346
2347
2348
2349
2350
2351
  };
  #endif
  
  
  /*
   *	Setup for IP multicast routing
   */
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2352
2353
  static int __net_init ipmr_net_init(struct net *net)
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2354
  	int err;
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2355

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2356
2357
  	err = ipmr_rules_init(net);
  	if (err < 0)
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2358
  		goto fail;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2359
2360
2361
2362
2363
2364
2365
2366
  
  #ifdef CONFIG_PROC_FS
  	err = -ENOMEM;
  	if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops))
  		goto proc_vif_fail;
  	if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops))
  		goto proc_cache_fail;
  #endif
2bb8b26c3   Benjamin Thery   netns: ipmr: dyna...
2367
  	return 0;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2368
2369
2370
2371
  #ifdef CONFIG_PROC_FS
  proc_cache_fail:
  	proc_net_remove(net, "ip_mr_vif");
  proc_vif_fail:
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2372
  	ipmr_rules_exit(net);
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2373
  #endif
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2374
2375
2376
2377
2378
2379
  fail:
  	return err;
  }
  
  static void __net_exit ipmr_net_exit(struct net *net)
  {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2380
2381
2382
2383
  #ifdef CONFIG_PROC_FS
  	proc_net_remove(net, "ip_mr_cache");
  	proc_net_remove(net, "ip_mr_vif");
  #endif
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2384
  	ipmr_rules_exit(net);
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2385
2386
2387
2388
2389
2390
  }
  
  static struct pernet_operations ipmr_net_ops = {
  	.init = ipmr_net_init,
  	.exit = ipmr_net_exit,
  };
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2391

03d2f897e   Wang Chen   ipv4: Do cleanup ...
2392
  int __init ip_mr_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2393
  {
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2394
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2395
2396
  	mrt_cachep = kmem_cache_create("ip_mrt_cache",
  				       sizeof(struct mfc_cache),
a8c9486b8   Eric Dumazet   ipmr: RCU protect...
2397
  				       0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
20c2df83d   Paul Mundt   mm: Remove slab d...
2398
  				       NULL);
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2399
2400
  	if (!mrt_cachep)
  		return -ENOMEM;
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2401
2402
2403
  	err = register_pernet_subsys(&ipmr_net_ops);
  	if (err)
  		goto reg_pernet_fail;
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2404
2405
2406
  	err = register_netdevice_notifier(&ip_mr_notifier);
  	if (err)
  		goto reg_notif_fail;
403dbb97f   Tom Goff   PIM-SM: namespace...
2407
2408
2409
2410
2411
2412
2413
2414
  #ifdef CONFIG_IP_PIMSM_V2
  	if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) {
  		printk(KERN_ERR "ip_mr_init: can't add PIM protocol
  ");
  		err = -EAGAIN;
  		goto add_proto_fail;
  	}
  #endif
c7ac8679b   Greg Rose   rtnetlink: Comput...
2415
2416
  	rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE,
  		      NULL, ipmr_rtm_dumproute, NULL);
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2417
  	return 0;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2418

403dbb97f   Tom Goff   PIM-SM: namespace...
2419
2420
2421
2422
  #ifdef CONFIG_IP_PIMSM_V2
  add_proto_fail:
  	unregister_netdevice_notifier(&ip_mr_notifier);
  #endif
c3e388964   Benjamin Thery   net: fix ip_mr_in...
2423
  reg_notif_fail:
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2424
2425
  	unregister_pernet_subsys(&ipmr_net_ops);
  reg_pernet_fail:
c3e388964   Benjamin Thery   net: fix ip_mr_in...
2426
  	kmem_cache_destroy(mrt_cachep);
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2427
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2428
  }