Blame view

net/ipv4/ipmr.c 55 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
62
63
  #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>
  #include <net/ipip.h>
  #include <net/checksum.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
64
  #include <net/netlink.h>
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
65
  #include <net/fib_rules.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
69
  
  #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
  #define CONFIG_IP_PIMSM	1
  #endif
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
70
  struct mr_table {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
71
  	struct list_head	list;
8de53dfbf   Patrick McHardy   ipv4: ipmr: fix N...
72
73
74
  #ifdef CONFIG_NET_NS
  	struct net		*net;
  #endif
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
75
  	u32			id;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
76
77
78
79
80
81
82
83
84
85
86
87
88
  	struct sock		*mroute_sk;
  	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...
89
90
91
92
93
94
95
  struct ipmr_rule {
  	struct fib_rule		common;
  };
  
  struct ipmr_result {
  	struct mr_table		*mrt;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
101
102
103
104
  /* Big lock, protecting vif table, mrt cache and mroute socket state.
     Note that the changes are semaphored via rtnl_lock.
   */
  
  static DEFINE_RWLOCK(mrt_lock);
  
  /*
   *	Multicast router control variables
   */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
105
  #define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
111
112
113
114
115
116
  
  /* Special spinlock for queue of unresolved entries */
  static DEFINE_SPINLOCK(mfc_unres_lock);
  
  /* We return to original Alan's scheme. Hash table of resolved
     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.
   */
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
117
  static struct kmem_cache *mrt_cachep __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
119
  static struct mr_table *ipmr_new_table(struct net *net, u32 id);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
120
121
122
123
  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...
124
  			     struct sk_buff *pkt, vifi_t vifi, int assert);
cb6a4e461   Patrick McHardy   net: ipmr: add su...
125
126
  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...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  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;
  }
  
  static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
  			   struct mr_table **mrt)
  {
  	struct ipmr_result res;
  	struct fib_lookup_arg arg = { .result = &res, };
  	int err;
  
  	err = fib_rules_lookup(net->ipv4.mr_rules_ops, flp, 0, &arg);
  	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
163

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  	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...
212
  static const struct fib_rules_ops __net_initdata ipmr_rules_ops_template = {
25239cee7   Patrick McHardy   net: rtnetlink: d...
213
  	.family		= RTNL_FAMILY_IPMR,
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
214
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
  	.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...
262
263
  	list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
  		list_del(&mrt->list);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
264
  		kfree(mrt);
035320d54   Eric Dumazet   ipmr: dont corrup...
265
  	}
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  	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;
  }
  
  static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
  			   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
385
386
  		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);
  		} else
  			err = -EOPNOTSUPP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
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
420
421
422
423
424
425
426
427
  	struct mr_table *mrt;
  	struct flowi fl = {
  		.oif		= dev->ifindex,
  		.iif		= skb->skb_iif,
  		.mark		= skb->mark,
  	};
  	int err;
  
  	err = ipmr_fib_lookup(net, &fl, &mrt);
e40dbc51f   Ben Greear   ipmr: Don't leak ...
428
429
  	if (err < 0) {
  		kfree_skb(skb);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
430
  		return err;
e40dbc51f   Ben Greear   ipmr: Don't leak ...
431
  	}
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
432

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

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

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
464
  	dev = alloc_netdev(0, name, reg_vif_setup);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
  
  	if (dev == NULL)
  		return NULL;
403dbb97f   Tom Goff   PIM-SM: namespace...
468
  	dev_net_set(dev, net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
472
473
  	if (register_netdevice(dev)) {
  		free_netdev(dev);
  		return NULL;
  	}
  	dev->iflink = 0;
71e27da96   Herbert Xu   [IPV4]: Restore o...
474
475
476
  	rcu_read_lock();
  	if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
  		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
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
527
  	if (vifi+1 == mrt->maxvif) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
529
  		int tmp;
  		for (tmp=vifi-1; tmp>=0; tmp--) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
530
  			if (VIF_EXISTS(mrt, tmp))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
  				break;
  		}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
533
  		mrt->maxvif = tmp+1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
536
537
538
  	}
  
  	write_unlock_bh(&mrt_lock);
  
  	dev_set_allmulti(dev, -1);
e5ed63991   Herbert Xu   [IPV4]: Replace _...
539
  	if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
42f811b8b   Herbert Xu   [IPV4]: Convert I...
540
  		IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
  		ip_rt_multicast_event(in_dev);
  	}
7dc00c82c   Wang Chen   ipv4: Fix ipmr un...
543
  	if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER) && !notify)
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
544
  		unregister_netdevice_queue(dev, head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
548
  
  	dev_put(dev);
  	return 0;
  }
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
549
550
  static inline void ipmr_cache_free(struct mfc_cache *c)
  {
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
551
552
  	kmem_cache_free(mrt_cachep, c);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
  /* Destroy an unresolved cache entry, killing queued skbs
     and reporting error to netlink readers.
   */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
556
  static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
  {
8de53dfbf   Patrick McHardy   ipv4: ipmr: fix N...
558
  	struct net *net = read_pnet(&mrt->net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  	struct sk_buff *skb;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
560
  	struct nlmsgerr *e;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561

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

c354e1246   Jianjun Kong   net: clean up net...
564
  	while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
565
  		if (ip_hdr(skb)->version == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
568
569
  			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...
570
571
572
  			e = NLMSG_DATA(nlh);
  			e->error = -ETIMEDOUT;
  			memset(&e->msg, 0, sizeof(e->msg));
2942e9005   Thomas Graf   [RTNETLINK]: Use ...
573

4feb88e5c   Benjamin Thery   netns: ipmr: enab...
574
  			rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
577
  		} else
  			kfree_skb(skb);
  	}
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
578
  	ipmr_cache_free(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
  }
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
580
  /* Timer process for the unresolved queue. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581

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

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

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
777
778
  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
779
780
  {
  	struct sk_buff *skb;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
781
  	struct nlmsgerr *e;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
  
  	/*
  	 *	Play the pending entries through our router
  	 */
c354e1246   Jianjun Kong   net: clean up net...
786
  	while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
787
  		if (ip_hdr(skb)->version == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
  			struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
cb6a4e461   Patrick McHardy   net: ipmr: add su...
789
  			if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
790
791
  				nlh->nlmsg_len = (skb_tail_pointer(skb) -
  						  (u8 *)nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
795
  			} 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...
796
797
798
  				e = NLMSG_DATA(nlh);
  				e->error = -EMSGSIZE;
  				memset(&e->msg, 0, sizeof(e->msg));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
  			}
2942e9005   Thomas Graf   [RTNETLINK]: Use ...
800

d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
801
  			rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
  		} else
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
803
  			ip_mr_forward(net, mrt, skb, c, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
809
810
811
812
  	}
  }
  
  /*
   *	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...
813

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
814
  static int ipmr_cache_report(struct mr_table *mrt,
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
815
  			     struct sk_buff *pkt, vifi_t vifi, int assert)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
  {
  	struct sk_buff *skb;
c9bdd4b52   Arnaldo Carvalho de Melo   [IP]: Introduce i...
818
  	const int ihl = ip_hdrlen(pkt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
821
822
823
824
825
826
827
828
  	struct igmphdr *igmp;
  	struct igmpmsg *msg;
  	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
829
  	if (!skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
831
832
833
834
835
836
837
838
  		return -ENOBUFS;
  
  #ifdef CONFIG_IP_PIMSM
  	if (assert == IGMPMSG_WHOLEPKT) {
  		/* Ugly, but we have no choice with this interface.
  		   Duplicate old header, fix ihl, length etc.
  		   And all this only to mangle msg->im_msgtype and
  		   to set msg->im_mbz to "mbz" :-)
  		 */
878c81450   Arnaldo Carvalho de Melo   [SK_BUFF] ipmr: A...
839
840
  		skb_push(skb, sizeof(struct iphdr));
  		skb_reset_network_header(skb);
badff6d01   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
841
  		skb_reset_transport_header(skb);
0272ffc46   Arnaldo Carvalho de Melo   [SK_BUFF] ipmr: M...
842
  		msg = (struct igmpmsg *)skb_network_header(skb);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
843
  		memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
845
  		msg->im_msgtype = IGMPMSG_WHOLEPKT;
  		msg->im_mbz = 0;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
846
  		msg->im_vif = mrt->mroute_reg_vif_num;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
847
848
849
  		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...
850
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
  #endif
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
852
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853
854
855
  	/*
  	 *	Copy the IP header
  	 */
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
856
  	skb->network_header = skb->tail;
ddc7b8e32   Arnaldo Carvalho de Melo   [SK_BUFF]: Some m...
857
  	skb_put(skb, ihl);
27d7ff46a   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
858
  	skb_copy_to_linear_data(skb, pkt->data, ihl);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
859
860
  	ip_hdr(skb)->protocol = 0;			/* Flag to the kernel this is a route add */
  	msg = (struct igmpmsg *)skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
  	msg->im_vif = vifi;
adf30907d   Eric Dumazet   net: skb->dst acc...
862
  	skb_dst_set(skb, dst_clone(skb_dst(pkt)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
863
864
865
866
  
  	/*
  	 *	Add our header
  	 */
c354e1246   Jianjun Kong   net: clean up net...
867
  	igmp=(struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
870
  	igmp->type	=
  	msg->im_msgtype = assert;
  	igmp->code 	=	0;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
871
  	ip_hdr(skb)->tot_len = htons(skb->len);			/* Fix the length */
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
872
  	skb->transport_header = skb->network_header;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
873
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
875
  	if (mrt->mroute_sk == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
878
879
880
881
882
  		kfree_skb(skb);
  		return -EINVAL;
  	}
  
  	/*
  	 *	Deliver to mrouted
  	 */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
883
  	ret = sock_queue_rcv_skb(mrt->mroute_sk, skb);
70a269e6c   Benjamin Thery   netns: ipmr: allo...
884
  	if (ret < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
888
889
890
891
892
893
894
895
896
  		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...
897

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
  static int
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
899
  ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900
  {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
901
  	bool found = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
  	int err;
  	struct mfc_cache *c;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
904
  	const struct iphdr *iph = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
906
  
  	spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
907
  	list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
908
  		if (c->mfc_mcastgrp == iph->daddr &&
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
909
910
  		    c->mfc_origin == iph->saddr) {
  			found = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
  			break;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
912
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913
  	}
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
914
  	if (!found) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
917
  		/*
  		 *	Create a new entry if allowable
  		 */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
918
  		if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
d658f8a0e   Patrick McHardy   ipv4: ipmr: remov...
919
  		    (c = ipmr_cache_alloc_unres()) == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
921
922
923
924
925
926
927
928
  			spin_unlock_bh(&mfc_unres_lock);
  
  			kfree_skb(skb);
  			return -ENOBUFS;
  		}
  
  		/*
  		 *	Fill in the new cache entry
  		 */
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
929
930
931
  		c->mfc_parent	= -1;
  		c->mfc_origin	= iph->saddr;
  		c->mfc_mcastgrp	= iph->daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
933
934
935
  
  		/*
  		 *	Reflect first query at mrouted.
  		 */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
936
  		err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
937
  		if (err < 0) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
938
  			/* If the report failed throw the cache entry
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939
940
941
  			   out - Brad Parker
  			 */
  			spin_unlock_bh(&mfc_unres_lock);
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
942
  			ipmr_cache_free(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
944
945
  			kfree_skb(skb);
  			return err;
  		}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
946
947
  		atomic_inc(&mrt->cache_resolve_queue_len);
  		list_add(&c->list, &mrt->mfc_unres_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
948

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

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

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
978
  	list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
980
981
  		if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
  		    c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
  			write_lock_bh(&mrt_lock);
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
982
  			list_del(&c->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
983
  			write_unlock_bh(&mrt_lock);
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
1028
1029
  	if (!mrtsock)
  		c->mfc_flags |= MFC_STATIC;
  
  	write_lock_bh(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1030
  	list_add(&c->list, &mrt->mfc_cache_array[line]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
1034
1035
1036
  	write_unlock_bh(&mrt_lock);
  
  	/*
  	 *	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...
1037
  	found = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038
  	spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1039
  	list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
1040
  		if (uc->mfc_origin == c->mfc_origin &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1041
  		    uc->mfc_mcastgrp == c->mfc_mcastgrp) {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1042
  			list_del(&uc->list);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1043
  			atomic_dec(&mrt->cache_resolve_queue_len);
b0ebb739a   Patrick McHardy   ipv4: ipmr: fix i...
1044
  			found = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
1046
1047
  			break;
  		}
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1048
1049
  	if (list_empty(&mrt->mfc_unres_queue))
  		del_timer(&mrt->ipmr_expire_timer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1050
  	spin_unlock_bh(&mfc_unres_lock);
b0ebb739a   Patrick McHardy   ipv4: ipmr: fix i...
1051
  	if (found) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1052
  		ipmr_cache_resolve(net, mrt, uc, c);
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
1053
  		ipmr_cache_free(uc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
1055
1056
1057
1058
1059
1060
  	}
  	return 0;
  }
  
  /*
   *	Close the multicast socket, and clear the vif tables etc
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1061

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1068
1069
1070
  	/*
  	 *	Shut down all active vif entries
  	 */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1071
1072
1073
  	for (i = 0; i < mrt->maxvif; i++) {
  		if (!(mrt->vif_table[i].flags&VIFF_STATIC))
  			vif_delete(mrt, i, 0, &list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1074
  	}
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
1075
  	unregister_netdevice_many(&list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
1078
1079
  
  	/*
  	 *	Wipe the cache
  	 */
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1080
  	for (i = 0; i < MFC_LINES; i++) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1081
  		list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1082
  			if (c->mfc_flags&MFC_STATIC)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
  				continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084
  			write_lock_bh(&mrt_lock);
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1085
  			list_del(&c->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
  			write_unlock_bh(&mrt_lock);
5c0a66f5f   Benjamin Thery   netns: ipmr: stor...
1087
  			ipmr_cache_free(c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
1089
  		}
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1090
  	if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1091
  		spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1092
  		list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
1093
  			list_del(&c->list);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1094
  			ipmr_destroy_unres(mrt, c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
1097
1098
1099
1100
1101
  		}
  		spin_unlock_bh(&mfc_unres_lock);
  	}
  }
  
  static void mrtsock_destruct(struct sock *sk)
  {
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1102
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1103
  	struct mr_table *mrt;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1104

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1105
  	rtnl_lock();
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1106
1107
1108
  	ipmr_for_each_table(mrt, net) {
  		if (sk == mrt->mroute_sk) {
  			IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1109

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1110
1111
1112
  			write_lock_bh(&mrt_lock);
  			mrt->mroute_sk = NULL;
  			write_unlock_bh(&mrt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1113

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1114
1115
  			mroute_clean_tables(mrt);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
  	}
  	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...
1126

b7058842c   David S. Miller   net: Make setsock...
1127
  int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1128
1129
1130
1131
  {
  	int ret;
  	struct vifctl vif;
  	struct mfcctl mfc;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1132
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1133
1134
1135
1136
1137
  	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...
1138

132adf546   Stephen Hemminger   [IPV4]: cleanup
1139
  	if (optname != MRT_INIT) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1140
  		if (sk != mrt->mroute_sk && !capable(CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1141
1142
  			return -EACCES;
  	}
132adf546   Stephen Hemminger   [IPV4]: cleanup
1143
1144
1145
  	switch (optname) {
  	case MRT_INIT:
  		if (sk->sk_type != SOCK_RAW ||
c720c7e83   Eric Dumazet   inet: rename some...
1146
  		    inet_sk(sk)->inet_num != IPPROTO_IGMP)
132adf546   Stephen Hemminger   [IPV4]: cleanup
1147
  			return -EOPNOTSUPP;
c354e1246   Jianjun Kong   net: clean up net...
1148
  		if (optlen != sizeof(int))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1149
  			return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1150

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

c354e1246   Jianjun Kong   net: clean up net...
1266
  int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1267
1268
1269
  {
  	int olr;
  	int val;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1270
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1271
1272
1273
1274
1275
  	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
1276

c354e1246   Jianjun Kong   net: clean up net...
1277
  	if (optname != MRT_VERSION &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
  #ifdef CONFIG_IP_PIMSM
  	   optname!=MRT_PIM &&
  #endif
  	   optname!=MRT_ASSERT)
  		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...
1290

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1310
1311
1312
1313
1314
1315
  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...
1316
  	struct net *net = sock_net(sk);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1317
1318
1319
1320
1321
  	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...
1322

132adf546   Stephen Hemminger   [IPV4]: cleanup
1323
1324
  	switch (cmd) {
  	case SIOCGETVIFCNT:
c354e1246   Jianjun Kong   net: clean up net...
1325
  		if (copy_from_user(&vr, arg, sizeof(vr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1326
  			return -EFAULT;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1327
  		if (vr.vifi >= mrt->maxvif)
132adf546   Stephen Hemminger   [IPV4]: cleanup
1328
1329
  			return -EINVAL;
  		read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1330
1331
  		vif = &mrt->vif_table[vr.vifi];
  		if (VIF_EXISTS(mrt, vr.vifi)) {
c354e1246   Jianjun Kong   net: clean up net...
1332
1333
1334
1335
  			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
1336
  			read_unlock(&mrt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1337

c354e1246   Jianjun Kong   net: clean up net...
1338
  			if (copy_to_user(arg, &vr, sizeof(vr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1339
1340
1341
1342
1343
1344
  				return -EFAULT;
  			return 0;
  		}
  		read_unlock(&mrt_lock);
  		return -EADDRNOTAVAIL;
  	case SIOCGETSGCNT:
c354e1246   Jianjun Kong   net: clean up net...
1345
  		if (copy_from_user(&sr, arg, sizeof(sr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1346
1347
1348
  			return -EFAULT;
  
  		read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1349
  		c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
132adf546   Stephen Hemminger   [IPV4]: cleanup
1350
1351
1352
1353
  		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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1354
  			read_unlock(&mrt_lock);
132adf546   Stephen Hemminger   [IPV4]: cleanup
1355

c354e1246   Jianjun Kong   net: clean up net...
1356
  			if (copy_to_user(arg, &sr, sizeof(sr)))
132adf546   Stephen Hemminger   [IPV4]: cleanup
1357
1358
1359
1360
1361
1362
1363
  				return -EFAULT;
  			return 0;
  		}
  		read_unlock(&mrt_lock);
  		return -EADDRNOTAVAIL;
  	default:
  		return -ENOIOCTLCMD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1364
1365
1366
1367
1368
1369
  	}
  }
  
  
  static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
  {
e9dc86534   Eric W. Biederman   [NET]: Make devic...
1370
  	struct net_device *dev = ptr;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1371
  	struct net *net = dev_net(dev);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1372
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1373
1374
  	struct vif_device *v;
  	int ct;
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
1375
  	LIST_HEAD(list);
e9dc86534   Eric W. Biederman   [NET]: Make devic...
1376

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1377
1378
  	if (event != NETDEV_UNREGISTER)
  		return NOTIFY_DONE;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1379
1380
1381
1382
1383
1384
1385
  
  	ipmr_for_each_table(mrt, net) {
  		v = &mrt->vif_table[0];
  		for (ct = 0; ct < mrt->maxvif; ct++, v++) {
  			if (v->dev == dev)
  				vif_delete(mrt, ct, 1, &list);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1386
  	}
d17fa6fa8   Eric Dumazet   ipmr: Optimize mu...
1387
  	unregister_netdevice_many(&list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1388
1389
  	return NOTIFY_DONE;
  }
c354e1246   Jianjun Kong   net: clean up net...
1390
  static struct notifier_block ip_mr_notifier = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1391
1392
1393
1394
1395
1396
1397
1398
  	.notifier_call = ipmr_device_event,
  };
  
  /*
   * 	Encapsulate a packet by attaching a valid IPIP header to it.
   *	This avoids tunnel drivers and other mess and gives us the speed so
   *	important for multicast video.
   */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1399

114c7844f   Al Viro   [IPV4]: mroute an...
1400
  static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1401
  {
8856dfa3e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1402
  	struct iphdr *iph;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1403
  	struct iphdr *old_iph = ip_hdr(skb);
8856dfa3e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1404
1405
  
  	skb_push(skb, sizeof(struct iphdr));
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
1406
  	skb->transport_header = skb->network_header;
8856dfa3e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1407
  	skb_reset_network_header(skb);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1408
  	iph = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1409
1410
  
  	iph->version	= 	4;
e023dd643   Arnaldo Carvalho de Melo   [IPMR]: Fix bug i...
1411
1412
  	iph->tos	=	old_iph->tos;
  	iph->ttl	=	old_iph->ttl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1413
1414
1415
1416
1417
1418
  	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...
1419
  	ip_select_ident(iph, skb_dst(skb), NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1420
  	ip_send_check(iph);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1421
1422
1423
1424
1425
1426
1427
  	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
  	nf_reset(skb);
  }
  
  static inline int ipmr_forward_finish(struct sk_buff *skb)
  {
  	struct ip_options * opt	= &(IPCB(skb)->opt);
adf30907d   Eric Dumazet   net: skb->dst acc...
1428
  	IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
  
  	if (unlikely(opt->optlen))
  		ip_forward_options(skb);
  
  	return dst_output(skb);
  }
  
  /*
   *	Processing handlers for ipmr_forward
   */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1439
1440
  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
1441
  {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1442
  	const struct iphdr *iph = ip_hdr(skb);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1443
  	struct vif_device *vif = &mrt->vif_table[vifi];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
  	struct net_device *dev;
  	struct rtable *rt;
  	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...
1454
  		vif->bytes_out += skb->len;
cf3677ae1   Pavel Emelyanov   ipmr: Use on-devi...
1455
1456
  		vif->dev->stats.tx_bytes += skb->len;
  		vif->dev->stats.tx_packets++;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1457
  		ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
69ebbf58f   Ilpo Järvinen   ipmr: use goto to...
1458
  		goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
  	}
  #endif
  
  	if (vif->flags&VIFF_TUNNEL) {
  		struct flowi fl = { .oif = vif->link,
  				    .nl_u = { .ip4_u =
  					      { .daddr = vif->remote,
  						.saddr = vif->local,
  						.tos = RT_TOS(iph->tos) } },
  				    .proto = IPPROTO_IPIP };
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1469
  		if (ip_route_output_key(net, &rt, &fl))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1470
1471
1472
1473
1474
1475
1476
1477
  			goto out_free;
  		encap = sizeof(struct iphdr);
  	} else {
  		struct flowi fl = { .oif = vif->link,
  				    .nl_u = { .ip4_u =
  					      { .daddr = iph->daddr,
  						.tos = RT_TOS(iph->tos) } },
  				    .proto = IPPROTO_IPIP };
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1478
  		if (ip_route_output_key(net, &rt, &fl))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1479
1480
  			goto out_free;
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
1481
  	dev = rt->dst.dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1482

d8d1f30b9   Changli Gao   net-next: remove ...
1483
  	if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1484
1485
1486
1487
  		/* Do not fragment multicasts. Alas, IPv4 does not
  		   allow to send ICMP, so that packets will disappear
  		   to blackhole.
  		 */
7c73a6faf   Pavel Emelyanov   mib: add net to I...
1488
  		IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1489
1490
1491
  		ip_rt_put(rt);
  		goto out_free;
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
1492
  	encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1493
1494
  
  	if (skb_cow(skb, encap)) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1495
  		ip_rt_put(rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1496
1497
1498
1499
  		goto out_free;
  	}
  
  	vif->pkt_out++;
c354e1246   Jianjun Kong   net: clean up net...
1500
  	vif->bytes_out += skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1501

adf30907d   Eric Dumazet   net: skb->dst acc...
1502
  	skb_dst_drop(skb);
d8d1f30b9   Changli Gao   net-next: remove ...
1503
  	skb_dst_set(skb, &rt->dst);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1504
  	ip_decrease_ttl(ip_hdr(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1505
1506
1507
1508
1509
1510
  
  	/* FIXME: forward and output firewalls used to be called here.
  	 * What do we do with netfilter? -- RR */
  	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...
1511
1512
  		vif->dev->stats.tx_packets++;
  		vif->dev->stats.tx_bytes += skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
  	}
  
  	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: ...
1528
  	NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1529
1530
1531
1532
1533
  		ipmr_forward_finish);
  	return;
  
  out_free:
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1534
  }
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1535
  static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1536
1537
  {
  	int ct;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1538
1539
1540
  
  	for (ct = mrt->maxvif-1; ct >= 0; ct--) {
  		if (mrt->vif_table[ct].dev == dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1541
1542
1543
1544
1545
1546
  			break;
  	}
  	return ct;
  }
  
  /* "local" means that we should preserve one skb (for local delivery) */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1547
1548
1549
  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
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
  {
  	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 ...
1561
  	if (mrt->vif_table[vif].dev != skb->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1562
  		int true_vifi;
511c3f92a   Eric Dumazet   net: skb->rtable ...
1563
  		if (skb_rtable(skb)->fl.iif == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
  			/* It is our own packet, looped back.
  			   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.
  			 */
  			goto dont_forward;
  		}
  
  		cache->mfc_un.res.wrong_if++;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1579
  		true_vifi = ipmr_find_vif(mrt, skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1580

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1581
  		if (true_vifi >= 0 && mrt->mroute_do_assert &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1582
1583
1584
1585
1586
  		    /* pimsm uses asserts, when switching from RPT to SPT,
  		       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
  		     */
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1587
  		    (mrt->mroute_do_pim ||
6f9374a93   Benjamin Thery   netns: ipmr: decl...
1588
  		     cache->mfc_un.res.ttls[true_vifi] < 255) &&
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1589
  		    time_after(jiffies,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1590
1591
  			       cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
  			cache->mfc_un.res.last_assert = jiffies;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1592
  			ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1593
1594
1595
  		}
  		goto dont_forward;
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1596
1597
  	mrt->vif_table[vif].pkt_in++;
  	mrt->vif_table[vif].bytes_in += skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1598
1599
1600
1601
1602
  
  	/*
  	 *	Forward the frame
  	 */
  	for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1603
  		if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1604
1605
1606
  			if (psend != -1) {
  				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
  				if (skb2)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1607
1608
  					ipmr_queue_xmit(net, mrt, skb2, cache,
  							psend);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1609
  			}
c354e1246   Jianjun Kong   net: clean up net...
1610
  			psend = ct;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1611
1612
1613
1614
1615
1616
  		}
  	}
  	if (psend != -1) {
  		if (local) {
  			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
  			if (skb2)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1617
  				ipmr_queue_xmit(net, mrt, skb2, cache, psend);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1618
  		} else {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1619
  			ipmr_queue_xmit(net, mrt, skb, cache, psend);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
  			return 0;
  		}
  	}
  
  dont_forward:
  	if (!local)
  		kfree_skb(skb);
  	return 0;
  }
  
  
  /*
   *	Multicast packets for forwarding arrive here
   */
  
  int ip_mr_input(struct sk_buff *skb)
  {
  	struct mfc_cache *cache;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1638
  	struct net *net = dev_net(skb->dev);
511c3f92a   Eric Dumazet   net: skb->rtable ...
1639
  	int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1640
1641
  	struct mr_table *mrt;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1642
1643
1644
1645
1646
1647
  
  	/* Packet is looped back after forward, it should not be
  	   forwarded second time, but still can be delivered locally.
  	 */
  	if (IPCB(skb)->flags&IPSKB_FORWARDED)
  		goto dont_forward;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1648
  	err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt);
e40dbc51f   Ben Greear   ipmr: Don't leak ...
1649
1650
  	if (err < 0) {
  		kfree_skb(skb);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1651
  		return err;
e40dbc51f   Ben Greear   ipmr: Don't leak ...
1652
  	}
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1653

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1654
1655
1656
1657
  	if (!local) {
  		    if (IPCB(skb)->opt.router_alert) {
  			    if (ip_call_ra_chain(skb))
  				    return 0;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1658
  		    } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP){
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1659
1660
1661
1662
1663
1664
1665
  			    /* 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.
  			     */
  			    read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1666
  			    if (mrt->mroute_sk) {
2715bcf9e   Patrick McHardy   [NETFILTER]: Drop...
1667
  				    nf_reset(skb);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1668
  				    raw_rcv(mrt->mroute_sk, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1669
1670
1671
1672
1673
1674
1675
1676
  				    read_unlock(&mrt_lock);
  				    return 0;
  			    }
  			    read_unlock(&mrt_lock);
  		    }
  	}
  
  	read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1677
  	cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1678
1679
1680
1681
  
  	/*
  	 *	No usable cache entry
  	 */
c354e1246   Jianjun Kong   net: clean up net...
1682
  	if (cache == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
  		int vif;
  
  		if (local) {
  			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
  			ip_local_deliver(skb);
  			if (skb2 == NULL) {
  				read_unlock(&mrt_lock);
  				return -ENOBUFS;
  			}
  			skb = skb2;
  		}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1694
  		vif = ipmr_find_vif(mrt, skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1695
  		if (vif >= 0) {
0eae88f31   Eric Dumazet   net: Fix various ...
1696
  			int err2 = ipmr_cache_unresolved(mrt, vif, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1697
  			read_unlock(&mrt_lock);
0eae88f31   Eric Dumazet   net: Fix various ...
1698
  			return err2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1699
1700
1701
1702
1703
  		}
  		read_unlock(&mrt_lock);
  		kfree_skb(skb);
  		return -ENODEV;
  	}
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1704
  	ip_mr_forward(net, mrt, skb, cache, local);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
  
  	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...
1719
  #ifdef CONFIG_IP_PIMSM
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1720
1721
  static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
  		     unsigned int pimlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1722
  {
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1723
1724
  	struct net_device *reg_dev = NULL;
  	struct iphdr *encap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1725

b1879204d   Ilpo Järvinen   ipmr: merge commo...
1726
  	encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1727
1728
1729
1730
1731
1732
  	/*
  	   Check that:
  	   a. packet is really destinted to a multicast group
  	   b. packet is not a NULL-REGISTER
  	   c. packet is not truncated
  	 */
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1733
  	if (!ipv4_is_multicast(encap->daddr) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1734
  	    encap->tot_len == 0 ||
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1735
1736
  	    ntohs(encap->tot_len) + pimlen > skb->len)
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1737
1738
  
  	read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1739
1740
  	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
1741
1742
1743
  	if (reg_dev)
  		dev_hold(reg_dev);
  	read_unlock(&mrt_lock);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1744
  	if (reg_dev == NULL)
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1745
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1746

b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
1747
  	skb->mac_header = skb->network_header;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1748
  	skb_pull(skb, (u8*)encap - skb->data);
31c7711b5   Arnaldo Carvalho de Melo   [SK_BUFF]: Some m...
1749
  	skb_reset_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1750
1751
1752
  	skb->protocol = htons(ETH_P_IP);
  	skb->ip_summed = 0;
  	skb->pkt_type = PACKET_HOST;
d19d56ddc   Eric Dumazet   net: Introduce sk...
1753
1754
  
  	skb_tunnel_rx(skb, reg_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1755
1756
  	netif_rx(skb);
  	dev_put(reg_dev);
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1757

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1758
  	return 0;
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
  }
  #endif
  
  #ifdef CONFIG_IP_PIMSM_V1
  /*
   * Handle IGMP messages of PIMv1
   */
  
  int pim_rcv_v1(struct sk_buff * skb)
  {
  	struct igmphdr *pim;
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1770
  	struct net *net = dev_net(skb->dev);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1771
  	struct mr_table *mrt;
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1772
1773
1774
1775
1776
  
  	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
  		goto drop;
  
  	pim = igmp_hdr(skb);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1777
1778
  	if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
  		goto drop;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1779
  	if (!mrt->mroute_do_pim ||
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1780
1781
  	    pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
  		goto drop;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1782
  	if (__pim_rcv(mrt, skb, sizeof(*pim))) {
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1783
1784
1785
  drop:
  		kfree_skb(skb);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1786
1787
1788
1789
1790
1791
1792
1793
  	return 0;
  }
  #endif
  
  #ifdef CONFIG_IP_PIMSM_V2
  static int pim_rcv(struct sk_buff * skb)
  {
  	struct pimreghdr *pim;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1794
1795
  	struct net *net = dev_net(skb->dev);
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1796

b1879204d   Ilpo Järvinen   ipmr: merge commo...
1797
  	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1798
  		goto drop;
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1799
  	pim = (struct pimreghdr *)skb_transport_header(skb);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1800
  	if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1801
  	    (pim->flags&PIM_NULL_REGISTER) ||
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1802
  	    (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
d3bc23e7e   Al Viro   [NET]: Annotate c...
1803
  	     csum_fold(skb_checksum(skb, 0, skb->len, 0))))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1804
  		goto drop;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1805
1806
1807
1808
  	if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
  		goto drop;
  
  	if (__pim_rcv(mrt, skb, sizeof(*pim))) {
b1879204d   Ilpo Järvinen   ipmr: merge commo...
1809
1810
1811
  drop:
  		kfree_skb(skb);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1812
1813
1814
  	return 0;
  }
  #endif
cb6a4e461   Patrick McHardy   net: ipmr: add su...
1815
1816
  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
1817
1818
1819
  {
  	int ct;
  	struct rtnexthop *nhp;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1820
  	u8 *b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1821
  	struct rtattr *mp_head;
7438189ba   Nicolas Dichtel   net: ipmr/ip6mr: ...
1822
  	/* If cache is unresolved, don't try to parse IIF and OIF */
ed0f160ad   Dan Carpenter   ipmr: off by one ...
1823
  	if (c->mfc_parent >= MAXVIFS)
7438189ba   Nicolas Dichtel   net: ipmr/ip6mr: ...
1824
  		return -ENOENT;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1825
1826
  	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
1827

c354e1246   Jianjun Kong   net: clean up net...
1828
  	mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1829
1830
  
  	for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1831
  		if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1832
1833
  			if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
  				goto rtattr_failure;
c354e1246   Jianjun Kong   net: clean up net...
1834
  			nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1835
1836
  			nhp->rtnh_flags = 0;
  			nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1837
  			nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1838
1839
1840
1841
  			nhp->rtnh_len = sizeof(*nhp);
  		}
  	}
  	mp_head->rta_type = RTA_MULTIPATH;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1842
  	mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1843
1844
1845
1846
  	rtm->rtm_type = RTN_MULTICAST;
  	return 1;
  
  rtattr_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
1847
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1848
1849
  	return -EMSGSIZE;
  }
4feb88e5c   Benjamin Thery   netns: ipmr: enab...
1850
1851
  int ipmr_get_route(struct net *net,
  		   struct sk_buff *skb, struct rtmsg *rtm, int nowait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1852
1853
  {
  	int err;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1854
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1855
  	struct mfc_cache *cache;
511c3f92a   Eric Dumazet   net: skb->rtable ...
1856
  	struct rtable *rt = skb_rtable(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1857

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1858
1859
1860
  	mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1861
  	read_lock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1862
  	cache = ipmr_cache_find(mrt, rt->rt_src, rt->rt_dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1863

c354e1246   Jianjun Kong   net: clean up net...
1864
  	if (cache == NULL) {
722874909   Alexey Kuznetsov   [IPV4] ipmr: ip m...
1865
  		struct sk_buff *skb2;
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1866
  		struct iphdr *iph;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1867
1868
1869
1870
1871
1872
1873
1874
1875
  		struct net_device *dev;
  		int vif;
  
  		if (nowait) {
  			read_unlock(&mrt_lock);
  			return -EAGAIN;
  		}
  
  		dev = skb->dev;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1876
  		if (dev == NULL || (vif = ipmr_find_vif(mrt, dev)) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1877
1878
1879
  			read_unlock(&mrt_lock);
  			return -ENODEV;
  		}
722874909   Alexey Kuznetsov   [IPV4] ipmr: ip m...
1880
1881
1882
1883
1884
  		skb2 = skb_clone(skb, GFP_ATOMIC);
  		if (!skb2) {
  			read_unlock(&mrt_lock);
  			return -ENOMEM;
  		}
e2d1bca7e   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
1885
1886
  		skb_push(skb2, sizeof(struct iphdr));
  		skb_reset_network_header(skb2);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
1887
1888
1889
1890
1891
  		iph = ip_hdr(skb2);
  		iph->ihl = sizeof(struct iphdr) >> 2;
  		iph->saddr = rt->rt_src;
  		iph->daddr = rt->rt_dst;
  		iph->version = 0;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1892
  		err = ipmr_cache_unresolved(mrt, vif, skb2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1893
1894
1895
1896
1897
1898
  		read_unlock(&mrt_lock);
  		return err;
  	}
  
  	if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
  		cache->mfc_flags |= MFC_NOTIFY;
cb6a4e461   Patrick McHardy   net: ipmr: add su...
1899
  	err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1900
1901
1902
  	read_unlock(&mrt_lock);
  	return err;
  }
cb6a4e461   Patrick McHardy   net: ipmr: add su...
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
  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];
  
  	read_lock(&mrt_lock);
  	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++) {
  			list_for_each_entry(mfc, &mrt->mfc_cache_array[h], list) {
  				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:
  	read_unlock(&mrt_lock);
  
  	cb->args[2] = e;
  	cb->args[1] = h;
  	cb->args[0] = t;
  
  	return skb->len;
  }
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1984
  #ifdef CONFIG_PROC_FS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1985
1986
1987
1988
  /*
   *	The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
   */
  struct ipmr_vif_iter {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
1989
  	struct seq_net_private p;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1990
  	struct mr_table *mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1991
1992
  	int ct;
  };
f6bb45147   Benjamin Thery   netns: ipmr: decl...
1993
1994
  static struct vif_device *ipmr_vif_seq_idx(struct net *net,
  					   struct ipmr_vif_iter *iter,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1995
1996
  					   loff_t pos)
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
1997
  	struct mr_table *mrt = iter->mrt;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
1998
1999
2000
  
  	for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) {
  		if (!VIF_EXISTS(mrt, iter->ct))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2001
  			continue;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2002
  		if (pos-- == 0)
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2003
  			return &mrt->vif_table[iter->ct];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2004
2005
2006
2007
2008
  	}
  	return NULL;
  }
  
  static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
ba93ef746   Stephen Hemminger   [IPV4]: ipmr spar...
2009
  	__acquires(mrt_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2010
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2011
  	struct ipmr_vif_iter *iter = seq->private;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2012
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2013
2014
2015
2016
2017
2018
2019
  	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...
2020

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2021
  	read_lock(&mrt_lock);
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2022
  	return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2023
2024
2025
2026
2027
2028
  		: 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...
2029
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2030
  	struct mr_table *mrt = iter->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2031
2032
2033
  
  	++*pos;
  	if (v == SEQ_START_TOKEN)
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2034
  		return ipmr_vif_seq_idx(net, iter, 0);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2035

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2036
2037
  	while (++iter->ct < mrt->maxvif) {
  		if (!VIF_EXISTS(mrt, iter->ct))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2038
  			continue;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2039
  		return &mrt->vif_table[iter->ct];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2040
2041
2042
2043
2044
  	}
  	return NULL;
  }
  
  static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
ba93ef746   Stephen Hemminger   [IPV4]: ipmr spar...
2045
  	__releases(mrt_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2046
2047
2048
2049
2050
2051
  {
  	read_unlock(&mrt_lock);
  }
  
  static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2052
2053
  	struct ipmr_vif_iter *iter = seq->private;
  	struct mr_table *mrt = iter->mrt;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2054

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2055
  	if (v == SEQ_START_TOKEN) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2056
  		seq_puts(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2057
2058
2059
2060
2061
2062
2063
2064
2065
  			 "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 ...
2066
  			   vif - mrt->vif_table,
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2067
  			   name, vif->bytes_in, vif->pkt_in,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2068
2069
2070
2071
2072
  			   vif->bytes_out, vif->pkt_out,
  			   vif->flags, vif->local, vif->remote);
  	}
  	return 0;
  }
f690808e1   Stephen Hemminger   [NET]: make seq_o...
2073
  static const struct seq_operations ipmr_vif_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2074
2075
2076
2077
2078
2079
2080
2081
  	.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...
2082
2083
  	return seq_open_net(inode, file, &ipmr_vif_seq_ops,
  			    sizeof(struct ipmr_vif_iter));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2084
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2085
  static const struct file_operations ipmr_vif_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2086
2087
2088
2089
  	.owner	 = THIS_MODULE,
  	.open    = ipmr_vif_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2090
  	.release = seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2091
2092
2093
  };
  
  struct ipmr_mfc_iter {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2094
  	struct seq_net_private p;
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2095
  	struct mr_table *mrt;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2096
  	struct list_head *cache;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2097
2098
  	int ct;
  };
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2099
2100
  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
2101
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2102
  	struct mr_table *mrt = it->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2103
  	struct mfc_cache *mfc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2104
  	read_lock(&mrt_lock);
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2105
  	for (it->ct = 0; it->ct < MFC_LINES; it->ct++) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2106
  		it->cache = &mrt->mfc_cache_array[it->ct];
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2107
  		list_for_each_entry(mfc, it->cache, list)
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2108
  			if (pos-- == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2109
  				return mfc;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2110
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2111
  	read_unlock(&mrt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2112
  	spin_lock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2113
  	it->cache = &mrt->mfc_unres_queue;
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2114
  	list_for_each_entry(mfc, it->cache, list)
e258beb22   Patrick McHardy   ipv4: ipmr: move ...
2115
  		if (pos-- == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
  			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...
2127
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2128
  	struct mr_table *mrt;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2129

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2130
2131
2132
  	mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
  	if (mrt == NULL)
  		return ERR_PTR(-ENOENT);
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2133

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2134
  	it->mrt = mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2135
2136
  	it->cache = NULL;
  	it->ct = 0;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2137
  	return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2138
2139
2140
2141
2142
2143
2144
  		: 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...
2145
  	struct net *net = seq_file_net(seq);
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2146
  	struct mr_table *mrt = it->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2147
2148
2149
2150
  
  	++*pos;
  
  	if (v == SEQ_START_TOKEN)
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2151
  		return ipmr_mfc_seq_idx(net, seq->private, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2152

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

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2156
  	if (it->cache == &mrt->mfc_unres_queue)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2157
  		goto end_of_list;
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2158
  	BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2159
2160
  
  	while (++it->ct < MFC_LINES) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2161
  		it->cache = &mrt->mfc_cache_array[it->ct];
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2162
2163
2164
  		if (list_empty(it->cache))
  			continue;
  		return list_first_entry(it->cache, struct mfc_cache, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2165
2166
2167
2168
  	}
  
  	/* exhausted cache_array, show unresolved */
  	read_unlock(&mrt_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2169
  	it->cache = &mrt->mfc_unres_queue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2170
  	it->ct = 0;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2171

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2172
  	spin_lock_bh(&mfc_unres_lock);
862465f2e   Patrick McHardy   ipv4: ipmr: conve...
2173
2174
  	if (!list_empty(it->cache))
  		return list_first_entry(it->cache, struct mfc_cache, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
  
   end_of_list:
  	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...
2186
  	struct mr_table *mrt = it->mrt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2187

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2188
  	if (it->cache == &mrt->mfc_unres_queue)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2189
  		spin_unlock_bh(&mfc_unres_lock);
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2190
  	else if (it->cache == &mrt->mfc_cache_array[it->ct])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2191
2192
2193
2194
2195
2196
2197
2198
  		read_unlock(&mrt_lock);
  }
  
  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...
2199
  		seq_puts(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2200
2201
2202
2203
2204
  		 "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...
2205
  		const struct mr_table *mrt = it->mrt;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2206

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

0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2212
  		if (it->cache != &mrt->mfc_unres_queue) {
1ea472e2d   Benjamin Thery   net: fix /proc/ne...
2213
2214
2215
2216
  			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
2217
2218
  			for (n = mfc->mfc_un.res.minvif;
  			     n < mfc->mfc_un.res.maxvif; n++ ) {
0c12295a7   Patrick McHardy   ipv4: ipmr: move ...
2219
  				if (VIF_EXISTS(mrt, n) &&
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2220
2221
  				    mfc->mfc_un.res.ttls[n] < 255)
  					seq_printf(seq,
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2222
  					   " %2d:%-3d",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2223
2224
  					   n, mfc->mfc_un.res.ttls[n]);
  			}
1ea472e2d   Benjamin Thery   net: fix /proc/ne...
2225
2226
2227
2228
2229
  		} 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
2230
2231
2232
2233
2234
2235
  		}
  		seq_putc(seq, '
  ');
  	}
  	return 0;
  }
f690808e1   Stephen Hemminger   [NET]: make seq_o...
2236
  static const struct seq_operations ipmr_mfc_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2237
2238
2239
2240
2241
2242
2243
2244
  	.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...
2245
2246
  	return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
  			    sizeof(struct ipmr_mfc_iter));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2247
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2248
  static const struct file_operations ipmr_mfc_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2249
2250
2251
2252
  	.owner	 = THIS_MODULE,
  	.open    = ipmr_mfc_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2253
  	.release = seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2254
  };
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2255
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2256
2257
  
  #ifdef CONFIG_IP_PIMSM_V2
32613090a   Alexey Dobriyan   net: constify str...
2258
  static const struct net_protocol pim_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2259
  	.handler	=	pim_rcv,
403dbb97f   Tom Goff   PIM-SM: namespace...
2260
  	.netns_ok	=	1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2261
2262
2263
2264
2265
2266
2267
  };
  #endif
  
  
  /*
   *	Setup for IP multicast routing
   */
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2268
2269
  static int __net_init ipmr_net_init(struct net *net)
  {
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2270
  	int err;
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2271

f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2272
2273
  	err = ipmr_rules_init(net);
  	if (err < 0)
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2274
  		goto fail;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2275
2276
2277
2278
2279
2280
2281
2282
  
  #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...
2283
  	return 0;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2284
2285
2286
2287
  #ifdef CONFIG_PROC_FS
  proc_cache_fail:
  	proc_net_remove(net, "ip_mr_vif");
  proc_vif_fail:
f0ad0860d   Patrick McHardy   ipv4: ipmr: suppo...
2288
  	ipmr_rules_exit(net);
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2289
  #endif
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2290
2291
2292
2293
2294
2295
  fail:
  	return err;
  }
  
  static void __net_exit ipmr_net_exit(struct net *net)
  {
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2296
2297
2298
2299
  #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...
2300
  	ipmr_rules_exit(net);
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2301
2302
2303
2304
2305
2306
  }
  
  static struct pernet_operations ipmr_net_ops = {
  	.init = ipmr_net_init,
  	.exit = ipmr_net_exit,
  };
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2307

03d2f897e   Wang Chen   ipv4: Do cleanup ...
2308
  int __init ip_mr_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2309
  {
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2310
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2311
2312
  	mrt_cachep = kmem_cache_create("ip_mrt_cache",
  				       sizeof(struct mfc_cache),
e5d679f33   Alexey Dobriyan   [NET]: Use SLAB_P...
2313
  				       0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
20c2df83d   Paul Mundt   mm: Remove slab d...
2314
  				       NULL);
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2315
2316
  	if (!mrt_cachep)
  		return -ENOMEM;
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2317
2318
2319
  	err = register_pernet_subsys(&ipmr_net_ops);
  	if (err)
  		goto reg_pernet_fail;
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2320
2321
2322
  	err = register_netdevice_notifier(&ip_mr_notifier);
  	if (err)
  		goto reg_notif_fail;
403dbb97f   Tom Goff   PIM-SM: namespace...
2323
2324
2325
2326
2327
2328
2329
2330
  #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
cb6a4e461   Patrick McHardy   net: ipmr: add su...
2331
  	rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute);
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2332
  	return 0;
f6bb45147   Benjamin Thery   netns: ipmr: decl...
2333

403dbb97f   Tom Goff   PIM-SM: namespace...
2334
2335
2336
2337
  #ifdef CONFIG_IP_PIMSM_V2
  add_proto_fail:
  	unregister_netdevice_notifier(&ip_mr_notifier);
  #endif
c3e388964   Benjamin Thery   net: fix ip_mr_in...
2338
  reg_notif_fail:
cf958ae37   Benjamin Thery   netns: ipmr: dyna...
2339
2340
  	unregister_pernet_subsys(&ipmr_net_ops);
  reg_pernet_fail:
c3e388964   Benjamin Thery   net: fix ip_mr_in...
2341
  	kmem_cache_destroy(mrt_cachep);
03d2f897e   Wang Chen   ipv4: Do cleanup ...
2342
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2343
  }