blob: 757f25eb9b4b2404ebebc6c4422b4ad1693ea227 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * IP multicast routing support for mrouted 3.6/3.8
3 *
Alan Cox113aa832008-10-13 19:01:08 -07004 * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Linux Consultancy and Custom Driver Development
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 * Fixes:
13 * Michael Chastain : Incorrect size of copying.
14 * Alan Cox : Added the cache manager code
15 * Alan Cox : Fixed the clone/copy bug and device race.
16 * Mike McLagan : Routing by source
17 * Malcolm Beattie : Buffer handling fixes.
18 * Alexey Kuznetsov : Double buffer free and other fixes.
19 * SVR Anand : Fixed several multicast bugs and problems.
20 * Alexey Kuznetsov : Status, optimisations and more.
21 * Brad Parker : Better behaviour on mrouted upcall
22 * overflow.
23 * Carlos Picoto : PIMv1 Support
24 * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header
Gilles Espinassef77f13e2010-03-29 15:41:47 +020025 * Relax this requirement to work with older peers.
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 *
27 */
28
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <asm/system.h>
30#include <asm/uaccess.h>
31#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080032#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/errno.h>
34#include <linux/timer.h>
35#include <linux/mm.h>
36#include <linux/kernel.h>
37#include <linux/fcntl.h>
38#include <linux/stat.h>
39#include <linux/socket.h>
40#include <linux/in.h>
41#include <linux/inet.h>
42#include <linux/netdevice.h>
43#include <linux/inetdevice.h>
44#include <linux/igmp.h>
45#include <linux/proc_fs.h>
46#include <linux/seq_file.h>
47#include <linux/mroute.h>
48#include <linux/init.h>
Kris Katterjohn46f25df2006-01-05 16:35:42 -080049#include <linux/if_ether.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090050#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020051#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <net/ip.h>
53#include <net/protocol.h>
54#include <linux/skbuff.h>
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020055#include <net/route.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#include <net/sock.h>
57#include <net/icmp.h>
58#include <net/udp.h>
59#include <net/raw.h>
60#include <linux/notifier.h>
61#include <linux/if_arp.h>
62#include <linux/netfilter_ipv4.h>
63#include <net/ipip.h>
64#include <net/checksum.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070065#include <net/netlink.h>
Patrick McHardyf0ad0862010-04-13 05:03:23 +000066#include <net/fib_rules.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
69#define CONFIG_IP_PIMSM 1
70#endif
71
Patrick McHardy0c122952010-04-13 05:03:22 +000072struct mr_table {
Patrick McHardyf0ad0862010-04-13 05:03:23 +000073 struct list_head list;
Patrick McHardy8de53df2010-04-15 13:29:28 +020074#ifdef CONFIG_NET_NS
75 struct net *net;
76#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +000077 u32 id;
Patrick McHardy0c122952010-04-13 05:03:22 +000078 struct sock *mroute_sk;
79 struct timer_list ipmr_expire_timer;
80 struct list_head mfc_unres_queue;
81 struct list_head mfc_cache_array[MFC_LINES];
82 struct vif_device vif_table[MAXVIFS];
83 int maxvif;
84 atomic_t cache_resolve_queue_len;
85 int mroute_do_assert;
86 int mroute_do_pim;
87#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
88 int mroute_reg_vif_num;
89#endif
90};
91
Patrick McHardyf0ad0862010-04-13 05:03:23 +000092struct ipmr_rule {
93 struct fib_rule common;
94};
95
96struct ipmr_result {
97 struct mr_table *mrt;
98};
99
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100/* Big lock, protecting vif table, mrt cache and mroute socket state.
101 Note that the changes are semaphored via rtnl_lock.
102 */
103
104static DEFINE_RWLOCK(mrt_lock);
105
106/*
107 * Multicast router control variables
108 */
109
Patrick McHardy0c122952010-04-13 05:03:22 +0000110#define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
112/* Special spinlock for queue of unresolved entries */
113static DEFINE_SPINLOCK(mfc_unres_lock);
114
115/* We return to original Alan's scheme. Hash table of resolved
116 entries is changed only in process context and protected
117 with weak lock mrt_lock. Queue of unresolved entries is protected
118 with strong spinlock mfc_unres_lock.
119
120 In this case data path is free of exclusive locks at all.
121 */
122
Christoph Lametere18b8902006-12-06 20:33:20 -0800123static struct kmem_cache *mrt_cachep __read_mostly;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000125static struct mr_table *ipmr_new_table(struct net *net, u32 id);
Patrick McHardy0c122952010-04-13 05:03:22 +0000126static int ip_mr_forward(struct net *net, struct mr_table *mrt,
127 struct sk_buff *skb, struct mfc_cache *cache,
128 int local);
129static int ipmr_cache_report(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000130 struct sk_buff *pkt, vifi_t vifi, int assert);
Patrick McHardycb6a4e42010-04-26 16:02:08 +0200131static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
132 struct mfc_cache *c, struct rtmsg *rtm);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000133static void ipmr_expire_process(unsigned long arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000135#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
136#define ipmr_for_each_table(mrt, net) \
137 list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list)
138
139static struct mr_table *ipmr_get_table(struct net *net, u32 id)
140{
141 struct mr_table *mrt;
142
143 ipmr_for_each_table(mrt, net) {
144 if (mrt->id == id)
145 return mrt;
146 }
147 return NULL;
148}
149
150static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
151 struct mr_table **mrt)
152{
153 struct ipmr_result res;
154 struct fib_lookup_arg arg = { .result = &res, };
155 int err;
156
157 err = fib_rules_lookup(net->ipv4.mr_rules_ops, flp, 0, &arg);
158 if (err < 0)
159 return err;
160 *mrt = res.mrt;
161 return 0;
162}
163
164static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp,
165 int flags, struct fib_lookup_arg *arg)
166{
167 struct ipmr_result *res = arg->result;
168 struct mr_table *mrt;
169
170 switch (rule->action) {
171 case FR_ACT_TO_TBL:
172 break;
173 case FR_ACT_UNREACHABLE:
174 return -ENETUNREACH;
175 case FR_ACT_PROHIBIT:
176 return -EACCES;
177 case FR_ACT_BLACKHOLE:
178 default:
179 return -EINVAL;
180 }
181
182 mrt = ipmr_get_table(rule->fr_net, rule->table);
183 if (mrt == NULL)
184 return -EAGAIN;
185 res->mrt = mrt;
186 return 0;
187}
188
189static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
190{
191 return 1;
192}
193
194static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = {
195 FRA_GENERIC_POLICY,
196};
197
198static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
199 struct fib_rule_hdr *frh, struct nlattr **tb)
200{
201 return 0;
202}
203
204static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
205 struct nlattr **tb)
206{
207 return 1;
208}
209
210static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
211 struct fib_rule_hdr *frh)
212{
213 frh->dst_len = 0;
214 frh->src_len = 0;
215 frh->tos = 0;
216 return 0;
217}
218
Patrick McHardy3d0c9c42010-04-26 16:02:04 +0200219static const struct fib_rules_ops __net_initdata ipmr_rules_ops_template = {
Patrick McHardy25239ce2010-04-26 16:02:05 +0200220 .family = RTNL_FAMILY_IPMR,
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000221 .rule_size = sizeof(struct ipmr_rule),
222 .addr_size = sizeof(u32),
223 .action = ipmr_rule_action,
224 .match = ipmr_rule_match,
225 .configure = ipmr_rule_configure,
226 .compare = ipmr_rule_compare,
227 .default_pref = fib_default_rule_pref,
228 .fill = ipmr_rule_fill,
229 .nlgroup = RTNLGRP_IPV4_RULE,
230 .policy = ipmr_rule_policy,
231 .owner = THIS_MODULE,
232};
233
234static int __net_init ipmr_rules_init(struct net *net)
235{
236 struct fib_rules_ops *ops;
237 struct mr_table *mrt;
238 int err;
239
240 ops = fib_rules_register(&ipmr_rules_ops_template, net);
241 if (IS_ERR(ops))
242 return PTR_ERR(ops);
243
244 INIT_LIST_HEAD(&net->ipv4.mr_tables);
245
246 mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
247 if (mrt == NULL) {
248 err = -ENOMEM;
249 goto err1;
250 }
251
252 err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0);
253 if (err < 0)
254 goto err2;
255
256 net->ipv4.mr_rules_ops = ops;
257 return 0;
258
259err2:
260 kfree(mrt);
261err1:
262 fib_rules_unregister(ops);
263 return err;
264}
265
266static void __net_exit ipmr_rules_exit(struct net *net)
267{
268 struct mr_table *mrt, *next;
269
Eric Dumazet035320d2010-06-06 23:48:40 +0000270 list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
271 list_del(&mrt->list);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000272 kfree(mrt);
Eric Dumazet035320d2010-06-06 23:48:40 +0000273 }
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000274 fib_rules_unregister(net->ipv4.mr_rules_ops);
275}
276#else
277#define ipmr_for_each_table(mrt, net) \
278 for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
279
280static struct mr_table *ipmr_get_table(struct net *net, u32 id)
281{
282 return net->ipv4.mrt;
283}
284
285static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
286 struct mr_table **mrt)
287{
288 *mrt = net->ipv4.mrt;
289 return 0;
290}
291
292static int __net_init ipmr_rules_init(struct net *net)
293{
294 net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
295 return net->ipv4.mrt ? 0 : -ENOMEM;
296}
297
298static void __net_exit ipmr_rules_exit(struct net *net)
299{
300 kfree(net->ipv4.mrt);
301}
302#endif
303
304static struct mr_table *ipmr_new_table(struct net *net, u32 id)
305{
306 struct mr_table *mrt;
307 unsigned int i;
308
309 mrt = ipmr_get_table(net, id);
310 if (mrt != NULL)
311 return mrt;
312
313 mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
314 if (mrt == NULL)
315 return NULL;
Patrick McHardy8de53df2010-04-15 13:29:28 +0200316 write_pnet(&mrt->net, net);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000317 mrt->id = id;
318
319 /* Forwarding cache */
320 for (i = 0; i < MFC_LINES; i++)
321 INIT_LIST_HEAD(&mrt->mfc_cache_array[i]);
322
323 INIT_LIST_HEAD(&mrt->mfc_unres_queue);
324
325 setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process,
326 (unsigned long)mrt);
327
328#ifdef CONFIG_IP_PIMSM
329 mrt->mroute_reg_vif_num = -1;
330#endif
331#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
332 list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables);
333#endif
334 return mrt;
335}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
338
Wang Chend6070322008-07-14 20:55:26 -0700339static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
340{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000341 struct net *net = dev_net(dev);
342
Wang Chend6070322008-07-14 20:55:26 -0700343 dev_close(dev);
344
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000345 dev = __dev_get_by_name(net, "tunl0");
Wang Chend6070322008-07-14 20:55:26 -0700346 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800347 const struct net_device_ops *ops = dev->netdev_ops;
Wang Chend6070322008-07-14 20:55:26 -0700348 struct ifreq ifr;
Wang Chend6070322008-07-14 20:55:26 -0700349 struct ip_tunnel_parm p;
350
351 memset(&p, 0, sizeof(p));
352 p.iph.daddr = v->vifc_rmt_addr.s_addr;
353 p.iph.saddr = v->vifc_lcl_addr.s_addr;
354 p.iph.version = 4;
355 p.iph.ihl = 5;
356 p.iph.protocol = IPPROTO_IPIP;
357 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
358 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
359
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800360 if (ops->ndo_do_ioctl) {
361 mm_segment_t oldfs = get_fs();
362
363 set_fs(KERNEL_DS);
364 ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL);
365 set_fs(oldfs);
366 }
Wang Chend6070322008-07-14 20:55:26 -0700367 }
368}
369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370static
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000371struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372{
373 struct net_device *dev;
374
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000375 dev = __dev_get_by_name(net, "tunl0");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800378 const struct net_device_ops *ops = dev->netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 int err;
380 struct ifreq ifr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 struct ip_tunnel_parm p;
382 struct in_device *in_dev;
383
384 memset(&p, 0, sizeof(p));
385 p.iph.daddr = v->vifc_rmt_addr.s_addr;
386 p.iph.saddr = v->vifc_lcl_addr.s_addr;
387 p.iph.version = 4;
388 p.iph.ihl = 5;
389 p.iph.protocol = IPPROTO_IPIP;
390 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
Stephen Hemmingerba93ef72008-01-21 17:28:59 -0800391 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800393 if (ops->ndo_do_ioctl) {
394 mm_segment_t oldfs = get_fs();
395
396 set_fs(KERNEL_DS);
397 err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
398 set_fs(oldfs);
399 } else
400 err = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
402 dev = NULL;
403
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000404 if (err == 0 &&
405 (dev = __dev_get_by_name(net, p.name)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 dev->flags |= IFF_MULTICAST;
407
Herbert Xue5ed6392005-10-03 14:35:55 -0700408 in_dev = __in_dev_get_rtnl(dev);
Herbert Xu71e27da2007-06-04 23:36:06 -0700409 if (in_dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 goto failure;
Herbert Xu71e27da2007-06-04 23:36:06 -0700411
412 ipv4_devconf_setall(in_dev);
413 IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
415 if (dev_open(dev))
416 goto failure;
Wang Chen7dc00c82008-07-14 20:56:34 -0700417 dev_hold(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
419 }
420 return dev;
421
422failure:
423 /* allow the register to be completed before unregistering. */
424 rtnl_unlock();
425 rtnl_lock();
426
427 unregister_netdevice(dev);
428 return NULL;
429}
430
431#ifdef CONFIG_IP_PIMSM
432
Stephen Hemminger6fef4c02009-08-31 19:50:41 +0000433static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000435 struct net *net = dev_net(dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000436 struct mr_table *mrt;
437 struct flowi fl = {
438 .oif = dev->ifindex,
439 .iif = skb->skb_iif,
440 .mark = skb->mark,
441 };
442 int err;
443
444 err = ipmr_fib_lookup(net, &fl, &mrt);
445 if (err < 0)
446 return err;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 read_lock(&mrt_lock);
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -0700449 dev->stats.tx_bytes += skb->len;
450 dev->stats.tx_packets++;
Patrick McHardy0c122952010-04-13 05:03:22 +0000451 ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 read_unlock(&mrt_lock);
453 kfree_skb(skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +0000454 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455}
456
Stephen Hemminger007c3832008-11-20 20:28:35 -0800457static const struct net_device_ops reg_vif_netdev_ops = {
458 .ndo_start_xmit = reg_vif_xmit,
459};
460
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461static void reg_vif_setup(struct net_device *dev)
462{
463 dev->type = ARPHRD_PIMREG;
Kris Katterjohn46f25df2006-01-05 16:35:42 -0800464 dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 dev->flags = IFF_NOARP;
Stephen Hemminger007c3832008-11-20 20:28:35 -0800466 dev->netdev_ops = &reg_vif_netdev_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 dev->destructor = free_netdev;
Tom Goff403dbb92009-06-14 03:16:13 -0700468 dev->features |= NETIF_F_NETNS_LOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469}
470
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000471static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
473 struct net_device *dev;
474 struct in_device *in_dev;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000475 char name[IFNAMSIZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000477 if (mrt->id == RT_TABLE_DEFAULT)
478 sprintf(name, "pimreg");
479 else
480 sprintf(name, "pimreg%u", mrt->id);
481
482 dev = alloc_netdev(0, name, reg_vif_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
484 if (dev == NULL)
485 return NULL;
486
Tom Goff403dbb92009-06-14 03:16:13 -0700487 dev_net_set(dev, net);
488
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 if (register_netdevice(dev)) {
490 free_netdev(dev);
491 return NULL;
492 }
493 dev->iflink = 0;
494
Herbert Xu71e27da2007-06-04 23:36:06 -0700495 rcu_read_lock();
496 if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
497 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 goto failure;
Herbert Xu71e27da2007-06-04 23:36:06 -0700499 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
Herbert Xu71e27da2007-06-04 23:36:06 -0700501 ipv4_devconf_setall(in_dev);
502 IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
503 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 if (dev_open(dev))
506 goto failure;
507
Wang Chen7dc00c82008-07-14 20:56:34 -0700508 dev_hold(dev);
509
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 return dev;
511
512failure:
513 /* allow the register to be completed before unregistering. */
514 rtnl_unlock();
515 rtnl_lock();
516
517 unregister_netdevice(dev);
518 return NULL;
519}
520#endif
521
522/*
523 * Delete a VIF entry
Wang Chen7dc00c82008-07-14 20:56:34 -0700524 * @notify: Set to 1, if the caller is a notifier_call
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900526
Patrick McHardy0c122952010-04-13 05:03:22 +0000527static int vif_delete(struct mr_table *mrt, int vifi, int notify,
Eric Dumazetd17fa6f2009-10-28 05:21:38 +0000528 struct list_head *head)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529{
530 struct vif_device *v;
531 struct net_device *dev;
532 struct in_device *in_dev;
533
Patrick McHardy0c122952010-04-13 05:03:22 +0000534 if (vifi < 0 || vifi >= mrt->maxvif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 return -EADDRNOTAVAIL;
536
Patrick McHardy0c122952010-04-13 05:03:22 +0000537 v = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 write_lock_bh(&mrt_lock);
540 dev = v->dev;
541 v->dev = NULL;
542
543 if (!dev) {
544 write_unlock_bh(&mrt_lock);
545 return -EADDRNOTAVAIL;
546 }
547
548#ifdef CONFIG_IP_PIMSM
Patrick McHardy0c122952010-04-13 05:03:22 +0000549 if (vifi == mrt->mroute_reg_vif_num)
550 mrt->mroute_reg_vif_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551#endif
552
Patrick McHardy0c122952010-04-13 05:03:22 +0000553 if (vifi+1 == mrt->maxvif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 int tmp;
555 for (tmp=vifi-1; tmp>=0; tmp--) {
Patrick McHardy0c122952010-04-13 05:03:22 +0000556 if (VIF_EXISTS(mrt, tmp))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 break;
558 }
Patrick McHardy0c122952010-04-13 05:03:22 +0000559 mrt->maxvif = tmp+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 }
561
562 write_unlock_bh(&mrt_lock);
563
564 dev_set_allmulti(dev, -1);
565
Herbert Xue5ed6392005-10-03 14:35:55 -0700566 if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
Herbert Xu42f811b2007-06-04 23:34:44 -0700567 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 ip_rt_multicast_event(in_dev);
569 }
570
Wang Chen7dc00c82008-07-14 20:56:34 -0700571 if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER) && !notify)
Eric Dumazetd17fa6f2009-10-28 05:21:38 +0000572 unregister_netdevice_queue(dev, head);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
574 dev_put(dev);
575 return 0;
576}
577
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000578static inline void ipmr_cache_free(struct mfc_cache *c)
579{
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000580 kmem_cache_free(mrt_cachep, c);
581}
582
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583/* Destroy an unresolved cache entry, killing queued skbs
584 and reporting error to netlink readers.
585 */
586
Patrick McHardy0c122952010-04-13 05:03:22 +0000587static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588{
Patrick McHardy8de53df2010-04-15 13:29:28 +0200589 struct net *net = read_pnet(&mrt->net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700591 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
Patrick McHardy0c122952010-04-13 05:03:22 +0000593 atomic_dec(&mrt->cache_resolve_queue_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Jianjun Kongc354e122008-11-03 00:28:02 -0800595 while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700596 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
598 nlh->nlmsg_type = NLMSG_ERROR;
599 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
600 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700601 e = NLMSG_DATA(nlh);
602 e->error = -ETIMEDOUT;
603 memset(&e->msg, 0, sizeof(e->msg));
Thomas Graf2942e902006-08-15 00:30:25 -0700604
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000605 rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 } else
607 kfree_skb(skb);
608 }
609
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000610 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
612
613
Patrick McHardye258beb2010-04-13 05:03:19 +0000614/* Timer process for the unresolved queue. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
Patrick McHardye258beb2010-04-13 05:03:19 +0000616static void ipmr_expire_process(unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617{
Patrick McHardy0c122952010-04-13 05:03:22 +0000618 struct mr_table *mrt = (struct mr_table *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 unsigned long now;
620 unsigned long expires;
Patrick McHardy862465f2010-04-13 05:03:21 +0000621 struct mfc_cache *c, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
623 if (!spin_trylock(&mfc_unres_lock)) {
Patrick McHardy0c122952010-04-13 05:03:22 +0000624 mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 return;
626 }
627
Patrick McHardy0c122952010-04-13 05:03:22 +0000628 if (list_empty(&mrt->mfc_unres_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 goto out;
630
631 now = jiffies;
632 expires = 10*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
Patrick McHardy0c122952010-04-13 05:03:22 +0000634 list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 if (time_after(c->mfc_un.unres.expires, now)) {
636 unsigned long interval = c->mfc_un.unres.expires - now;
637 if (interval < expires)
638 expires = interval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 continue;
640 }
641
Patrick McHardy862465f2010-04-13 05:03:21 +0000642 list_del(&c->list);
Patrick McHardy0c122952010-04-13 05:03:22 +0000643 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 }
645
Patrick McHardy0c122952010-04-13 05:03:22 +0000646 if (!list_empty(&mrt->mfc_unres_queue))
647 mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
649out:
650 spin_unlock(&mfc_unres_lock);
651}
652
653/* Fill oifs list. It is called under write locked mrt_lock. */
654
Patrick McHardy0c122952010-04-13 05:03:22 +0000655static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000656 unsigned char *ttls)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
658 int vifi;
659
660 cache->mfc_un.res.minvif = MAXVIFS;
661 cache->mfc_un.res.maxvif = 0;
662 memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
663
Patrick McHardy0c122952010-04-13 05:03:22 +0000664 for (vifi = 0; vifi < mrt->maxvif; vifi++) {
665 if (VIF_EXISTS(mrt, vifi) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +0000666 ttls[vifi] && ttls[vifi] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 cache->mfc_un.res.ttls[vifi] = ttls[vifi];
668 if (cache->mfc_un.res.minvif > vifi)
669 cache->mfc_un.res.minvif = vifi;
670 if (cache->mfc_un.res.maxvif <= vifi)
671 cache->mfc_un.res.maxvif = vifi + 1;
672 }
673 }
674}
675
Patrick McHardy0c122952010-04-13 05:03:22 +0000676static int vif_add(struct net *net, struct mr_table *mrt,
677 struct vifctl *vifc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678{
679 int vifi = vifc->vifc_vifi;
Patrick McHardy0c122952010-04-13 05:03:22 +0000680 struct vif_device *v = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 struct net_device *dev;
682 struct in_device *in_dev;
Wang Chend6070322008-07-14 20:55:26 -0700683 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
685 /* Is vif busy ? */
Patrick McHardy0c122952010-04-13 05:03:22 +0000686 if (VIF_EXISTS(mrt, vifi))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 return -EADDRINUSE;
688
689 switch (vifc->vifc_flags) {
690#ifdef CONFIG_IP_PIMSM
691 case VIFF_REGISTER:
692 /*
693 * Special Purpose VIF in PIM
694 * All the packets will be sent to the daemon
695 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000696 if (mrt->mroute_reg_vif_num >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 return -EADDRINUSE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000698 dev = ipmr_reg_vif(net, mrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 if (!dev)
700 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700701 err = dev_set_allmulti(dev, 1);
702 if (err) {
703 unregister_netdevice(dev);
Wang Chen7dc00c82008-07-14 20:56:34 -0700704 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700705 return err;
706 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 break;
708#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900709 case VIFF_TUNNEL:
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000710 dev = ipmr_new_tunnel(net, vifc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 if (!dev)
712 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700713 err = dev_set_allmulti(dev, 1);
714 if (err) {
715 ipmr_del_tunnel(dev, vifc);
Wang Chen7dc00c82008-07-14 20:56:34 -0700716 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700717 return err;
718 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 break;
Ilia Kee5e81f2009-09-16 05:53:07 +0000720
721 case VIFF_USE_IFINDEX:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 case 0:
Ilia Kee5e81f2009-09-16 05:53:07 +0000723 if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
724 dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex);
725 if (dev && dev->ip_ptr == NULL) {
726 dev_put(dev);
727 return -EADDRNOTAVAIL;
728 }
729 } else
730 dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
731
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 if (!dev)
733 return -EADDRNOTAVAIL;
Wang Chend6070322008-07-14 20:55:26 -0700734 err = dev_set_allmulti(dev, 1);
Wang Chen7dc00c82008-07-14 20:56:34 -0700735 if (err) {
736 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700737 return err;
Wang Chen7dc00c82008-07-14 20:56:34 -0700738 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 break;
740 default:
741 return -EINVAL;
742 }
743
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000744 if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) {
745 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 return -EADDRNOTAVAIL;
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000747 }
Herbert Xu42f811b2007-06-04 23:34:44 -0700748 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 ip_rt_multicast_event(in_dev);
750
751 /*
752 * Fill in the VIF structures
753 */
Jianjun Kongc354e122008-11-03 00:28:02 -0800754 v->rate_limit = vifc->vifc_rate_limit;
755 v->local = vifc->vifc_lcl_addr.s_addr;
756 v->remote = vifc->vifc_rmt_addr.s_addr;
757 v->flags = vifc->vifc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 if (!mrtsock)
759 v->flags |= VIFF_STATIC;
Jianjun Kongc354e122008-11-03 00:28:02 -0800760 v->threshold = vifc->vifc_threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 v->bytes_in = 0;
762 v->bytes_out = 0;
763 v->pkt_in = 0;
764 v->pkt_out = 0;
765 v->link = dev->ifindex;
766 if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))
767 v->link = dev->iflink;
768
769 /* And finish update writing critical data */
770 write_lock_bh(&mrt_lock);
Jianjun Kongc354e122008-11-03 00:28:02 -0800771 v->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772#ifdef CONFIG_IP_PIMSM
773 if (v->flags&VIFF_REGISTER)
Patrick McHardy0c122952010-04-13 05:03:22 +0000774 mrt->mroute_reg_vif_num = vifi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775#endif
Patrick McHardy0c122952010-04-13 05:03:22 +0000776 if (vifi+1 > mrt->maxvif)
777 mrt->maxvif = vifi+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 write_unlock_bh(&mrt_lock);
779 return 0;
780}
781
Patrick McHardy0c122952010-04-13 05:03:22 +0000782static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000783 __be32 origin,
784 __be32 mcastgrp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
Jianjun Kongc354e122008-11-03 00:28:02 -0800786 int line = MFC_HASH(mcastgrp, origin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 struct mfc_cache *c;
788
Patrick McHardy0c122952010-04-13 05:03:22 +0000789 list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
Patrick McHardy862465f2010-04-13 05:03:21 +0000790 if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp)
791 return c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 }
Patrick McHardy862465f2010-04-13 05:03:21 +0000793 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794}
795
796/*
797 * Allocate a multicast cache entry
798 */
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000799static struct mfc_cache *ipmr_cache_alloc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800{
Jianjun Kongc354e122008-11-03 00:28:02 -0800801 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
802 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 c->mfc_un.res.minvif = MAXVIFS;
805 return c;
806}
807
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000808static struct mfc_cache *ipmr_cache_alloc_unres(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809{
Jianjun Kongc354e122008-11-03 00:28:02 -0800810 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
811 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 skb_queue_head_init(&c->mfc_un.unres.unresolved);
814 c->mfc_un.unres.expires = jiffies + 10*HZ;
815 return c;
816}
817
818/*
819 * A cache entry has gone into a resolved state from queued
820 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900821
Patrick McHardy0c122952010-04-13 05:03:22 +0000822static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
823 struct mfc_cache *uc, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824{
825 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700826 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
828 /*
829 * Play the pending entries through our router
830 */
831
Jianjun Kongc354e122008-11-03 00:28:02 -0800832 while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700833 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
835
Patrick McHardycb6a4e42010-04-26 16:02:08 +0200836 if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700837 nlh->nlmsg_len = (skb_tail_pointer(skb) -
838 (u8 *)nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 } else {
840 nlh->nlmsg_type = NLMSG_ERROR;
841 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
842 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700843 e = NLMSG_DATA(nlh);
844 e->error = -EMSGSIZE;
845 memset(&e->msg, 0, sizeof(e->msg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 }
Thomas Graf2942e902006-08-15 00:30:25 -0700847
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000848 rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 } else
Patrick McHardy0c122952010-04-13 05:03:22 +0000850 ip_mr_forward(net, mrt, skb, c, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 }
852}
853
854/*
855 * Bounce a cache query up to mrouted. We could use netlink for this but mrouted
856 * expects the following bizarre scheme.
857 *
858 * Called under mrt_lock.
859 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900860
Patrick McHardy0c122952010-04-13 05:03:22 +0000861static int ipmr_cache_report(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000862 struct sk_buff *pkt, vifi_t vifi, int assert)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863{
864 struct sk_buff *skb;
Arnaldo Carvalho de Meloc9bdd4b2007-03-12 20:09:15 -0300865 const int ihl = ip_hdrlen(pkt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 struct igmphdr *igmp;
867 struct igmpmsg *msg;
868 int ret;
869
870#ifdef CONFIG_IP_PIMSM
871 if (assert == IGMPMSG_WHOLEPKT)
872 skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
873 else
874#endif
875 skb = alloc_skb(128, GFP_ATOMIC);
876
Stephen Hemminger132adf52007-03-08 20:44:43 -0800877 if (!skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 return -ENOBUFS;
879
880#ifdef CONFIG_IP_PIMSM
881 if (assert == IGMPMSG_WHOLEPKT) {
882 /* Ugly, but we have no choice with this interface.
883 Duplicate old header, fix ihl, length etc.
884 And all this only to mangle msg->im_msgtype and
885 to set msg->im_mbz to "mbz" :-)
886 */
Arnaldo Carvalho de Melo878c8142007-03-11 22:38:29 -0300887 skb_push(skb, sizeof(struct iphdr));
888 skb_reset_network_header(skb);
Arnaldo Carvalho de Melobadff6d2007-03-13 13:06:52 -0300889 skb_reset_transport_header(skb);
Arnaldo Carvalho de Melo0272ffc2007-03-12 20:05:39 -0300890 msg = (struct igmpmsg *)skb_network_header(skb);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700891 memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 msg->im_msgtype = IGMPMSG_WHOLEPKT;
893 msg->im_mbz = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +0000894 msg->im_vif = mrt->mroute_reg_vif_num;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700895 ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
896 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
897 sizeof(struct iphdr));
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900898 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900900 {
901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 /*
903 * Copy the IP header
904 */
905
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700906 skb->network_header = skb->tail;
Arnaldo Carvalho de Meloddc7b8e2007-03-15 21:42:27 -0300907 skb_put(skb, ihl);
Arnaldo Carvalho de Melo27d7ff42007-03-31 11:55:19 -0300908 skb_copy_to_linear_data(skb, pkt->data, ihl);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700909 ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
910 msg = (struct igmpmsg *)skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 msg->im_vif = vifi;
Eric Dumazetadf30902009-06-02 05:19:30 +0000912 skb_dst_set(skb, dst_clone(skb_dst(pkt)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
914 /*
915 * Add our header
916 */
917
Jianjun Kongc354e122008-11-03 00:28:02 -0800918 igmp=(struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 igmp->type =
920 msg->im_msgtype = assert;
921 igmp->code = 0;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700922 ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700923 skb->transport_header = skb->network_header;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900924 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
Patrick McHardy0c122952010-04-13 05:03:22 +0000926 if (mrt->mroute_sk == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 kfree_skb(skb);
928 return -EINVAL;
929 }
930
931 /*
932 * Deliver to mrouted
933 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000934 ret = sock_queue_rcv_skb(mrt->mroute_sk, skb);
Benjamin Thery70a269e2009-01-22 04:56:15 +0000935 if (ret < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 if (net_ratelimit())
937 printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
938 kfree_skb(skb);
939 }
940
941 return ret;
942}
943
944/*
945 * Queue a packet for resolution. It gets locked cache entry!
946 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900947
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948static int
Patrick McHardy0c122952010-04-13 05:03:22 +0000949ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950{
Patrick McHardy862465f2010-04-13 05:03:21 +0000951 bool found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 int err;
953 struct mfc_cache *c;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700954 const struct iphdr *iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +0000957 list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +0000958 if (c->mfc_mcastgrp == iph->daddr &&
Patrick McHardy862465f2010-04-13 05:03:21 +0000959 c->mfc_origin == iph->saddr) {
960 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 break;
Patrick McHardy862465f2010-04-13 05:03:21 +0000962 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 }
964
Patrick McHardy862465f2010-04-13 05:03:21 +0000965 if (!found) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 /*
967 * Create a new entry if allowable
968 */
969
Patrick McHardy0c122952010-04-13 05:03:22 +0000970 if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000971 (c = ipmr_cache_alloc_unres()) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 spin_unlock_bh(&mfc_unres_lock);
973
974 kfree_skb(skb);
975 return -ENOBUFS;
976 }
977
978 /*
979 * Fill in the new cache entry
980 */
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700981 c->mfc_parent = -1;
982 c->mfc_origin = iph->saddr;
983 c->mfc_mcastgrp = iph->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
985 /*
986 * Reflect first query at mrouted.
987 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000988 err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000989 if (err < 0) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900990 /* If the report failed throw the cache entry
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 out - Brad Parker
992 */
993 spin_unlock_bh(&mfc_unres_lock);
994
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000995 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 kfree_skb(skb);
997 return err;
998 }
999
Patrick McHardy0c122952010-04-13 05:03:22 +00001000 atomic_inc(&mrt->cache_resolve_queue_len);
1001 list_add(&c->list, &mrt->mfc_unres_queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
David S. Miller278554b2010-05-12 00:05:35 -07001003 if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
1004 mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 }
1006
1007 /*
1008 * See if we can append the packet
1009 */
1010 if (c->mfc_un.unres.unresolved.qlen>3) {
1011 kfree_skb(skb);
1012 err = -ENOBUFS;
1013 } else {
Jianjun Kongc354e122008-11-03 00:28:02 -08001014 skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 err = 0;
1016 }
1017
1018 spin_unlock_bh(&mfc_unres_lock);
1019 return err;
1020}
1021
1022/*
1023 * MFC cache manipulation by user space mroute daemon
1024 */
1025
Patrick McHardy0c122952010-04-13 05:03:22 +00001026static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027{
1028 int line;
Patrick McHardy862465f2010-04-13 05:03:21 +00001029 struct mfc_cache *c, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
Jianjun Kongc354e122008-11-03 00:28:02 -08001031 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Patrick McHardy0c122952010-04-13 05:03:22 +00001033 list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
1035 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
1036 write_lock_bh(&mrt_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00001037 list_del(&c->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 write_unlock_bh(&mrt_lock);
1039
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001040 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 return 0;
1042 }
1043 }
1044 return -ENOENT;
1045}
1046
Patrick McHardy0c122952010-04-13 05:03:22 +00001047static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
1048 struct mfcctl *mfc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049{
Patrick McHardy862465f2010-04-13 05:03:21 +00001050 bool found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 int line;
Patrick McHardy862465f2010-04-13 05:03:21 +00001052 struct mfc_cache *uc, *c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
Patrick McHardya50436f22010-03-17 06:04:14 +00001054 if (mfc->mfcc_parent >= MAXVIFS)
1055 return -ENFILE;
1056
Jianjun Kongc354e122008-11-03 00:28:02 -08001057 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Patrick McHardy0c122952010-04-13 05:03:22 +00001059 list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
Patrick McHardy862465f2010-04-13 05:03:21 +00001061 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
1062 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 break;
Patrick McHardy862465f2010-04-13 05:03:21 +00001064 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 }
1066
Patrick McHardy862465f2010-04-13 05:03:21 +00001067 if (found) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 write_lock_bh(&mrt_lock);
1069 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001070 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if (!mrtsock)
1072 c->mfc_flags |= MFC_STATIC;
1073 write_unlock_bh(&mrt_lock);
1074 return 0;
1075 }
1076
Joe Perchesf97c1e02007-12-16 13:45:43 -08001077 if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 return -EINVAL;
1079
Patrick McHardyd658f8a2010-04-13 05:03:20 +00001080 c = ipmr_cache_alloc();
Jianjun Kongc354e122008-11-03 00:28:02 -08001081 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return -ENOMEM;
1083
Jianjun Kongc354e122008-11-03 00:28:02 -08001084 c->mfc_origin = mfc->mfcc_origin.s_addr;
1085 c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
1086 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001087 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 if (!mrtsock)
1089 c->mfc_flags |= MFC_STATIC;
1090
1091 write_lock_bh(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001092 list_add(&c->list, &mrt->mfc_cache_array[line]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 write_unlock_bh(&mrt_lock);
1094
1095 /*
1096 * Check to see if we resolved a queued list. If so we
1097 * need to send on the frames and tidy up.
1098 */
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001099 found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001101 list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +00001102 if (uc->mfc_origin == c->mfc_origin &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 uc->mfc_mcastgrp == c->mfc_mcastgrp) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001104 list_del(&uc->list);
Patrick McHardy0c122952010-04-13 05:03:22 +00001105 atomic_dec(&mrt->cache_resolve_queue_len);
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001106 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 break;
1108 }
1109 }
Patrick McHardy0c122952010-04-13 05:03:22 +00001110 if (list_empty(&mrt->mfc_unres_queue))
1111 del_timer(&mrt->ipmr_expire_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 spin_unlock_bh(&mfc_unres_lock);
1113
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001114 if (found) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001115 ipmr_cache_resolve(net, mrt, uc, c);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001116 ipmr_cache_free(uc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 }
1118 return 0;
1119}
1120
1121/*
1122 * Close the multicast socket, and clear the vif tables etc
1123 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001124
Patrick McHardy0c122952010-04-13 05:03:22 +00001125static void mroute_clean_tables(struct mr_table *mrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126{
1127 int i;
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001128 LIST_HEAD(list);
Patrick McHardy862465f2010-04-13 05:03:21 +00001129 struct mfc_cache *c, *next;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001130
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 /*
1132 * Shut down all active vif entries
1133 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001134 for (i = 0; i < mrt->maxvif; i++) {
1135 if (!(mrt->vif_table[i].flags&VIFF_STATIC))
1136 vif_delete(mrt, i, 0, &list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 }
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001138 unregister_netdevice_many(&list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
1140 /*
1141 * Wipe the cache
1142 */
Patrick McHardy862465f2010-04-13 05:03:21 +00001143 for (i = 0; i < MFC_LINES; i++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001144 list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001145 if (c->mfc_flags&MFC_STATIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 write_lock_bh(&mrt_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00001148 list_del(&c->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 write_unlock_bh(&mrt_lock);
1150
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001151 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 }
1153 }
1154
Patrick McHardy0c122952010-04-13 05:03:22 +00001155 if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001157 list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001158 list_del(&c->list);
Patrick McHardy0c122952010-04-13 05:03:22 +00001159 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161 spin_unlock_bh(&mfc_unres_lock);
1162 }
1163}
1164
1165static void mrtsock_destruct(struct sock *sk)
1166{
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001167 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001168 struct mr_table *mrt;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001169
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 rtnl_lock();
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001171 ipmr_for_each_table(mrt, net) {
1172 if (sk == mrt->mroute_sk) {
1173 IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001175 write_lock_bh(&mrt_lock);
1176 mrt->mroute_sk = NULL;
1177 write_unlock_bh(&mrt_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001179 mroute_clean_tables(mrt);
1180 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 }
1182 rtnl_unlock();
1183}
1184
1185/*
1186 * Socket options and virtual interface manipulation. The whole
1187 * virtual interface system is a complete heap, but unfortunately
1188 * that's how BSD mrouted happens to think. Maybe one day with a proper
1189 * MOSPF/PIM router set up we can clean this up.
1190 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001191
David S. Millerb7058842009-09-30 16:12:20 -07001192int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193{
1194 int ret;
1195 struct vifctl vif;
1196 struct mfcctl mfc;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001197 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001198 struct mr_table *mrt;
1199
1200 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1201 if (mrt == NULL)
1202 return -ENOENT;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001203
Stephen Hemminger132adf52007-03-08 20:44:43 -08001204 if (optname != MRT_INIT) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001205 if (sk != mrt->mroute_sk && !capable(CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 return -EACCES;
1207 }
1208
Stephen Hemminger132adf52007-03-08 20:44:43 -08001209 switch (optname) {
1210 case MRT_INIT:
1211 if (sk->sk_type != SOCK_RAW ||
Eric Dumazetc720c7e82009-10-15 06:30:45 +00001212 inet_sk(sk)->inet_num != IPPROTO_IGMP)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001213 return -EOPNOTSUPP;
Jianjun Kongc354e122008-11-03 00:28:02 -08001214 if (optlen != sizeof(int))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001215 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
Stephen Hemminger132adf52007-03-08 20:44:43 -08001217 rtnl_lock();
Patrick McHardy0c122952010-04-13 05:03:22 +00001218 if (mrt->mroute_sk) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 rtnl_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -08001220 return -EADDRINUSE;
1221 }
1222
1223 ret = ip_ra_control(sk, 1, mrtsock_destruct);
1224 if (ret == 0) {
1225 write_lock_bh(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001226 mrt->mroute_sk = sk;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001227 write_unlock_bh(&mrt_lock);
1228
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001229 IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001230 }
1231 rtnl_unlock();
1232 return ret;
1233 case MRT_DONE:
Patrick McHardy0c122952010-04-13 05:03:22 +00001234 if (sk != mrt->mroute_sk)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001235 return -EACCES;
1236 return ip_ra_control(sk, 0, NULL);
1237 case MRT_ADD_VIF:
1238 case MRT_DEL_VIF:
Jianjun Kongc354e122008-11-03 00:28:02 -08001239 if (optlen != sizeof(vif))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001240 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -08001241 if (copy_from_user(&vif, optval, sizeof(vif)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001242 return -EFAULT;
1243 if (vif.vifc_vifi >= MAXVIFS)
1244 return -ENFILE;
1245 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -08001246 if (optname == MRT_ADD_VIF) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001247 ret = vif_add(net, mrt, &vif, sk == mrt->mroute_sk);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001248 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00001249 ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001250 }
1251 rtnl_unlock();
1252 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
1254 /*
1255 * Manipulate the forwarding caches. These live
1256 * in a sort of kernel/user symbiosis.
1257 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001258 case MRT_ADD_MFC:
1259 case MRT_DEL_MFC:
Jianjun Kongc354e122008-11-03 00:28:02 -08001260 if (optlen != sizeof(mfc))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001261 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -08001262 if (copy_from_user(&mfc, optval, sizeof(mfc)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001263 return -EFAULT;
1264 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -08001265 if (optname == MRT_DEL_MFC)
Patrick McHardy0c122952010-04-13 05:03:22 +00001266 ret = ipmr_mfc_delete(mrt, &mfc);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001267 else
Patrick McHardy0c122952010-04-13 05:03:22 +00001268 ret = ipmr_mfc_add(net, mrt, &mfc, sk == mrt->mroute_sk);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001269 rtnl_unlock();
1270 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 /*
1272 * Control PIM assert.
1273 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001274 case MRT_ASSERT:
1275 {
1276 int v;
1277 if (get_user(v,(int __user *)optval))
1278 return -EFAULT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001279 mrt->mroute_do_assert = (v) ? 1 : 0;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001280 return 0;
1281 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282#ifdef CONFIG_IP_PIMSM
Stephen Hemminger132adf52007-03-08 20:44:43 -08001283 case MRT_PIM:
1284 {
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001285 int v;
1286
Stephen Hemminger132adf52007-03-08 20:44:43 -08001287 if (get_user(v,(int __user *)optval))
1288 return -EFAULT;
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001289 v = (v) ? 1 : 0;
1290
Stephen Hemminger132adf52007-03-08 20:44:43 -08001291 rtnl_lock();
1292 ret = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +00001293 if (v != mrt->mroute_do_pim) {
1294 mrt->mroute_do_pim = v;
1295 mrt->mroute_do_assert = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 }
Stephen Hemminger132adf52007-03-08 20:44:43 -08001297 rtnl_unlock();
1298 return ret;
1299 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001301#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
1302 case MRT_TABLE:
1303 {
1304 u32 v;
1305
1306 if (optlen != sizeof(u32))
1307 return -EINVAL;
1308 if (get_user(v, (u32 __user *)optval))
1309 return -EFAULT;
1310 if (sk == mrt->mroute_sk)
1311 return -EBUSY;
1312
1313 rtnl_lock();
1314 ret = 0;
1315 if (!ipmr_new_table(net, v))
1316 ret = -ENOMEM;
1317 raw_sk(sk)->ipmr_table = v;
1318 rtnl_unlock();
1319 return ret;
1320 }
1321#endif
Stephen Hemminger132adf52007-03-08 20:44:43 -08001322 /*
1323 * Spurious command, or MRT_VERSION which you cannot
1324 * set.
1325 */
1326 default:
1327 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 }
1329}
1330
1331/*
1332 * Getsock opt support for the multicast routing system.
1333 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001334
Jianjun Kongc354e122008-11-03 00:28:02 -08001335int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336{
1337 int olr;
1338 int val;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001339 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001340 struct mr_table *mrt;
1341
1342 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1343 if (mrt == NULL)
1344 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
Jianjun Kongc354e122008-11-03 00:28:02 -08001346 if (optname != MRT_VERSION &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347#ifdef CONFIG_IP_PIMSM
1348 optname!=MRT_PIM &&
1349#endif
1350 optname!=MRT_ASSERT)
1351 return -ENOPROTOOPT;
1352
1353 if (get_user(olr, optlen))
1354 return -EFAULT;
1355
1356 olr = min_t(unsigned int, olr, sizeof(int));
1357 if (olr < 0)
1358 return -EINVAL;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001359
Jianjun Kongc354e122008-11-03 00:28:02 -08001360 if (put_user(olr, optlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 return -EFAULT;
Jianjun Kongc354e122008-11-03 00:28:02 -08001362 if (optname == MRT_VERSION)
1363 val = 0x0305;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364#ifdef CONFIG_IP_PIMSM
Jianjun Kongc354e122008-11-03 00:28:02 -08001365 else if (optname == MRT_PIM)
Patrick McHardy0c122952010-04-13 05:03:22 +00001366 val = mrt->mroute_do_pim;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367#endif
1368 else
Patrick McHardy0c122952010-04-13 05:03:22 +00001369 val = mrt->mroute_do_assert;
Jianjun Kongc354e122008-11-03 00:28:02 -08001370 if (copy_to_user(optval, &val, olr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 return -EFAULT;
1372 return 0;
1373}
1374
1375/*
1376 * The IP multicast ioctl support routines.
1377 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001378
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
1380{
1381 struct sioc_sg_req sr;
1382 struct sioc_vif_req vr;
1383 struct vif_device *vif;
1384 struct mfc_cache *c;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001385 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001386 struct mr_table *mrt;
1387
1388 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1389 if (mrt == NULL)
1390 return -ENOENT;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001391
Stephen Hemminger132adf52007-03-08 20:44:43 -08001392 switch (cmd) {
1393 case SIOCGETVIFCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001394 if (copy_from_user(&vr, arg, sizeof(vr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001395 return -EFAULT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001396 if (vr.vifi >= mrt->maxvif)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001397 return -EINVAL;
1398 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001399 vif = &mrt->vif_table[vr.vifi];
1400 if (VIF_EXISTS(mrt, vr.vifi)) {
Jianjun Kongc354e122008-11-03 00:28:02 -08001401 vr.icount = vif->pkt_in;
1402 vr.ocount = vif->pkt_out;
1403 vr.ibytes = vif->bytes_in;
1404 vr.obytes = vif->bytes_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001406
Jianjun Kongc354e122008-11-03 00:28:02 -08001407 if (copy_to_user(arg, &vr, sizeof(vr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 return -EFAULT;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001409 return 0;
1410 }
1411 read_unlock(&mrt_lock);
1412 return -EADDRNOTAVAIL;
1413 case SIOCGETSGCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001414 if (copy_from_user(&sr, arg, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001415 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
Stephen Hemminger132adf52007-03-08 20:44:43 -08001417 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001418 c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001419 if (c) {
1420 sr.pktcnt = c->mfc_un.res.pkt;
1421 sr.bytecnt = c->mfc_un.res.bytes;
1422 sr.wrong_if = c->mfc_un.res.wrong_if;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001424
Jianjun Kongc354e122008-11-03 00:28:02 -08001425 if (copy_to_user(arg, &sr, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001426 return -EFAULT;
1427 return 0;
1428 }
1429 read_unlock(&mrt_lock);
1430 return -EADDRNOTAVAIL;
1431 default:
1432 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 }
1434}
1435
1436
1437static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
1438{
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001439 struct net_device *dev = ptr;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001440 struct net *net = dev_net(dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001441 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 struct vif_device *v;
1443 int ct;
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001444 LIST_HEAD(list);
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001445
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 if (event != NETDEV_UNREGISTER)
1447 return NOTIFY_DONE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001448
1449 ipmr_for_each_table(mrt, net) {
1450 v = &mrt->vif_table[0];
1451 for (ct = 0; ct < mrt->maxvif; ct++, v++) {
1452 if (v->dev == dev)
1453 vif_delete(mrt, ct, 1, &list);
1454 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 }
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001456 unregister_netdevice_many(&list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 return NOTIFY_DONE;
1458}
1459
1460
Jianjun Kongc354e122008-11-03 00:28:02 -08001461static struct notifier_block ip_mr_notifier = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 .notifier_call = ipmr_device_event,
1463};
1464
1465/*
1466 * Encapsulate a packet by attaching a valid IPIP header to it.
1467 * This avoids tunnel drivers and other mess and gives us the speed so
1468 * important for multicast video.
1469 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001470
Al Viro114c7842006-09-27 18:39:29 -07001471static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472{
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001473 struct iphdr *iph;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001474 struct iphdr *old_iph = ip_hdr(skb);
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001475
1476 skb_push(skb, sizeof(struct iphdr));
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001477 skb->transport_header = skb->network_header;
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001478 skb_reset_network_header(skb);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001479 iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480
1481 iph->version = 4;
Arnaldo Carvalho de Meloe023dd62007-03-12 20:09:36 -03001482 iph->tos = old_iph->tos;
1483 iph->ttl = old_iph->ttl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 iph->frag_off = 0;
1485 iph->daddr = daddr;
1486 iph->saddr = saddr;
1487 iph->protocol = IPPROTO_IPIP;
1488 iph->ihl = 5;
1489 iph->tot_len = htons(skb->len);
Eric Dumazetadf30902009-06-02 05:19:30 +00001490 ip_select_ident(iph, skb_dst(skb), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 ip_send_check(iph);
1492
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
1494 nf_reset(skb);
1495}
1496
1497static inline int ipmr_forward_finish(struct sk_buff *skb)
1498{
1499 struct ip_options * opt = &(IPCB(skb)->opt);
1500
Eric Dumazetadf30902009-06-02 05:19:30 +00001501 IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503 if (unlikely(opt->optlen))
1504 ip_forward_options(skb);
1505
1506 return dst_output(skb);
1507}
1508
1509/*
1510 * Processing handlers for ipmr_forward
1511 */
1512
Patrick McHardy0c122952010-04-13 05:03:22 +00001513static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
1514 struct sk_buff *skb, struct mfc_cache *c, int vifi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515{
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001516 const struct iphdr *iph = ip_hdr(skb);
Patrick McHardy0c122952010-04-13 05:03:22 +00001517 struct vif_device *vif = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 struct net_device *dev;
1519 struct rtable *rt;
1520 int encap = 0;
1521
1522 if (vif->dev == NULL)
1523 goto out_free;
1524
1525#ifdef CONFIG_IP_PIMSM
1526 if (vif->flags & VIFF_REGISTER) {
1527 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001528 vif->bytes_out += skb->len;
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -07001529 vif->dev->stats.tx_bytes += skb->len;
1530 vif->dev->stats.tx_packets++;
Patrick McHardy0c122952010-04-13 05:03:22 +00001531 ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
Ilpo Järvinen69ebbf52009-02-06 23:46:51 -08001532 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 }
1534#endif
1535
1536 if (vif->flags&VIFF_TUNNEL) {
1537 struct flowi fl = { .oif = vif->link,
1538 .nl_u = { .ip4_u =
1539 { .daddr = vif->remote,
1540 .saddr = vif->local,
1541 .tos = RT_TOS(iph->tos) } },
1542 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001543 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 goto out_free;
1545 encap = sizeof(struct iphdr);
1546 } else {
1547 struct flowi fl = { .oif = vif->link,
1548 .nl_u = { .ip4_u =
1549 { .daddr = iph->daddr,
1550 .tos = RT_TOS(iph->tos) } },
1551 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001552 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 goto out_free;
1554 }
1555
1556 dev = rt->u.dst.dev;
1557
1558 if (skb->len+encap > dst_mtu(&rt->u.dst) && (ntohs(iph->frag_off) & IP_DF)) {
1559 /* Do not fragment multicasts. Alas, IPv4 does not
1560 allow to send ICMP, so that packets will disappear
1561 to blackhole.
1562 */
1563
Pavel Emelyanov7c73a6f2008-07-16 20:20:11 -07001564 IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 ip_rt_put(rt);
1566 goto out_free;
1567 }
1568
1569 encap += LL_RESERVED_SPACE(dev) + rt->u.dst.header_len;
1570
1571 if (skb_cow(skb, encap)) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001572 ip_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 goto out_free;
1574 }
1575
1576 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001577 vif->bytes_out += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578
Eric Dumazetadf30902009-06-02 05:19:30 +00001579 skb_dst_drop(skb);
1580 skb_dst_set(skb, &rt->u.dst);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001581 ip_decrease_ttl(ip_hdr(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
1583 /* FIXME: forward and output firewalls used to be called here.
1584 * What do we do with netfilter? -- RR */
1585 if (vif->flags & VIFF_TUNNEL) {
1586 ip_encap(skb, vif->local, vif->remote);
1587 /* FIXME: extra output firewall step used to be here. --RR */
Pavel Emelyanov2f4c02d2008-05-21 14:16:14 -07001588 vif->dev->stats.tx_packets++;
1589 vif->dev->stats.tx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 }
1591
1592 IPCB(skb)->flags |= IPSKB_FORWARDED;
1593
1594 /*
1595 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
1596 * not only before forwarding, but after forwarding on all output
1597 * interfaces. It is clear, if mrouter runs a multicasting
1598 * program, it should receive packets not depending to what interface
1599 * program is joined.
1600 * If we will not make it, the program will have to join on all
1601 * interfaces. On the other hand, multihoming host (or router, but
1602 * not mrouter) cannot join to more than one interface - it will
1603 * result in receiving multiple packets.
1604 */
Jan Engelhardt9bbc7682010-03-23 04:07:29 +01001605 NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 ipmr_forward_finish);
1607 return;
1608
1609out_free:
1610 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611}
1612
Patrick McHardy0c122952010-04-13 05:03:22 +00001613static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614{
1615 int ct;
Patrick McHardy0c122952010-04-13 05:03:22 +00001616
1617 for (ct = mrt->maxvif-1; ct >= 0; ct--) {
1618 if (mrt->vif_table[ct].dev == dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 break;
1620 }
1621 return ct;
1622}
1623
1624/* "local" means that we should preserve one skb (for local delivery) */
1625
Patrick McHardy0c122952010-04-13 05:03:22 +00001626static int ip_mr_forward(struct net *net, struct mr_table *mrt,
1627 struct sk_buff *skb, struct mfc_cache *cache,
1628 int local)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629{
1630 int psend = -1;
1631 int vif, ct;
1632
1633 vif = cache->mfc_parent;
1634 cache->mfc_un.res.pkt++;
1635 cache->mfc_un.res.bytes += skb->len;
1636
1637 /*
1638 * Wrong interface: drop packet and (maybe) send PIM assert.
1639 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001640 if (mrt->vif_table[vif].dev != skb->dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 int true_vifi;
1642
Eric Dumazet511c3f92009-06-02 05:14:27 +00001643 if (skb_rtable(skb)->fl.iif == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 /* It is our own packet, looped back.
1645 Very complicated situation...
1646
1647 The best workaround until routing daemons will be
1648 fixed is not to redistribute packet, if it was
1649 send through wrong interface. It means, that
1650 multicast applications WILL NOT work for
1651 (S,G), which have default multicast route pointing
1652 to wrong oif. In any case, it is not a good
1653 idea to use multicasting applications on router.
1654 */
1655 goto dont_forward;
1656 }
1657
1658 cache->mfc_un.res.wrong_if++;
Patrick McHardy0c122952010-04-13 05:03:22 +00001659 true_vifi = ipmr_find_vif(mrt, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
Patrick McHardy0c122952010-04-13 05:03:22 +00001661 if (true_vifi >= 0 && mrt->mroute_do_assert &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 /* pimsm uses asserts, when switching from RPT to SPT,
1663 so that we cannot check that packet arrived on an oif.
1664 It is bad, but otherwise we would need to move pretty
1665 large chunk of pimd to kernel. Ough... --ANK
1666 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001667 (mrt->mroute_do_pim ||
Benjamin Thery6f9374a2009-01-22 04:56:20 +00001668 cache->mfc_un.res.ttls[true_vifi] < 255) &&
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001669 time_after(jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
1671 cache->mfc_un.res.last_assert = jiffies;
Patrick McHardy0c122952010-04-13 05:03:22 +00001672 ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 }
1674 goto dont_forward;
1675 }
1676
Patrick McHardy0c122952010-04-13 05:03:22 +00001677 mrt->vif_table[vif].pkt_in++;
1678 mrt->vif_table[vif].bytes_in += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
1680 /*
1681 * Forward the frame
1682 */
1683 for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001684 if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 if (psend != -1) {
1686 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1687 if (skb2)
Patrick McHardy0c122952010-04-13 05:03:22 +00001688 ipmr_queue_xmit(net, mrt, skb2, cache,
1689 psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 }
Jianjun Kongc354e122008-11-03 00:28:02 -08001691 psend = ct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 }
1693 }
1694 if (psend != -1) {
1695 if (local) {
1696 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1697 if (skb2)
Patrick McHardy0c122952010-04-13 05:03:22 +00001698 ipmr_queue_xmit(net, mrt, skb2, cache, psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00001700 ipmr_queue_xmit(net, mrt, skb, cache, psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 return 0;
1702 }
1703 }
1704
1705dont_forward:
1706 if (!local)
1707 kfree_skb(skb);
1708 return 0;
1709}
1710
1711
1712/*
1713 * Multicast packets for forwarding arrive here
1714 */
1715
1716int ip_mr_input(struct sk_buff *skb)
1717{
1718 struct mfc_cache *cache;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001719 struct net *net = dev_net(skb->dev);
Eric Dumazet511c3f92009-06-02 05:14:27 +00001720 int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001721 struct mr_table *mrt;
1722 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723
1724 /* Packet is looped back after forward, it should not be
1725 forwarded second time, but still can be delivered locally.
1726 */
1727 if (IPCB(skb)->flags&IPSKB_FORWARDED)
1728 goto dont_forward;
1729
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001730 err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt);
1731 if (err < 0)
1732 return err;
1733
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 if (!local) {
1735 if (IPCB(skb)->opt.router_alert) {
1736 if (ip_call_ra_chain(skb))
1737 return 0;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001738 } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 /* IGMPv1 (and broken IGMPv2 implementations sort of
1740 Cisco IOS <= 11.2(8)) do not put router alert
1741 option to IGMP packets destined to routable
1742 groups. It is very bad, because it means
1743 that we can forward NO IGMP messages.
1744 */
1745 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001746 if (mrt->mroute_sk) {
Patrick McHardy2715bcf2005-06-21 14:06:24 -07001747 nf_reset(skb);
Patrick McHardy0c122952010-04-13 05:03:22 +00001748 raw_rcv(mrt->mroute_sk, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 read_unlock(&mrt_lock);
1750 return 0;
1751 }
1752 read_unlock(&mrt_lock);
1753 }
1754 }
1755
1756 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001757 cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
1759 /*
1760 * No usable cache entry
1761 */
Jianjun Kongc354e122008-11-03 00:28:02 -08001762 if (cache == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 int vif;
1764
1765 if (local) {
1766 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1767 ip_local_deliver(skb);
1768 if (skb2 == NULL) {
1769 read_unlock(&mrt_lock);
1770 return -ENOBUFS;
1771 }
1772 skb = skb2;
1773 }
1774
Patrick McHardy0c122952010-04-13 05:03:22 +00001775 vif = ipmr_find_vif(mrt, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 if (vif >= 0) {
Eric Dumazet0eae88f2010-04-20 19:06:52 -07001777 int err2 = ipmr_cache_unresolved(mrt, vif, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 read_unlock(&mrt_lock);
1779
Eric Dumazet0eae88f2010-04-20 19:06:52 -07001780 return err2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 }
1782 read_unlock(&mrt_lock);
1783 kfree_skb(skb);
1784 return -ENODEV;
1785 }
1786
Patrick McHardy0c122952010-04-13 05:03:22 +00001787 ip_mr_forward(net, mrt, skb, cache, local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788
1789 read_unlock(&mrt_lock);
1790
1791 if (local)
1792 return ip_local_deliver(skb);
1793
1794 return 0;
1795
1796dont_forward:
1797 if (local)
1798 return ip_local_deliver(skb);
1799 kfree_skb(skb);
1800 return 0;
1801}
1802
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001803#ifdef CONFIG_IP_PIMSM
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001804static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
1805 unsigned int pimlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806{
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001807 struct net_device *reg_dev = NULL;
1808 struct iphdr *encap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001810 encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811 /*
1812 Check that:
1813 a. packet is really destinted to a multicast group
1814 b. packet is not a NULL-REGISTER
1815 c. packet is not truncated
1816 */
Joe Perchesf97c1e02007-12-16 13:45:43 -08001817 if (!ipv4_is_multicast(encap->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 encap->tot_len == 0 ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001819 ntohs(encap->tot_len) + pimlen > skb->len)
1820 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
1822 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001823 if (mrt->mroute_reg_vif_num >= 0)
1824 reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 if (reg_dev)
1826 dev_hold(reg_dev);
1827 read_unlock(&mrt_lock);
1828
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001829 if (reg_dev == NULL)
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001830 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001832 skb->mac_header = skb->network_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 skb_pull(skb, (u8*)encap - skb->data);
Arnaldo Carvalho de Melo31c77112007-03-10 19:04:55 -03001834 skb_reset_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 skb->protocol = htons(ETH_P_IP);
1836 skb->ip_summed = 0;
1837 skb->pkt_type = PACKET_HOST;
Eric Dumazetd19d56d2010-05-17 22:36:55 -07001838
1839 skb_tunnel_rx(skb, reg_dev);
1840
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841 netif_rx(skb);
1842 dev_put(reg_dev);
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001843
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 return 0;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001845}
1846#endif
1847
1848#ifdef CONFIG_IP_PIMSM_V1
1849/*
1850 * Handle IGMP messages of PIMv1
1851 */
1852
1853int pim_rcv_v1(struct sk_buff * skb)
1854{
1855 struct igmphdr *pim;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001856 struct net *net = dev_net(skb->dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001857 struct mr_table *mrt;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001858
1859 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
1860 goto drop;
1861
1862 pim = igmp_hdr(skb);
1863
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001864 if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
1865 goto drop;
1866
Patrick McHardy0c122952010-04-13 05:03:22 +00001867 if (!mrt->mroute_do_pim ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001868 pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
1869 goto drop;
1870
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001871 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001872drop:
1873 kfree_skb(skb);
1874 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875 return 0;
1876}
1877#endif
1878
1879#ifdef CONFIG_IP_PIMSM_V2
1880static int pim_rcv(struct sk_buff * skb)
1881{
1882 struct pimreghdr *pim;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001883 struct net *net = dev_net(skb->dev);
1884 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001886 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 goto drop;
1888
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001889 pim = (struct pimreghdr *)skb_transport_header(skb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001890 if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 (pim->flags&PIM_NULL_REGISTER) ||
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001892 (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
Al Virod3bc23e2006-11-14 21:24:49 -08001893 csum_fold(skb_checksum(skb, 0, skb->len, 0))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 goto drop;
1895
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001896 if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
1897 goto drop;
1898
1899 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001900drop:
1901 kfree_skb(skb);
1902 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 return 0;
1904}
1905#endif
1906
Patrick McHardycb6a4e42010-04-26 16:02:08 +02001907static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
1908 struct mfc_cache *c, struct rtmsg *rtm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909{
1910 int ct;
1911 struct rtnexthop *nhp;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001912 u8 *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 struct rtattr *mp_head;
1914
Nicolas Dichtel74381892010-03-25 23:45:35 +00001915 /* If cache is unresolved, don't try to parse IIF and OIF */
Dan Carpentered0f160a2010-05-26 00:38:56 -07001916 if (c->mfc_parent >= MAXVIFS)
Nicolas Dichtel74381892010-03-25 23:45:35 +00001917 return -ENOENT;
1918
Patrick McHardy0c122952010-04-13 05:03:22 +00001919 if (VIF_EXISTS(mrt, c->mfc_parent))
1920 RTA_PUT(skb, RTA_IIF, 4, &mrt->vif_table[c->mfc_parent].dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921
Jianjun Kongc354e122008-11-03 00:28:02 -08001922 mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923
1924 for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001925 if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
1927 goto rtattr_failure;
Jianjun Kongc354e122008-11-03 00:28:02 -08001928 nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 nhp->rtnh_flags = 0;
1930 nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
Patrick McHardy0c122952010-04-13 05:03:22 +00001931 nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 nhp->rtnh_len = sizeof(*nhp);
1933 }
1934 }
1935 mp_head->rta_type = RTA_MULTIPATH;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001936 mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 rtm->rtm_type = RTN_MULTICAST;
1938 return 1;
1939
1940rtattr_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -07001941 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 return -EMSGSIZE;
1943}
1944
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001945int ipmr_get_route(struct net *net,
1946 struct sk_buff *skb, struct rtmsg *rtm, int nowait)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947{
1948 int err;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001949 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 struct mfc_cache *cache;
Eric Dumazet511c3f92009-06-02 05:14:27 +00001951 struct rtable *rt = skb_rtable(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001953 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
1954 if (mrt == NULL)
1955 return -ENOENT;
1956
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001958 cache = ipmr_cache_find(mrt, rt->rt_src, rt->rt_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
Jianjun Kongc354e122008-11-03 00:28:02 -08001960 if (cache == NULL) {
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001961 struct sk_buff *skb2;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001962 struct iphdr *iph;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 struct net_device *dev;
1964 int vif;
1965
1966 if (nowait) {
1967 read_unlock(&mrt_lock);
1968 return -EAGAIN;
1969 }
1970
1971 dev = skb->dev;
Patrick McHardy0c122952010-04-13 05:03:22 +00001972 if (dev == NULL || (vif = ipmr_find_vif(mrt, dev)) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 read_unlock(&mrt_lock);
1974 return -ENODEV;
1975 }
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001976 skb2 = skb_clone(skb, GFP_ATOMIC);
1977 if (!skb2) {
1978 read_unlock(&mrt_lock);
1979 return -ENOMEM;
1980 }
1981
Arnaldo Carvalho de Meloe2d1bca2007-04-10 20:46:21 -07001982 skb_push(skb2, sizeof(struct iphdr));
1983 skb_reset_network_header(skb2);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001984 iph = ip_hdr(skb2);
1985 iph->ihl = sizeof(struct iphdr) >> 2;
1986 iph->saddr = rt->rt_src;
1987 iph->daddr = rt->rt_dst;
1988 iph->version = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +00001989 err = ipmr_cache_unresolved(mrt, vif, skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 read_unlock(&mrt_lock);
1991 return err;
1992 }
1993
1994 if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
1995 cache->mfc_flags |= MFC_NOTIFY;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02001996 err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 read_unlock(&mrt_lock);
1998 return err;
1999}
2000
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002001static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
2002 u32 pid, u32 seq, struct mfc_cache *c)
2003{
2004 struct nlmsghdr *nlh;
2005 struct rtmsg *rtm;
2006
2007 nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
2008 if (nlh == NULL)
2009 return -EMSGSIZE;
2010
2011 rtm = nlmsg_data(nlh);
2012 rtm->rtm_family = RTNL_FAMILY_IPMR;
2013 rtm->rtm_dst_len = 32;
2014 rtm->rtm_src_len = 32;
2015 rtm->rtm_tos = 0;
2016 rtm->rtm_table = mrt->id;
2017 NLA_PUT_U32(skb, RTA_TABLE, mrt->id);
2018 rtm->rtm_type = RTN_MULTICAST;
2019 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2020 rtm->rtm_protocol = RTPROT_UNSPEC;
2021 rtm->rtm_flags = 0;
2022
2023 NLA_PUT_BE32(skb, RTA_SRC, c->mfc_origin);
2024 NLA_PUT_BE32(skb, RTA_DST, c->mfc_mcastgrp);
2025
2026 if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0)
2027 goto nla_put_failure;
2028
2029 return nlmsg_end(skb, nlh);
2030
2031nla_put_failure:
2032 nlmsg_cancel(skb, nlh);
2033 return -EMSGSIZE;
2034}
2035
2036static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
2037{
2038 struct net *net = sock_net(skb->sk);
2039 struct mr_table *mrt;
2040 struct mfc_cache *mfc;
2041 unsigned int t = 0, s_t;
2042 unsigned int h = 0, s_h;
2043 unsigned int e = 0, s_e;
2044
2045 s_t = cb->args[0];
2046 s_h = cb->args[1];
2047 s_e = cb->args[2];
2048
2049 read_lock(&mrt_lock);
2050 ipmr_for_each_table(mrt, net) {
2051 if (t < s_t)
2052 goto next_table;
2053 if (t > s_t)
2054 s_h = 0;
2055 for (h = s_h; h < MFC_LINES; h++) {
2056 list_for_each_entry(mfc, &mrt->mfc_cache_array[h], list) {
2057 if (e < s_e)
2058 goto next_entry;
2059 if (ipmr_fill_mroute(mrt, skb,
2060 NETLINK_CB(cb->skb).pid,
2061 cb->nlh->nlmsg_seq,
2062 mfc) < 0)
2063 goto done;
2064next_entry:
2065 e++;
2066 }
2067 e = s_e = 0;
2068 }
2069 s_h = 0;
2070next_table:
2071 t++;
2072 }
2073done:
2074 read_unlock(&mrt_lock);
2075
2076 cb->args[2] = e;
2077 cb->args[1] = h;
2078 cb->args[0] = t;
2079
2080 return skb->len;
2081}
2082
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002083#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084/*
2085 * The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
2086 */
2087struct ipmr_vif_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002088 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002089 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 int ct;
2091};
2092
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002093static struct vif_device *ipmr_vif_seq_idx(struct net *net,
2094 struct ipmr_vif_iter *iter,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 loff_t pos)
2096{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002097 struct mr_table *mrt = iter->mrt;
Patrick McHardy0c122952010-04-13 05:03:22 +00002098
2099 for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) {
2100 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 continue;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002102 if (pos-- == 0)
Patrick McHardy0c122952010-04-13 05:03:22 +00002103 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 }
2105 return NULL;
2106}
2107
2108static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002109 __acquires(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002111 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002112 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002113 struct mr_table *mrt;
2114
2115 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2116 if (mrt == NULL)
2117 return ERR_PTR(-ENOENT);
2118
2119 iter->mrt = mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002120
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 read_lock(&mrt_lock);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002122 return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 : SEQ_START_TOKEN;
2124}
2125
2126static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2127{
2128 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002129 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002130 struct mr_table *mrt = iter->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131
2132 ++*pos;
2133 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002134 return ipmr_vif_seq_idx(net, iter, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002135
Patrick McHardy0c122952010-04-13 05:03:22 +00002136 while (++iter->ct < mrt->maxvif) {
2137 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 continue;
Patrick McHardy0c122952010-04-13 05:03:22 +00002139 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 }
2141 return NULL;
2142}
2143
2144static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002145 __releases(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146{
2147 read_unlock(&mrt_lock);
2148}
2149
2150static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
2151{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002152 struct ipmr_vif_iter *iter = seq->private;
2153 struct mr_table *mrt = iter->mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002154
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002156 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n");
2158 } else {
2159 const struct vif_device *vif = v;
2160 const char *name = vif->dev ? vif->dev->name : "none";
2161
2162 seq_printf(seq,
2163 "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n",
Patrick McHardy0c122952010-04-13 05:03:22 +00002164 vif - mrt->vif_table,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002165 name, vif->bytes_in, vif->pkt_in,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166 vif->bytes_out, vif->pkt_out,
2167 vif->flags, vif->local, vif->remote);
2168 }
2169 return 0;
2170}
2171
Stephen Hemmingerf6908082007-03-12 14:34:29 -07002172static const struct seq_operations ipmr_vif_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 .start = ipmr_vif_seq_start,
2174 .next = ipmr_vif_seq_next,
2175 .stop = ipmr_vif_seq_stop,
2176 .show = ipmr_vif_seq_show,
2177};
2178
2179static int ipmr_vif_open(struct inode *inode, struct file *file)
2180{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002181 return seq_open_net(inode, file, &ipmr_vif_seq_ops,
2182 sizeof(struct ipmr_vif_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183}
2184
Arjan van de Ven9a321442007-02-12 00:55:35 -08002185static const struct file_operations ipmr_vif_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186 .owner = THIS_MODULE,
2187 .open = ipmr_vif_open,
2188 .read = seq_read,
2189 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002190 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191};
2192
2193struct ipmr_mfc_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002194 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002195 struct mr_table *mrt;
Patrick McHardy862465f2010-04-13 05:03:21 +00002196 struct list_head *cache;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197 int ct;
2198};
2199
2200
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002201static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
2202 struct ipmr_mfc_iter *it, loff_t pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002204 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 struct mfc_cache *mfc;
2206
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 read_lock(&mrt_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00002208 for (it->ct = 0; it->ct < MFC_LINES; it->ct++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002209 it->cache = &mrt->mfc_cache_array[it->ct];
Patrick McHardy862465f2010-04-13 05:03:21 +00002210 list_for_each_entry(mfc, it->cache, list)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002211 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 return mfc;
Patrick McHardy862465f2010-04-13 05:03:21 +00002213 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214 read_unlock(&mrt_lock);
2215
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002217 it->cache = &mrt->mfc_unres_queue;
Patrick McHardy862465f2010-04-13 05:03:21 +00002218 list_for_each_entry(mfc, it->cache, list)
Patrick McHardye258beb2010-04-13 05:03:19 +00002219 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220 return mfc;
2221 spin_unlock_bh(&mfc_unres_lock);
2222
2223 it->cache = NULL;
2224 return NULL;
2225}
2226
2227
2228static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
2229{
2230 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002231 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002232 struct mr_table *mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002233
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002234 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2235 if (mrt == NULL)
2236 return ERR_PTR(-ENOENT);
2237
2238 it->mrt = mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239 it->cache = NULL;
2240 it->ct = 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002241 return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242 : SEQ_START_TOKEN;
2243}
2244
2245static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2246{
2247 struct mfc_cache *mfc = v;
2248 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002249 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002250 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251
2252 ++*pos;
2253
2254 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002255 return ipmr_mfc_seq_idx(net, seq->private, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256
Patrick McHardy862465f2010-04-13 05:03:21 +00002257 if (mfc->list.next != it->cache)
2258 return list_entry(mfc->list.next, struct mfc_cache, list);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002259
Patrick McHardy0c122952010-04-13 05:03:22 +00002260 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 goto end_of_list;
2262
Patrick McHardy0c122952010-04-13 05:03:22 +00002263 BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264
2265 while (++it->ct < MFC_LINES) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002266 it->cache = &mrt->mfc_cache_array[it->ct];
Patrick McHardy862465f2010-04-13 05:03:21 +00002267 if (list_empty(it->cache))
2268 continue;
2269 return list_first_entry(it->cache, struct mfc_cache, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 }
2271
2272 /* exhausted cache_array, show unresolved */
2273 read_unlock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002274 it->cache = &mrt->mfc_unres_queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 it->ct = 0;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002276
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00002278 if (!list_empty(it->cache))
2279 return list_first_entry(it->cache, struct mfc_cache, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
2281 end_of_list:
2282 spin_unlock_bh(&mfc_unres_lock);
2283 it->cache = NULL;
2284
2285 return NULL;
2286}
2287
2288static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
2289{
2290 struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002291 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292
Patrick McHardy0c122952010-04-13 05:03:22 +00002293 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 spin_unlock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002295 else if (it->cache == &mrt->mfc_cache_array[it->ct])
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 read_unlock(&mrt_lock);
2297}
2298
2299static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
2300{
2301 int n;
2302
2303 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002304 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 "Group Origin Iif Pkts Bytes Wrong Oifs\n");
2306 } else {
2307 const struct mfc_cache *mfc = v;
2308 const struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002309 const struct mr_table *mrt = it->mrt;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002310
Eric Dumazet0eae88f2010-04-20 19:06:52 -07002311 seq_printf(seq, "%08X %08X %-3hd",
2312 (__force u32) mfc->mfc_mcastgrp,
2313 (__force u32) mfc->mfc_origin,
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002314 mfc->mfc_parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315
Patrick McHardy0c122952010-04-13 05:03:22 +00002316 if (it->cache != &mrt->mfc_unres_queue) {
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002317 seq_printf(seq, " %8lu %8lu %8lu",
2318 mfc->mfc_un.res.pkt,
2319 mfc->mfc_un.res.bytes,
2320 mfc->mfc_un.res.wrong_if);
Stephen Hemminger132adf52007-03-08 20:44:43 -08002321 for (n = mfc->mfc_un.res.minvif;
2322 n < mfc->mfc_un.res.maxvif; n++ ) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002323 if (VIF_EXISTS(mrt, n) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +00002324 mfc->mfc_un.res.ttls[n] < 255)
2325 seq_printf(seq,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002326 " %2d:%-3d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 n, mfc->mfc_un.res.ttls[n]);
2328 }
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002329 } else {
2330 /* unresolved mfc_caches don't contain
2331 * pkt, bytes and wrong_if values
2332 */
2333 seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334 }
2335 seq_putc(seq, '\n');
2336 }
2337 return 0;
2338}
2339
Stephen Hemmingerf6908082007-03-12 14:34:29 -07002340static const struct seq_operations ipmr_mfc_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341 .start = ipmr_mfc_seq_start,
2342 .next = ipmr_mfc_seq_next,
2343 .stop = ipmr_mfc_seq_stop,
2344 .show = ipmr_mfc_seq_show,
2345};
2346
2347static int ipmr_mfc_open(struct inode *inode, struct file *file)
2348{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002349 return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
2350 sizeof(struct ipmr_mfc_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351}
2352
Arjan van de Ven9a321442007-02-12 00:55:35 -08002353static const struct file_operations ipmr_mfc_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 .owner = THIS_MODULE,
2355 .open = ipmr_mfc_open,
2356 .read = seq_read,
2357 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002358 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002360#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361
2362#ifdef CONFIG_IP_PIMSM_V2
Alexey Dobriyan32613092009-09-14 12:21:47 +00002363static const struct net_protocol pim_protocol = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364 .handler = pim_rcv,
Tom Goff403dbb92009-06-14 03:16:13 -07002365 .netns_ok = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366};
2367#endif
2368
2369
2370/*
2371 * Setup for IP multicast routing
2372 */
Benjamin Therycf958ae32009-01-22 04:56:16 +00002373static int __net_init ipmr_net_init(struct net *net)
2374{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002375 int err;
Benjamin Therycf958ae32009-01-22 04:56:16 +00002376
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002377 err = ipmr_rules_init(net);
2378 if (err < 0)
Benjamin Therycf958ae32009-01-22 04:56:16 +00002379 goto fail;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002380
2381#ifdef CONFIG_PROC_FS
2382 err = -ENOMEM;
2383 if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops))
2384 goto proc_vif_fail;
2385 if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops))
2386 goto proc_cache_fail;
2387#endif
Benjamin Thery2bb8b262009-01-22 04:56:18 +00002388 return 0;
2389
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002390#ifdef CONFIG_PROC_FS
2391proc_cache_fail:
2392 proc_net_remove(net, "ip_mr_vif");
2393proc_vif_fail:
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002394 ipmr_rules_exit(net);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002395#endif
Benjamin Therycf958ae32009-01-22 04:56:16 +00002396fail:
2397 return err;
2398}
2399
2400static void __net_exit ipmr_net_exit(struct net *net)
2401{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002402#ifdef CONFIG_PROC_FS
2403 proc_net_remove(net, "ip_mr_cache");
2404 proc_net_remove(net, "ip_mr_vif");
2405#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002406 ipmr_rules_exit(net);
Benjamin Therycf958ae32009-01-22 04:56:16 +00002407}
2408
2409static struct pernet_operations ipmr_net_ops = {
2410 .init = ipmr_net_init,
2411 .exit = ipmr_net_exit,
2412};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002413
Wang Chen03d2f892008-07-03 12:13:36 +08002414int __init ip_mr_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415{
Wang Chen03d2f892008-07-03 12:13:36 +08002416 int err;
2417
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418 mrt_cachep = kmem_cache_create("ip_mrt_cache",
2419 sizeof(struct mfc_cache),
Alexey Dobriyane5d679f332006-08-26 19:25:52 -07002420 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
Paul Mundt20c2df82007-07-20 10:11:58 +09002421 NULL);
Wang Chen03d2f892008-07-03 12:13:36 +08002422 if (!mrt_cachep)
2423 return -ENOMEM;
2424
Benjamin Therycf958ae32009-01-22 04:56:16 +00002425 err = register_pernet_subsys(&ipmr_net_ops);
2426 if (err)
2427 goto reg_pernet_fail;
2428
Wang Chen03d2f892008-07-03 12:13:36 +08002429 err = register_netdevice_notifier(&ip_mr_notifier);
2430 if (err)
2431 goto reg_notif_fail;
Tom Goff403dbb92009-06-14 03:16:13 -07002432#ifdef CONFIG_IP_PIMSM_V2
2433 if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) {
2434 printk(KERN_ERR "ip_mr_init: can't add PIM protocol\n");
2435 err = -EAGAIN;
2436 goto add_proto_fail;
2437 }
2438#endif
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002439 rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute);
Wang Chen03d2f892008-07-03 12:13:36 +08002440 return 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002441
Tom Goff403dbb92009-06-14 03:16:13 -07002442#ifdef CONFIG_IP_PIMSM_V2
2443add_proto_fail:
2444 unregister_netdevice_notifier(&ip_mr_notifier);
2445#endif
Benjamin Theryc3e38892008-11-19 14:07:41 -08002446reg_notif_fail:
Benjamin Therycf958ae32009-01-22 04:56:16 +00002447 unregister_pernet_subsys(&ipmr_net_ops);
2448reg_pernet_fail:
Benjamin Theryc3e38892008-11-19 14:07:41 -08002449 kmem_cache_destroy(mrt_cachep);
Wang Chen03d2f892008-07-03 12:13:36 +08002450 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451}