blob: cbb6dabe024f4ebadbe2fea414e9eb8434e64815 [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;
Eric Dumazet4c968702010-10-01 16:15:01 +000078 struct sock __rcu *mroute_sk;
Patrick McHardy0c122952010-04-13 05:03:22 +000079 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);
Ben Greeare40dbc52010-07-15 13:22:33 +0000445 if (err < 0) {
446 kfree_skb(skb);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000447 return err;
Ben Greeare40dbc52010-07-15 13:22:33 +0000448 }
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000449
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 read_lock(&mrt_lock);
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -0700451 dev->stats.tx_bytes += skb->len;
452 dev->stats.tx_packets++;
Patrick McHardy0c122952010-04-13 05:03:22 +0000453 ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 read_unlock(&mrt_lock);
455 kfree_skb(skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +0000456 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457}
458
Stephen Hemminger007c3832008-11-20 20:28:35 -0800459static const struct net_device_ops reg_vif_netdev_ops = {
460 .ndo_start_xmit = reg_vif_xmit,
461};
462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463static void reg_vif_setup(struct net_device *dev)
464{
465 dev->type = ARPHRD_PIMREG;
Kris Katterjohn46f25df2006-01-05 16:35:42 -0800466 dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 dev->flags = IFF_NOARP;
Stephen Hemminger007c3832008-11-20 20:28:35 -0800468 dev->netdev_ops = &reg_vif_netdev_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 dev->destructor = free_netdev;
Tom Goff403dbb92009-06-14 03:16:13 -0700470 dev->features |= NETIF_F_NETNS_LOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000473static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
475 struct net_device *dev;
476 struct in_device *in_dev;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000477 char name[IFNAMSIZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000479 if (mrt->id == RT_TABLE_DEFAULT)
480 sprintf(name, "pimreg");
481 else
482 sprintf(name, "pimreg%u", mrt->id);
483
484 dev = alloc_netdev(0, name, reg_vif_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 if (dev == NULL)
487 return NULL;
488
Tom Goff403dbb92009-06-14 03:16:13 -0700489 dev_net_set(dev, net);
490
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 if (register_netdevice(dev)) {
492 free_netdev(dev);
493 return NULL;
494 }
495 dev->iflink = 0;
496
Herbert Xu71e27da2007-06-04 23:36:06 -0700497 rcu_read_lock();
498 if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
499 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 goto failure;
Herbert Xu71e27da2007-06-04 23:36:06 -0700501 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Herbert Xu71e27da2007-06-04 23:36:06 -0700503 ipv4_devconf_setall(in_dev);
504 IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
505 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
507 if (dev_open(dev))
508 goto failure;
509
Wang Chen7dc00c82008-07-14 20:56:34 -0700510 dev_hold(dev);
511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 return dev;
513
514failure:
515 /* allow the register to be completed before unregistering. */
516 rtnl_unlock();
517 rtnl_lock();
518
519 unregister_netdevice(dev);
520 return NULL;
521}
522#endif
523
524/*
525 * Delete a VIF entry
Wang Chen7dc00c82008-07-14 20:56:34 -0700526 * @notify: Set to 1, if the caller is a notifier_call
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900528
Patrick McHardy0c122952010-04-13 05:03:22 +0000529static int vif_delete(struct mr_table *mrt, int vifi, int notify,
Eric Dumazetd17fa6f2009-10-28 05:21:38 +0000530 struct list_head *head)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{
532 struct vif_device *v;
533 struct net_device *dev;
534 struct in_device *in_dev;
535
Patrick McHardy0c122952010-04-13 05:03:22 +0000536 if (vifi < 0 || vifi >= mrt->maxvif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 return -EADDRNOTAVAIL;
538
Patrick McHardy0c122952010-04-13 05:03:22 +0000539 v = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541 write_lock_bh(&mrt_lock);
542 dev = v->dev;
543 v->dev = NULL;
544
545 if (!dev) {
546 write_unlock_bh(&mrt_lock);
547 return -EADDRNOTAVAIL;
548 }
549
550#ifdef CONFIG_IP_PIMSM
Patrick McHardy0c122952010-04-13 05:03:22 +0000551 if (vifi == mrt->mroute_reg_vif_num)
552 mrt->mroute_reg_vif_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553#endif
554
Patrick McHardy0c122952010-04-13 05:03:22 +0000555 if (vifi+1 == mrt->maxvif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 int tmp;
557 for (tmp=vifi-1; tmp>=0; tmp--) {
Patrick McHardy0c122952010-04-13 05:03:22 +0000558 if (VIF_EXISTS(mrt, tmp))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 break;
560 }
Patrick McHardy0c122952010-04-13 05:03:22 +0000561 mrt->maxvif = tmp+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 }
563
564 write_unlock_bh(&mrt_lock);
565
566 dev_set_allmulti(dev, -1);
567
Herbert Xue5ed6392005-10-03 14:35:55 -0700568 if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
Herbert Xu42f811b2007-06-04 23:34:44 -0700569 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 ip_rt_multicast_event(in_dev);
571 }
572
Wang Chen7dc00c82008-07-14 20:56:34 -0700573 if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER) && !notify)
Eric Dumazetd17fa6f2009-10-28 05:21:38 +0000574 unregister_netdevice_queue(dev, head);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
576 dev_put(dev);
577 return 0;
578}
579
Eric Dumazeta8c94862010-10-01 16:15:08 +0000580static void ipmr_cache_free_rcu(struct rcu_head *head)
581{
582 struct mfc_cache *c = container_of(head, struct mfc_cache, rcu);
583
584 kmem_cache_free(mrt_cachep, c);
585}
586
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000587static inline void ipmr_cache_free(struct mfc_cache *c)
588{
Eric Dumazeta8c94862010-10-01 16:15:08 +0000589 call_rcu(&c->rcu, ipmr_cache_free_rcu);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000590}
591
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592/* Destroy an unresolved cache entry, killing queued skbs
593 and reporting error to netlink readers.
594 */
595
Patrick McHardy0c122952010-04-13 05:03:22 +0000596static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597{
Patrick McHardy8de53df2010-04-15 13:29:28 +0200598 struct net *net = read_pnet(&mrt->net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700600 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
Patrick McHardy0c122952010-04-13 05:03:22 +0000602 atomic_dec(&mrt->cache_resolve_queue_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Jianjun Kongc354e122008-11-03 00:28:02 -0800604 while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700605 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
607 nlh->nlmsg_type = NLMSG_ERROR;
608 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
609 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700610 e = NLMSG_DATA(nlh);
611 e->error = -ETIMEDOUT;
612 memset(&e->msg, 0, sizeof(e->msg));
Thomas Graf2942e902006-08-15 00:30:25 -0700613
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000614 rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 } else
616 kfree_skb(skb);
617 }
618
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000619 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620}
621
622
Patrick McHardye258beb2010-04-13 05:03:19 +0000623/* Timer process for the unresolved queue. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
Patrick McHardye258beb2010-04-13 05:03:19 +0000625static void ipmr_expire_process(unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626{
Patrick McHardy0c122952010-04-13 05:03:22 +0000627 struct mr_table *mrt = (struct mr_table *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 unsigned long now;
629 unsigned long expires;
Patrick McHardy862465f2010-04-13 05:03:21 +0000630 struct mfc_cache *c, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
632 if (!spin_trylock(&mfc_unres_lock)) {
Patrick McHardy0c122952010-04-13 05:03:22 +0000633 mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 return;
635 }
636
Patrick McHardy0c122952010-04-13 05:03:22 +0000637 if (list_empty(&mrt->mfc_unres_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 goto out;
639
640 now = jiffies;
641 expires = 10*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Patrick McHardy0c122952010-04-13 05:03:22 +0000643 list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 if (time_after(c->mfc_un.unres.expires, now)) {
645 unsigned long interval = c->mfc_un.unres.expires - now;
646 if (interval < expires)
647 expires = interval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 continue;
649 }
650
Patrick McHardy862465f2010-04-13 05:03:21 +0000651 list_del(&c->list);
Patrick McHardy0c122952010-04-13 05:03:22 +0000652 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 }
654
Patrick McHardy0c122952010-04-13 05:03:22 +0000655 if (!list_empty(&mrt->mfc_unres_queue))
656 mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658out:
659 spin_unlock(&mfc_unres_lock);
660}
661
662/* Fill oifs list. It is called under write locked mrt_lock. */
663
Patrick McHardy0c122952010-04-13 05:03:22 +0000664static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000665 unsigned char *ttls)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666{
667 int vifi;
668
669 cache->mfc_un.res.minvif = MAXVIFS;
670 cache->mfc_un.res.maxvif = 0;
671 memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
672
Patrick McHardy0c122952010-04-13 05:03:22 +0000673 for (vifi = 0; vifi < mrt->maxvif; vifi++) {
674 if (VIF_EXISTS(mrt, vifi) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +0000675 ttls[vifi] && ttls[vifi] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 cache->mfc_un.res.ttls[vifi] = ttls[vifi];
677 if (cache->mfc_un.res.minvif > vifi)
678 cache->mfc_un.res.minvif = vifi;
679 if (cache->mfc_un.res.maxvif <= vifi)
680 cache->mfc_un.res.maxvif = vifi + 1;
681 }
682 }
683}
684
Patrick McHardy0c122952010-04-13 05:03:22 +0000685static int vif_add(struct net *net, struct mr_table *mrt,
686 struct vifctl *vifc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
688 int vifi = vifc->vifc_vifi;
Patrick McHardy0c122952010-04-13 05:03:22 +0000689 struct vif_device *v = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 struct net_device *dev;
691 struct in_device *in_dev;
Wang Chend6070322008-07-14 20:55:26 -0700692 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
694 /* Is vif busy ? */
Patrick McHardy0c122952010-04-13 05:03:22 +0000695 if (VIF_EXISTS(mrt, vifi))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 return -EADDRINUSE;
697
698 switch (vifc->vifc_flags) {
699#ifdef CONFIG_IP_PIMSM
700 case VIFF_REGISTER:
701 /*
702 * Special Purpose VIF in PIM
703 * All the packets will be sent to the daemon
704 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000705 if (mrt->mroute_reg_vif_num >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 return -EADDRINUSE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000707 dev = ipmr_reg_vif(net, mrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 if (!dev)
709 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700710 err = dev_set_allmulti(dev, 1);
711 if (err) {
712 unregister_netdevice(dev);
Wang Chen7dc00c82008-07-14 20:56:34 -0700713 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700714 return err;
715 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 break;
717#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900718 case VIFF_TUNNEL:
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000719 dev = ipmr_new_tunnel(net, vifc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 if (!dev)
721 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700722 err = dev_set_allmulti(dev, 1);
723 if (err) {
724 ipmr_del_tunnel(dev, vifc);
Wang Chen7dc00c82008-07-14 20:56:34 -0700725 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700726 return err;
727 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 break;
Ilia Kee5e81f2009-09-16 05:53:07 +0000729
730 case VIFF_USE_IFINDEX:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 case 0:
Ilia Kee5e81f2009-09-16 05:53:07 +0000732 if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
733 dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex);
Eric Dumazet95ae6b22010-09-15 04:04:31 +0000734 if (dev && __in_dev_get_rtnl(dev) == NULL) {
Ilia Kee5e81f2009-09-16 05:53:07 +0000735 dev_put(dev);
736 return -EADDRNOTAVAIL;
737 }
738 } else
739 dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 if (!dev)
742 return -EADDRNOTAVAIL;
Wang Chend6070322008-07-14 20:55:26 -0700743 err = dev_set_allmulti(dev, 1);
Wang Chen7dc00c82008-07-14 20:56:34 -0700744 if (err) {
745 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700746 return err;
Wang Chen7dc00c82008-07-14 20:56:34 -0700747 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 break;
749 default:
750 return -EINVAL;
751 }
752
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000753 if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) {
754 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 return -EADDRNOTAVAIL;
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000756 }
Herbert Xu42f811b2007-06-04 23:34:44 -0700757 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 ip_rt_multicast_event(in_dev);
759
760 /*
761 * Fill in the VIF structures
762 */
Jianjun Kongc354e122008-11-03 00:28:02 -0800763 v->rate_limit = vifc->vifc_rate_limit;
764 v->local = vifc->vifc_lcl_addr.s_addr;
765 v->remote = vifc->vifc_rmt_addr.s_addr;
766 v->flags = vifc->vifc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 if (!mrtsock)
768 v->flags |= VIFF_STATIC;
Jianjun Kongc354e122008-11-03 00:28:02 -0800769 v->threshold = vifc->vifc_threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 v->bytes_in = 0;
771 v->bytes_out = 0;
772 v->pkt_in = 0;
773 v->pkt_out = 0;
774 v->link = dev->ifindex;
775 if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))
776 v->link = dev->iflink;
777
778 /* And finish update writing critical data */
779 write_lock_bh(&mrt_lock);
Jianjun Kongc354e122008-11-03 00:28:02 -0800780 v->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781#ifdef CONFIG_IP_PIMSM
782 if (v->flags&VIFF_REGISTER)
Patrick McHardy0c122952010-04-13 05:03:22 +0000783 mrt->mroute_reg_vif_num = vifi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784#endif
Patrick McHardy0c122952010-04-13 05:03:22 +0000785 if (vifi+1 > mrt->maxvif)
786 mrt->maxvif = vifi+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 write_unlock_bh(&mrt_lock);
788 return 0;
789}
790
Eric Dumazeta8c94862010-10-01 16:15:08 +0000791/* called with rcu_read_lock() */
Patrick McHardy0c122952010-04-13 05:03:22 +0000792static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000793 __be32 origin,
794 __be32 mcastgrp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
Jianjun Kongc354e122008-11-03 00:28:02 -0800796 int line = MFC_HASH(mcastgrp, origin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 struct mfc_cache *c;
798
Eric Dumazeta8c94862010-10-01 16:15:08 +0000799 list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) {
Patrick McHardy862465f2010-04-13 05:03:21 +0000800 if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp)
801 return c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 }
Patrick McHardy862465f2010-04-13 05:03:21 +0000803 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804}
805
806/*
807 * Allocate a multicast cache entry
808 */
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000809static struct mfc_cache *ipmr_cache_alloc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
Jianjun Kongc354e122008-11-03 00:28:02 -0800811 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
Eric Dumazeta8c94862010-10-01 16:15:08 +0000812
813 if (c)
814 c->mfc_un.res.minvif = MAXVIFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 return c;
816}
817
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000818static struct mfc_cache *ipmr_cache_alloc_unres(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Jianjun Kongc354e122008-11-03 00:28:02 -0800820 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
Eric Dumazeta8c94862010-10-01 16:15:08 +0000821
822 if (c) {
823 skb_queue_head_init(&c->mfc_un.unres.unresolved);
824 c->mfc_un.unres.expires = jiffies + 10*HZ;
825 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 return c;
827}
828
829/*
830 * A cache entry has gone into a resolved state from queued
831 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900832
Patrick McHardy0c122952010-04-13 05:03:22 +0000833static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
834 struct mfc_cache *uc, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835{
836 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700837 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
839 /*
840 * Play the pending entries through our router
841 */
842
Jianjun Kongc354e122008-11-03 00:28:02 -0800843 while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700844 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
846
Patrick McHardycb6a4e42010-04-26 16:02:08 +0200847 if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700848 nlh->nlmsg_len = (skb_tail_pointer(skb) -
849 (u8 *)nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 } else {
851 nlh->nlmsg_type = NLMSG_ERROR;
852 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
853 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700854 e = NLMSG_DATA(nlh);
855 e->error = -EMSGSIZE;
856 memset(&e->msg, 0, sizeof(e->msg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 }
Thomas Graf2942e902006-08-15 00:30:25 -0700858
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000859 rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 } else
Patrick McHardy0c122952010-04-13 05:03:22 +0000861 ip_mr_forward(net, mrt, skb, c, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 }
863}
864
865/*
866 * Bounce a cache query up to mrouted. We could use netlink for this but mrouted
867 * expects the following bizarre scheme.
868 *
869 * Called under mrt_lock.
870 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900871
Patrick McHardy0c122952010-04-13 05:03:22 +0000872static int ipmr_cache_report(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000873 struct sk_buff *pkt, vifi_t vifi, int assert)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874{
875 struct sk_buff *skb;
Arnaldo Carvalho de Meloc9bdd4b2007-03-12 20:09:15 -0300876 const int ihl = ip_hdrlen(pkt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 struct igmphdr *igmp;
878 struct igmpmsg *msg;
Eric Dumazet4c968702010-10-01 16:15:01 +0000879 struct sock *mroute_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 int ret;
881
882#ifdef CONFIG_IP_PIMSM
883 if (assert == IGMPMSG_WHOLEPKT)
884 skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
885 else
886#endif
887 skb = alloc_skb(128, GFP_ATOMIC);
888
Stephen Hemminger132adf52007-03-08 20:44:43 -0800889 if (!skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 return -ENOBUFS;
891
892#ifdef CONFIG_IP_PIMSM
893 if (assert == IGMPMSG_WHOLEPKT) {
894 /* Ugly, but we have no choice with this interface.
895 Duplicate old header, fix ihl, length etc.
896 And all this only to mangle msg->im_msgtype and
897 to set msg->im_mbz to "mbz" :-)
898 */
Arnaldo Carvalho de Melo878c8142007-03-11 22:38:29 -0300899 skb_push(skb, sizeof(struct iphdr));
900 skb_reset_network_header(skb);
Arnaldo Carvalho de Melobadff6d2007-03-13 13:06:52 -0300901 skb_reset_transport_header(skb);
Arnaldo Carvalho de Melo0272ffc2007-03-12 20:05:39 -0300902 msg = (struct igmpmsg *)skb_network_header(skb);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700903 memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 msg->im_msgtype = IGMPMSG_WHOLEPKT;
905 msg->im_mbz = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +0000906 msg->im_vif = mrt->mroute_reg_vif_num;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700907 ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
908 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
909 sizeof(struct iphdr));
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900910 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900912 {
913
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 /*
915 * Copy the IP header
916 */
917
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700918 skb->network_header = skb->tail;
Arnaldo Carvalho de Meloddc7b8e2007-03-15 21:42:27 -0300919 skb_put(skb, ihl);
Arnaldo Carvalho de Melo27d7ff42007-03-31 11:55:19 -0300920 skb_copy_to_linear_data(skb, pkt->data, ihl);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700921 ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
922 msg = (struct igmpmsg *)skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 msg->im_vif = vifi;
Eric Dumazetadf30902009-06-02 05:19:30 +0000924 skb_dst_set(skb, dst_clone(skb_dst(pkt)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
926 /*
927 * Add our header
928 */
929
Jianjun Kongc354e122008-11-03 00:28:02 -0800930 igmp=(struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 igmp->type =
932 msg->im_msgtype = assert;
933 igmp->code = 0;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700934 ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700935 skb->transport_header = skb->network_header;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900936 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
Eric Dumazet4c968702010-10-01 16:15:01 +0000938 rcu_read_lock();
939 mroute_sk = rcu_dereference(mrt->mroute_sk);
940 if (mroute_sk == NULL) {
941 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 kfree_skb(skb);
943 return -EINVAL;
944 }
945
946 /*
947 * Deliver to mrouted
948 */
Eric Dumazet4c968702010-10-01 16:15:01 +0000949 ret = sock_queue_rcv_skb(mroute_sk, skb);
950 rcu_read_unlock();
Benjamin Thery70a269e2009-01-22 04:56:15 +0000951 if (ret < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 if (net_ratelimit())
953 printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
954 kfree_skb(skb);
955 }
956
957 return ret;
958}
959
960/*
961 * Queue a packet for resolution. It gets locked cache entry!
962 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900963
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964static int
Patrick McHardy0c122952010-04-13 05:03:22 +0000965ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966{
Patrick McHardy862465f2010-04-13 05:03:21 +0000967 bool found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 int err;
969 struct mfc_cache *c;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700970 const struct iphdr *iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971
972 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +0000973 list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +0000974 if (c->mfc_mcastgrp == iph->daddr &&
Patrick McHardy862465f2010-04-13 05:03:21 +0000975 c->mfc_origin == iph->saddr) {
976 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 break;
Patrick McHardy862465f2010-04-13 05:03:21 +0000978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 }
980
Patrick McHardy862465f2010-04-13 05:03:21 +0000981 if (!found) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 /*
983 * Create a new entry if allowable
984 */
985
Patrick McHardy0c122952010-04-13 05:03:22 +0000986 if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000987 (c = ipmr_cache_alloc_unres()) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 spin_unlock_bh(&mfc_unres_lock);
989
990 kfree_skb(skb);
991 return -ENOBUFS;
992 }
993
994 /*
995 * Fill in the new cache entry
996 */
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700997 c->mfc_parent = -1;
998 c->mfc_origin = iph->saddr;
999 c->mfc_mcastgrp = iph->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
1001 /*
1002 * Reflect first query at mrouted.
1003 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001004 err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001005 if (err < 0) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001006 /* If the report failed throw the cache entry
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 out - Brad Parker
1008 */
1009 spin_unlock_bh(&mfc_unres_lock);
1010
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001011 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 kfree_skb(skb);
1013 return err;
1014 }
1015
Patrick McHardy0c122952010-04-13 05:03:22 +00001016 atomic_inc(&mrt->cache_resolve_queue_len);
1017 list_add(&c->list, &mrt->mfc_unres_queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
David S. Miller278554b2010-05-12 00:05:35 -07001019 if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
1020 mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 }
1022
1023 /*
1024 * See if we can append the packet
1025 */
1026 if (c->mfc_un.unres.unresolved.qlen>3) {
1027 kfree_skb(skb);
1028 err = -ENOBUFS;
1029 } else {
Jianjun Kongc354e122008-11-03 00:28:02 -08001030 skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 err = 0;
1032 }
1033
1034 spin_unlock_bh(&mfc_unres_lock);
1035 return err;
1036}
1037
1038/*
1039 * MFC cache manipulation by user space mroute daemon
1040 */
1041
Patrick McHardy0c122952010-04-13 05:03:22 +00001042static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043{
1044 int line;
Patrick McHardy862465f2010-04-13 05:03:21 +00001045 struct mfc_cache *c, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
Jianjun Kongc354e122008-11-03 00:28:02 -08001047 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Patrick McHardy0c122952010-04-13 05:03:22 +00001049 list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
1051 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
Eric Dumazeta8c94862010-10-01 16:15:08 +00001052 list_del_rcu(&c->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001054 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 return 0;
1056 }
1057 }
1058 return -ENOENT;
1059}
1060
Patrick McHardy0c122952010-04-13 05:03:22 +00001061static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
1062 struct mfcctl *mfc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063{
Patrick McHardy862465f2010-04-13 05:03:21 +00001064 bool found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 int line;
Patrick McHardy862465f2010-04-13 05:03:21 +00001066 struct mfc_cache *uc, *c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
Patrick McHardya50436f22010-03-17 06:04:14 +00001068 if (mfc->mfcc_parent >= MAXVIFS)
1069 return -ENFILE;
1070
Jianjun Kongc354e122008-11-03 00:28:02 -08001071 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072
Patrick McHardy0c122952010-04-13 05:03:22 +00001073 list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
Patrick McHardy862465f2010-04-13 05:03:21 +00001075 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
1076 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 break;
Patrick McHardy862465f2010-04-13 05:03:21 +00001078 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 }
1080
Patrick McHardy862465f2010-04-13 05:03:21 +00001081 if (found) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 write_lock_bh(&mrt_lock);
1083 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001084 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 if (!mrtsock)
1086 c->mfc_flags |= MFC_STATIC;
1087 write_unlock_bh(&mrt_lock);
1088 return 0;
1089 }
1090
Joe Perchesf97c1e02007-12-16 13:45:43 -08001091 if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 return -EINVAL;
1093
Patrick McHardyd658f8a2010-04-13 05:03:20 +00001094 c = ipmr_cache_alloc();
Jianjun Kongc354e122008-11-03 00:28:02 -08001095 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 return -ENOMEM;
1097
Jianjun Kongc354e122008-11-03 00:28:02 -08001098 c->mfc_origin = mfc->mfcc_origin.s_addr;
1099 c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
1100 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001101 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 if (!mrtsock)
1103 c->mfc_flags |= MFC_STATIC;
1104
Eric Dumazeta8c94862010-10-01 16:15:08 +00001105 list_add_rcu(&c->list, &mrt->mfc_cache_array[line]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106
1107 /*
1108 * Check to see if we resolved a queued list. If so we
1109 * need to send on the frames and tidy up.
1110 */
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001111 found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001113 list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +00001114 if (uc->mfc_origin == c->mfc_origin &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 uc->mfc_mcastgrp == c->mfc_mcastgrp) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001116 list_del(&uc->list);
Patrick McHardy0c122952010-04-13 05:03:22 +00001117 atomic_dec(&mrt->cache_resolve_queue_len);
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001118 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 break;
1120 }
1121 }
Patrick McHardy0c122952010-04-13 05:03:22 +00001122 if (list_empty(&mrt->mfc_unres_queue))
1123 del_timer(&mrt->ipmr_expire_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 spin_unlock_bh(&mfc_unres_lock);
1125
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001126 if (found) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001127 ipmr_cache_resolve(net, mrt, uc, c);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001128 ipmr_cache_free(uc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 }
1130 return 0;
1131}
1132
1133/*
1134 * Close the multicast socket, and clear the vif tables etc
1135 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001136
Patrick McHardy0c122952010-04-13 05:03:22 +00001137static void mroute_clean_tables(struct mr_table *mrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138{
1139 int i;
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001140 LIST_HEAD(list);
Patrick McHardy862465f2010-04-13 05:03:21 +00001141 struct mfc_cache *c, *next;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001142
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 /*
1144 * Shut down all active vif entries
1145 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001146 for (i = 0; i < mrt->maxvif; i++) {
1147 if (!(mrt->vif_table[i].flags&VIFF_STATIC))
1148 vif_delete(mrt, i, 0, &list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 }
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001150 unregister_netdevice_many(&list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
1152 /*
1153 * Wipe the cache
1154 */
Patrick McHardy862465f2010-04-13 05:03:21 +00001155 for (i = 0; i < MFC_LINES; i++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001156 list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
Eric Dumazeta8c94862010-10-01 16:15:08 +00001157 if (c->mfc_flags & MFC_STATIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 continue;
Eric Dumazeta8c94862010-10-01 16:15:08 +00001159 list_del_rcu(&c->list);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001160 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 }
1162 }
1163
Patrick McHardy0c122952010-04-13 05:03:22 +00001164 if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001166 list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001167 list_del(&c->list);
Patrick McHardy0c122952010-04-13 05:03:22 +00001168 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 }
1170 spin_unlock_bh(&mfc_unres_lock);
1171 }
1172}
1173
Eric Dumazet4c968702010-10-01 16:15:01 +00001174/* called from ip_ra_control(), before an RCU grace period,
1175 * we dont need to call synchronize_rcu() here
1176 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177static void mrtsock_destruct(struct sock *sk)
1178{
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001179 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001180 struct mr_table *mrt;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001181
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 rtnl_lock();
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001183 ipmr_for_each_table(mrt, net) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001184 if (sk == rtnl_dereference(mrt->mroute_sk)) {
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001185 IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
Eric Dumazet4c968702010-10-01 16:15:01 +00001186 rcu_assign_pointer(mrt->mroute_sk, NULL);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001187 mroute_clean_tables(mrt);
1188 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 }
1190 rtnl_unlock();
1191}
1192
1193/*
1194 * Socket options and virtual interface manipulation. The whole
1195 * virtual interface system is a complete heap, but unfortunately
1196 * that's how BSD mrouted happens to think. Maybe one day with a proper
1197 * MOSPF/PIM router set up we can clean this up.
1198 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001199
David S. Millerb7058842009-09-30 16:12:20 -07001200int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201{
1202 int ret;
1203 struct vifctl vif;
1204 struct mfcctl mfc;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001205 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001206 struct mr_table *mrt;
1207
1208 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1209 if (mrt == NULL)
1210 return -ENOENT;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001211
Stephen Hemminger132adf52007-03-08 20:44:43 -08001212 if (optname != MRT_INIT) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001213 if (sk != rcu_dereference_raw(mrt->mroute_sk) &&
1214 !capable(CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 return -EACCES;
1216 }
1217
Stephen Hemminger132adf52007-03-08 20:44:43 -08001218 switch (optname) {
1219 case MRT_INIT:
1220 if (sk->sk_type != SOCK_RAW ||
Eric Dumazetc720c7e2009-10-15 06:30:45 +00001221 inet_sk(sk)->inet_num != IPPROTO_IGMP)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001222 return -EOPNOTSUPP;
Jianjun Kongc354e122008-11-03 00:28:02 -08001223 if (optlen != sizeof(int))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001224 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
Stephen Hemminger132adf52007-03-08 20:44:43 -08001226 rtnl_lock();
Eric Dumazet4c968702010-10-01 16:15:01 +00001227 if (rtnl_dereference(mrt->mroute_sk)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 rtnl_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -08001229 return -EADDRINUSE;
1230 }
1231
1232 ret = ip_ra_control(sk, 1, mrtsock_destruct);
1233 if (ret == 0) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001234 rcu_assign_pointer(mrt->mroute_sk, sk);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001235 IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001236 }
1237 rtnl_unlock();
1238 return ret;
1239 case MRT_DONE:
Eric Dumazet4c968702010-10-01 16:15:01 +00001240 if (sk != rcu_dereference_raw(mrt->mroute_sk))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001241 return -EACCES;
1242 return ip_ra_control(sk, 0, NULL);
1243 case MRT_ADD_VIF:
1244 case MRT_DEL_VIF:
Jianjun Kongc354e122008-11-03 00:28:02 -08001245 if (optlen != sizeof(vif))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001246 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -08001247 if (copy_from_user(&vif, optval, sizeof(vif)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001248 return -EFAULT;
1249 if (vif.vifc_vifi >= MAXVIFS)
1250 return -ENFILE;
1251 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -08001252 if (optname == MRT_ADD_VIF) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001253 ret = vif_add(net, mrt, &vif,
1254 sk == rtnl_dereference(mrt->mroute_sk));
Stephen Hemminger132adf52007-03-08 20:44:43 -08001255 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00001256 ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001257 }
1258 rtnl_unlock();
1259 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
1261 /*
1262 * Manipulate the forwarding caches. These live
1263 * in a sort of kernel/user symbiosis.
1264 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001265 case MRT_ADD_MFC:
1266 case MRT_DEL_MFC:
Jianjun Kongc354e122008-11-03 00:28:02 -08001267 if (optlen != sizeof(mfc))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001268 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -08001269 if (copy_from_user(&mfc, optval, sizeof(mfc)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001270 return -EFAULT;
1271 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -08001272 if (optname == MRT_DEL_MFC)
Patrick McHardy0c122952010-04-13 05:03:22 +00001273 ret = ipmr_mfc_delete(mrt, &mfc);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001274 else
Eric Dumazet4c968702010-10-01 16:15:01 +00001275 ret = ipmr_mfc_add(net, mrt, &mfc,
1276 sk == rtnl_dereference(mrt->mroute_sk));
Stephen Hemminger132adf52007-03-08 20:44:43 -08001277 rtnl_unlock();
1278 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 /*
1280 * Control PIM assert.
1281 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001282 case MRT_ASSERT:
1283 {
1284 int v;
1285 if (get_user(v,(int __user *)optval))
1286 return -EFAULT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001287 mrt->mroute_do_assert = (v) ? 1 : 0;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001288 return 0;
1289 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290#ifdef CONFIG_IP_PIMSM
Stephen Hemminger132adf52007-03-08 20:44:43 -08001291 case MRT_PIM:
1292 {
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001293 int v;
1294
Stephen Hemminger132adf52007-03-08 20:44:43 -08001295 if (get_user(v,(int __user *)optval))
1296 return -EFAULT;
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001297 v = (v) ? 1 : 0;
1298
Stephen Hemminger132adf52007-03-08 20:44:43 -08001299 rtnl_lock();
1300 ret = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +00001301 if (v != mrt->mroute_do_pim) {
1302 mrt->mroute_do_pim = v;
1303 mrt->mroute_do_assert = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 }
Stephen Hemminger132adf52007-03-08 20:44:43 -08001305 rtnl_unlock();
1306 return ret;
1307 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001309#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
1310 case MRT_TABLE:
1311 {
1312 u32 v;
1313
1314 if (optlen != sizeof(u32))
1315 return -EINVAL;
1316 if (get_user(v, (u32 __user *)optval))
1317 return -EFAULT;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001318
1319 rtnl_lock();
1320 ret = 0;
Eric Dumazet4c968702010-10-01 16:15:01 +00001321 if (sk == rtnl_dereference(mrt->mroute_sk)) {
1322 ret = -EBUSY;
1323 } else {
1324 if (!ipmr_new_table(net, v))
1325 ret = -ENOMEM;
1326 raw_sk(sk)->ipmr_table = v;
1327 }
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001328 rtnl_unlock();
1329 return ret;
1330 }
1331#endif
Stephen Hemminger132adf52007-03-08 20:44:43 -08001332 /*
1333 * Spurious command, or MRT_VERSION which you cannot
1334 * set.
1335 */
1336 default:
1337 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 }
1339}
1340
1341/*
1342 * Getsock opt support for the multicast routing system.
1343 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001344
Jianjun Kongc354e122008-11-03 00:28:02 -08001345int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346{
1347 int olr;
1348 int val;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001349 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001350 struct mr_table *mrt;
1351
1352 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1353 if (mrt == NULL)
1354 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
Jianjun Kongc354e122008-11-03 00:28:02 -08001356 if (optname != MRT_VERSION &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357#ifdef CONFIG_IP_PIMSM
1358 optname!=MRT_PIM &&
1359#endif
1360 optname!=MRT_ASSERT)
1361 return -ENOPROTOOPT;
1362
1363 if (get_user(olr, optlen))
1364 return -EFAULT;
1365
1366 olr = min_t(unsigned int, olr, sizeof(int));
1367 if (olr < 0)
1368 return -EINVAL;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001369
Jianjun Kongc354e122008-11-03 00:28:02 -08001370 if (put_user(olr, optlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 return -EFAULT;
Jianjun Kongc354e122008-11-03 00:28:02 -08001372 if (optname == MRT_VERSION)
1373 val = 0x0305;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374#ifdef CONFIG_IP_PIMSM
Jianjun Kongc354e122008-11-03 00:28:02 -08001375 else if (optname == MRT_PIM)
Patrick McHardy0c122952010-04-13 05:03:22 +00001376 val = mrt->mroute_do_pim;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377#endif
1378 else
Patrick McHardy0c122952010-04-13 05:03:22 +00001379 val = mrt->mroute_do_assert;
Jianjun Kongc354e122008-11-03 00:28:02 -08001380 if (copy_to_user(optval, &val, olr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 return -EFAULT;
1382 return 0;
1383}
1384
1385/*
1386 * The IP multicast ioctl support routines.
1387 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001388
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
1390{
1391 struct sioc_sg_req sr;
1392 struct sioc_vif_req vr;
1393 struct vif_device *vif;
1394 struct mfc_cache *c;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001395 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001396 struct mr_table *mrt;
1397
1398 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1399 if (mrt == NULL)
1400 return -ENOENT;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001401
Stephen Hemminger132adf52007-03-08 20:44:43 -08001402 switch (cmd) {
1403 case SIOCGETVIFCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001404 if (copy_from_user(&vr, arg, sizeof(vr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001405 return -EFAULT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001406 if (vr.vifi >= mrt->maxvif)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001407 return -EINVAL;
1408 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001409 vif = &mrt->vif_table[vr.vifi];
1410 if (VIF_EXISTS(mrt, vr.vifi)) {
Jianjun Kongc354e122008-11-03 00:28:02 -08001411 vr.icount = vif->pkt_in;
1412 vr.ocount = vif->pkt_out;
1413 vr.ibytes = vif->bytes_in;
1414 vr.obytes = vif->bytes_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001416
Jianjun Kongc354e122008-11-03 00:28:02 -08001417 if (copy_to_user(arg, &vr, sizeof(vr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 return -EFAULT;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001419 return 0;
1420 }
1421 read_unlock(&mrt_lock);
1422 return -EADDRNOTAVAIL;
1423 case SIOCGETSGCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001424 if (copy_from_user(&sr, arg, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001425 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426
Eric Dumazeta8c94862010-10-01 16:15:08 +00001427 rcu_read_lock();
Patrick McHardy0c122952010-04-13 05:03:22 +00001428 c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001429 if (c) {
1430 sr.pktcnt = c->mfc_un.res.pkt;
1431 sr.bytecnt = c->mfc_un.res.bytes;
1432 sr.wrong_if = c->mfc_un.res.wrong_if;
Eric Dumazeta8c94862010-10-01 16:15:08 +00001433 rcu_read_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -08001434
Jianjun Kongc354e122008-11-03 00:28:02 -08001435 if (copy_to_user(arg, &sr, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001436 return -EFAULT;
1437 return 0;
1438 }
Eric Dumazeta8c94862010-10-01 16:15:08 +00001439 rcu_read_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -08001440 return -EADDRNOTAVAIL;
1441 default:
1442 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 }
1444}
1445
1446
1447static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
1448{
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001449 struct net_device *dev = ptr;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001450 struct net *net = dev_net(dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001451 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 struct vif_device *v;
1453 int ct;
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001454 LIST_HEAD(list);
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001455
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 if (event != NETDEV_UNREGISTER)
1457 return NOTIFY_DONE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001458
1459 ipmr_for_each_table(mrt, net) {
1460 v = &mrt->vif_table[0];
1461 for (ct = 0; ct < mrt->maxvif; ct++, v++) {
1462 if (v->dev == dev)
1463 vif_delete(mrt, ct, 1, &list);
1464 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 }
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001466 unregister_netdevice_many(&list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 return NOTIFY_DONE;
1468}
1469
1470
Jianjun Kongc354e122008-11-03 00:28:02 -08001471static struct notifier_block ip_mr_notifier = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 .notifier_call = ipmr_device_event,
1473};
1474
1475/*
1476 * Encapsulate a packet by attaching a valid IPIP header to it.
1477 * This avoids tunnel drivers and other mess and gives us the speed so
1478 * important for multicast video.
1479 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001480
Al Viro114c7842006-09-27 18:39:29 -07001481static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482{
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001483 struct iphdr *iph;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001484 struct iphdr *old_iph = ip_hdr(skb);
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001485
1486 skb_push(skb, sizeof(struct iphdr));
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001487 skb->transport_header = skb->network_header;
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001488 skb_reset_network_header(skb);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001489 iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
1491 iph->version = 4;
Arnaldo Carvalho de Meloe023dd62007-03-12 20:09:36 -03001492 iph->tos = old_iph->tos;
1493 iph->ttl = old_iph->ttl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 iph->frag_off = 0;
1495 iph->daddr = daddr;
1496 iph->saddr = saddr;
1497 iph->protocol = IPPROTO_IPIP;
1498 iph->ihl = 5;
1499 iph->tot_len = htons(skb->len);
Eric Dumazetadf30902009-06-02 05:19:30 +00001500 ip_select_ident(iph, skb_dst(skb), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 ip_send_check(iph);
1502
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
1504 nf_reset(skb);
1505}
1506
1507static inline int ipmr_forward_finish(struct sk_buff *skb)
1508{
1509 struct ip_options * opt = &(IPCB(skb)->opt);
1510
Eric Dumazetadf30902009-06-02 05:19:30 +00001511 IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
1513 if (unlikely(opt->optlen))
1514 ip_forward_options(skb);
1515
1516 return dst_output(skb);
1517}
1518
1519/*
1520 * Processing handlers for ipmr_forward
1521 */
1522
Patrick McHardy0c122952010-04-13 05:03:22 +00001523static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
1524 struct sk_buff *skb, struct mfc_cache *c, int vifi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525{
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001526 const struct iphdr *iph = ip_hdr(skb);
Patrick McHardy0c122952010-04-13 05:03:22 +00001527 struct vif_device *vif = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 struct net_device *dev;
1529 struct rtable *rt;
1530 int encap = 0;
1531
1532 if (vif->dev == NULL)
1533 goto out_free;
1534
1535#ifdef CONFIG_IP_PIMSM
1536 if (vif->flags & VIFF_REGISTER) {
1537 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001538 vif->bytes_out += skb->len;
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -07001539 vif->dev->stats.tx_bytes += skb->len;
1540 vif->dev->stats.tx_packets++;
Patrick McHardy0c122952010-04-13 05:03:22 +00001541 ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
Ilpo Järvinen69ebbf52009-02-06 23:46:51 -08001542 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 }
1544#endif
1545
1546 if (vif->flags&VIFF_TUNNEL) {
1547 struct flowi fl = { .oif = vif->link,
1548 .nl_u = { .ip4_u =
1549 { .daddr = vif->remote,
1550 .saddr = vif->local,
1551 .tos = RT_TOS(iph->tos) } },
1552 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001553 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 goto out_free;
1555 encap = sizeof(struct iphdr);
1556 } else {
1557 struct flowi fl = { .oif = vif->link,
1558 .nl_u = { .ip4_u =
1559 { .daddr = iph->daddr,
1560 .tos = RT_TOS(iph->tos) } },
1561 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001562 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 goto out_free;
1564 }
1565
Changli Gaod8d1f302010-06-10 23:31:35 -07001566 dev = rt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
Changli Gaod8d1f302010-06-10 23:31:35 -07001568 if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 /* Do not fragment multicasts. Alas, IPv4 does not
1570 allow to send ICMP, so that packets will disappear
1571 to blackhole.
1572 */
1573
Pavel Emelyanov7c73a6f2008-07-16 20:20:11 -07001574 IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 ip_rt_put(rt);
1576 goto out_free;
1577 }
1578
Changli Gaod8d1f302010-06-10 23:31:35 -07001579 encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
1581 if (skb_cow(skb, encap)) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001582 ip_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 goto out_free;
1584 }
1585
1586 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001587 vif->bytes_out += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
Eric Dumazetadf30902009-06-02 05:19:30 +00001589 skb_dst_drop(skb);
Changli Gaod8d1f302010-06-10 23:31:35 -07001590 skb_dst_set(skb, &rt->dst);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001591 ip_decrease_ttl(ip_hdr(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 /* FIXME: forward and output firewalls used to be called here.
1594 * What do we do with netfilter? -- RR */
1595 if (vif->flags & VIFF_TUNNEL) {
1596 ip_encap(skb, vif->local, vif->remote);
1597 /* FIXME: extra output firewall step used to be here. --RR */
Pavel Emelyanov2f4c02d2008-05-21 14:16:14 -07001598 vif->dev->stats.tx_packets++;
1599 vif->dev->stats.tx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 }
1601
1602 IPCB(skb)->flags |= IPSKB_FORWARDED;
1603
1604 /*
1605 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
1606 * not only before forwarding, but after forwarding on all output
1607 * interfaces. It is clear, if mrouter runs a multicasting
1608 * program, it should receive packets not depending to what interface
1609 * program is joined.
1610 * If we will not make it, the program will have to join on all
1611 * interfaces. On the other hand, multihoming host (or router, but
1612 * not mrouter) cannot join to more than one interface - it will
1613 * result in receiving multiple packets.
1614 */
Jan Engelhardt9bbc7682010-03-23 04:07:29 +01001615 NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 ipmr_forward_finish);
1617 return;
1618
1619out_free:
1620 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621}
1622
Patrick McHardy0c122952010-04-13 05:03:22 +00001623static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624{
1625 int ct;
Patrick McHardy0c122952010-04-13 05:03:22 +00001626
1627 for (ct = mrt->maxvif-1; ct >= 0; ct--) {
1628 if (mrt->vif_table[ct].dev == dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 break;
1630 }
1631 return ct;
1632}
1633
1634/* "local" means that we should preserve one skb (for local delivery) */
1635
Patrick McHardy0c122952010-04-13 05:03:22 +00001636static int ip_mr_forward(struct net *net, struct mr_table *mrt,
1637 struct sk_buff *skb, struct mfc_cache *cache,
1638 int local)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
1640 int psend = -1;
1641 int vif, ct;
1642
1643 vif = cache->mfc_parent;
1644 cache->mfc_un.res.pkt++;
1645 cache->mfc_un.res.bytes += skb->len;
1646
1647 /*
1648 * Wrong interface: drop packet and (maybe) send PIM assert.
1649 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001650 if (mrt->vif_table[vif].dev != skb->dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 int true_vifi;
1652
Eric Dumazet511c3f92009-06-02 05:14:27 +00001653 if (skb_rtable(skb)->fl.iif == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 /* It is our own packet, looped back.
1655 Very complicated situation...
1656
1657 The best workaround until routing daemons will be
1658 fixed is not to redistribute packet, if it was
1659 send through wrong interface. It means, that
1660 multicast applications WILL NOT work for
1661 (S,G), which have default multicast route pointing
1662 to wrong oif. In any case, it is not a good
1663 idea to use multicasting applications on router.
1664 */
1665 goto dont_forward;
1666 }
1667
1668 cache->mfc_un.res.wrong_if++;
Patrick McHardy0c122952010-04-13 05:03:22 +00001669 true_vifi = ipmr_find_vif(mrt, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
Patrick McHardy0c122952010-04-13 05:03:22 +00001671 if (true_vifi >= 0 && mrt->mroute_do_assert &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 /* pimsm uses asserts, when switching from RPT to SPT,
1673 so that we cannot check that packet arrived on an oif.
1674 It is bad, but otherwise we would need to move pretty
1675 large chunk of pimd to kernel. Ough... --ANK
1676 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001677 (mrt->mroute_do_pim ||
Benjamin Thery6f9374a2009-01-22 04:56:20 +00001678 cache->mfc_un.res.ttls[true_vifi] < 255) &&
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001679 time_after(jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
1681 cache->mfc_un.res.last_assert = jiffies;
Patrick McHardy0c122952010-04-13 05:03:22 +00001682 ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 }
1684 goto dont_forward;
1685 }
1686
Patrick McHardy0c122952010-04-13 05:03:22 +00001687 mrt->vif_table[vif].pkt_in++;
1688 mrt->vif_table[vif].bytes_in += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689
1690 /*
1691 * Forward the frame
1692 */
1693 for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001694 if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 if (psend != -1) {
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,
1699 psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 }
Jianjun Kongc354e122008-11-03 00:28:02 -08001701 psend = ct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 }
1703 }
1704 if (psend != -1) {
1705 if (local) {
1706 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1707 if (skb2)
Patrick McHardy0c122952010-04-13 05:03:22 +00001708 ipmr_queue_xmit(net, mrt, skb2, cache, psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00001710 ipmr_queue_xmit(net, mrt, skb, cache, psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 return 0;
1712 }
1713 }
1714
1715dont_forward:
1716 if (!local)
1717 kfree_skb(skb);
1718 return 0;
1719}
1720
1721
1722/*
1723 * Multicast packets for forwarding arrive here
Eric Dumazet4c968702010-10-01 16:15:01 +00001724 * Called with rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 */
1726
1727int ip_mr_input(struct sk_buff *skb)
1728{
1729 struct mfc_cache *cache;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001730 struct net *net = dev_net(skb->dev);
Eric Dumazet511c3f92009-06-02 05:14:27 +00001731 int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001732 struct mr_table *mrt;
1733 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734
1735 /* Packet is looped back after forward, it should not be
1736 forwarded second time, but still can be delivered locally.
1737 */
Eric Dumazet4c968702010-10-01 16:15:01 +00001738 if (IPCB(skb)->flags & IPSKB_FORWARDED)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 goto dont_forward;
1740
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001741 err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt);
Ben Greeare40dbc52010-07-15 13:22:33 +00001742 if (err < 0) {
1743 kfree_skb(skb);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001744 return err;
Ben Greeare40dbc52010-07-15 13:22:33 +00001745 }
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001746
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 if (!local) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001748 if (IPCB(skb)->opt.router_alert) {
1749 if (ip_call_ra_chain(skb))
1750 return 0;
1751 } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) {
1752 /* IGMPv1 (and broken IGMPv2 implementations sort of
1753 * Cisco IOS <= 11.2(8)) do not put router alert
1754 * option to IGMP packets destined to routable
1755 * groups. It is very bad, because it means
1756 * that we can forward NO IGMP messages.
1757 */
1758 struct sock *mroute_sk;
1759
1760 mroute_sk = rcu_dereference(mrt->mroute_sk);
1761 if (mroute_sk) {
1762 nf_reset(skb);
1763 raw_rcv(mroute_sk, skb);
1764 return 0;
1765 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 }
1767 }
1768
Eric Dumazeta8c94862010-10-01 16:15:08 +00001769 /* already under rcu_read_lock() */
Patrick McHardy0c122952010-04-13 05:03:22 +00001770 cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771
1772 /*
1773 * No usable cache entry
1774 */
Jianjun Kongc354e122008-11-03 00:28:02 -08001775 if (cache == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 int vif;
1777
1778 if (local) {
1779 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1780 ip_local_deliver(skb);
Eric Dumazeta8c94862010-10-01 16:15:08 +00001781 if (skb2 == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 skb = skb2;
1784 }
1785
Eric Dumazeta8c94862010-10-01 16:15:08 +00001786 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001787 vif = ipmr_find_vif(mrt, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 if (vif >= 0) {
Eric Dumazet0eae88f2010-04-20 19:06:52 -07001789 int err2 = ipmr_cache_unresolved(mrt, vif, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 read_unlock(&mrt_lock);
1791
Eric Dumazet0eae88f2010-04-20 19:06:52 -07001792 return err2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 }
1794 read_unlock(&mrt_lock);
1795 kfree_skb(skb);
1796 return -ENODEV;
1797 }
1798
Eric Dumazeta8c94862010-10-01 16:15:08 +00001799 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001800 ip_mr_forward(net, mrt, skb, cache, local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 read_unlock(&mrt_lock);
1802
1803 if (local)
1804 return ip_local_deliver(skb);
1805
1806 return 0;
1807
1808dont_forward:
1809 if (local)
1810 return ip_local_deliver(skb);
1811 kfree_skb(skb);
1812 return 0;
1813}
1814
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001815#ifdef CONFIG_IP_PIMSM
Eric Dumazet55747a02010-10-01 16:14:55 +00001816/* called with rcu_read_lock() */
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001817static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
1818 unsigned int pimlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819{
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001820 struct net_device *reg_dev = NULL;
1821 struct iphdr *encap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001823 encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 /*
1825 Check that:
1826 a. packet is really destinted to a multicast group
1827 b. packet is not a NULL-REGISTER
1828 c. packet is not truncated
1829 */
Joe Perchesf97c1e02007-12-16 13:45:43 -08001830 if (!ipv4_is_multicast(encap->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831 encap->tot_len == 0 ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001832 ntohs(encap->tot_len) + pimlen > skb->len)
1833 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834
1835 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001836 if (mrt->mroute_reg_vif_num >= 0)
1837 reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838 read_unlock(&mrt_lock);
1839
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001840 if (reg_dev == NULL)
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001841 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001843 skb->mac_header = skb->network_header;
Eric Dumazet55747a02010-10-01 16:14:55 +00001844 skb_pull(skb, (u8 *)encap - skb->data);
Arnaldo Carvalho de Melo31c77112007-03-10 19:04:55 -03001845 skb_reset_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 skb->protocol = htons(ETH_P_IP);
Eric Dumazet55747a02010-10-01 16:14:55 +00001847 skb->ip_summed = CHECKSUM_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 skb->pkt_type = PACKET_HOST;
Eric Dumazetd19d56d2010-05-17 22:36:55 -07001849
1850 skb_tunnel_rx(skb, reg_dev);
1851
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852 netif_rx(skb);
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001853
Eric Dumazet55747a02010-10-01 16:14:55 +00001854 return NET_RX_SUCCESS;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001855}
1856#endif
1857
1858#ifdef CONFIG_IP_PIMSM_V1
1859/*
1860 * Handle IGMP messages of PIMv1
1861 */
1862
1863int pim_rcv_v1(struct sk_buff * skb)
1864{
1865 struct igmphdr *pim;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001866 struct net *net = dev_net(skb->dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001867 struct mr_table *mrt;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001868
1869 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
1870 goto drop;
1871
1872 pim = igmp_hdr(skb);
1873
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001874 if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
1875 goto drop;
1876
Patrick McHardy0c122952010-04-13 05:03:22 +00001877 if (!mrt->mroute_do_pim ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001878 pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
1879 goto drop;
1880
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001881 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001882drop:
1883 kfree_skb(skb);
1884 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 return 0;
1886}
1887#endif
1888
1889#ifdef CONFIG_IP_PIMSM_V2
1890static int pim_rcv(struct sk_buff * skb)
1891{
1892 struct pimreghdr *pim;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001893 struct net *net = dev_net(skb->dev);
1894 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001896 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897 goto drop;
1898
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001899 pim = (struct pimreghdr *)skb_transport_header(skb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001900 if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 (pim->flags&PIM_NULL_REGISTER) ||
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001902 (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
Al Virod3bc23e2006-11-14 21:24:49 -08001903 csum_fold(skb_checksum(skb, 0, skb->len, 0))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 goto drop;
1905
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001906 if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
1907 goto drop;
1908
1909 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001910drop:
1911 kfree_skb(skb);
1912 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 return 0;
1914}
1915#endif
1916
Patrick McHardycb6a4e42010-04-26 16:02:08 +02001917static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
1918 struct mfc_cache *c, struct rtmsg *rtm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919{
1920 int ct;
1921 struct rtnexthop *nhp;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001922 u8 *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 struct rtattr *mp_head;
1924
Nicolas Dichtel74381892010-03-25 23:45:35 +00001925 /* If cache is unresolved, don't try to parse IIF and OIF */
Dan Carpentered0f160a2010-05-26 00:38:56 -07001926 if (c->mfc_parent >= MAXVIFS)
Nicolas Dichtel74381892010-03-25 23:45:35 +00001927 return -ENOENT;
1928
Patrick McHardy0c122952010-04-13 05:03:22 +00001929 if (VIF_EXISTS(mrt, c->mfc_parent))
1930 RTA_PUT(skb, RTA_IIF, 4, &mrt->vif_table[c->mfc_parent].dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Jianjun Kongc354e122008-11-03 00:28:02 -08001932 mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
1934 for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001935 if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
1937 goto rtattr_failure;
Jianjun Kongc354e122008-11-03 00:28:02 -08001938 nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 nhp->rtnh_flags = 0;
1940 nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
Patrick McHardy0c122952010-04-13 05:03:22 +00001941 nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 nhp->rtnh_len = sizeof(*nhp);
1943 }
1944 }
1945 mp_head->rta_type = RTA_MULTIPATH;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001946 mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 rtm->rtm_type = RTN_MULTICAST;
1948 return 1;
1949
1950rtattr_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -07001951 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 return -EMSGSIZE;
1953}
1954
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001955int ipmr_get_route(struct net *net,
1956 struct sk_buff *skb, struct rtmsg *rtm, int nowait)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957{
1958 int err;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001959 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 struct mfc_cache *cache;
Eric Dumazet511c3f92009-06-02 05:14:27 +00001961 struct rtable *rt = skb_rtable(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001963 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
1964 if (mrt == NULL)
1965 return -ENOENT;
1966
Eric Dumazeta8c94862010-10-01 16:15:08 +00001967 rcu_read_lock();
Patrick McHardy0c122952010-04-13 05:03:22 +00001968 cache = ipmr_cache_find(mrt, rt->rt_src, rt->rt_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
Jianjun Kongc354e122008-11-03 00:28:02 -08001970 if (cache == NULL) {
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001971 struct sk_buff *skb2;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001972 struct iphdr *iph;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 struct net_device *dev;
1974 int vif;
1975
1976 if (nowait) {
Eric Dumazeta8c94862010-10-01 16:15:08 +00001977 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 return -EAGAIN;
1979 }
1980
1981 dev = skb->dev;
Eric Dumazeta8c94862010-10-01 16:15:08 +00001982 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001983 if (dev == NULL || (vif = ipmr_find_vif(mrt, dev)) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00001985 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 return -ENODEV;
1987 }
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001988 skb2 = skb_clone(skb, GFP_ATOMIC);
1989 if (!skb2) {
1990 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00001991 rcu_read_unlock();
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001992 return -ENOMEM;
1993 }
1994
Arnaldo Carvalho de Meloe2d1bca2007-04-10 20:46:21 -07001995 skb_push(skb2, sizeof(struct iphdr));
1996 skb_reset_network_header(skb2);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001997 iph = ip_hdr(skb2);
1998 iph->ihl = sizeof(struct iphdr) >> 2;
1999 iph->saddr = rt->rt_src;
2000 iph->daddr = rt->rt_dst;
2001 iph->version = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +00002002 err = ipmr_cache_unresolved(mrt, vif, skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00002004 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 return err;
2006 }
2007
Eric Dumazeta8c94862010-10-01 16:15:08 +00002008 read_lock(&mrt_lock);
2009 if (!nowait && (rtm->rtm_flags & RTM_F_NOTIFY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 cache->mfc_flags |= MFC_NOTIFY;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002011 err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00002013 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 return err;
2015}
2016
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002017static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
2018 u32 pid, u32 seq, struct mfc_cache *c)
2019{
2020 struct nlmsghdr *nlh;
2021 struct rtmsg *rtm;
2022
2023 nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
2024 if (nlh == NULL)
2025 return -EMSGSIZE;
2026
2027 rtm = nlmsg_data(nlh);
2028 rtm->rtm_family = RTNL_FAMILY_IPMR;
2029 rtm->rtm_dst_len = 32;
2030 rtm->rtm_src_len = 32;
2031 rtm->rtm_tos = 0;
2032 rtm->rtm_table = mrt->id;
2033 NLA_PUT_U32(skb, RTA_TABLE, mrt->id);
2034 rtm->rtm_type = RTN_MULTICAST;
2035 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2036 rtm->rtm_protocol = RTPROT_UNSPEC;
2037 rtm->rtm_flags = 0;
2038
2039 NLA_PUT_BE32(skb, RTA_SRC, c->mfc_origin);
2040 NLA_PUT_BE32(skb, RTA_DST, c->mfc_mcastgrp);
2041
2042 if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0)
2043 goto nla_put_failure;
2044
2045 return nlmsg_end(skb, nlh);
2046
2047nla_put_failure:
2048 nlmsg_cancel(skb, nlh);
2049 return -EMSGSIZE;
2050}
2051
2052static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
2053{
2054 struct net *net = sock_net(skb->sk);
2055 struct mr_table *mrt;
2056 struct mfc_cache *mfc;
2057 unsigned int t = 0, s_t;
2058 unsigned int h = 0, s_h;
2059 unsigned int e = 0, s_e;
2060
2061 s_t = cb->args[0];
2062 s_h = cb->args[1];
2063 s_e = cb->args[2];
2064
Eric Dumazeta8c94862010-10-01 16:15:08 +00002065 rcu_read_lock();
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002066 ipmr_for_each_table(mrt, net) {
2067 if (t < s_t)
2068 goto next_table;
2069 if (t > s_t)
2070 s_h = 0;
2071 for (h = s_h; h < MFC_LINES; h++) {
Eric Dumazeta8c94862010-10-01 16:15:08 +00002072 list_for_each_entry_rcu(mfc, &mrt->mfc_cache_array[h], list) {
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002073 if (e < s_e)
2074 goto next_entry;
2075 if (ipmr_fill_mroute(mrt, skb,
2076 NETLINK_CB(cb->skb).pid,
2077 cb->nlh->nlmsg_seq,
2078 mfc) < 0)
2079 goto done;
2080next_entry:
2081 e++;
2082 }
2083 e = s_e = 0;
2084 }
2085 s_h = 0;
2086next_table:
2087 t++;
2088 }
2089done:
Eric Dumazeta8c94862010-10-01 16:15:08 +00002090 rcu_read_unlock();
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002091
2092 cb->args[2] = e;
2093 cb->args[1] = h;
2094 cb->args[0] = t;
2095
2096 return skb->len;
2097}
2098
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002099#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100/*
2101 * The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
2102 */
2103struct ipmr_vif_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002104 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002105 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 int ct;
2107};
2108
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002109static struct vif_device *ipmr_vif_seq_idx(struct net *net,
2110 struct ipmr_vif_iter *iter,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 loff_t pos)
2112{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002113 struct mr_table *mrt = iter->mrt;
Patrick McHardy0c122952010-04-13 05:03:22 +00002114
2115 for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) {
2116 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 continue;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002118 if (pos-- == 0)
Patrick McHardy0c122952010-04-13 05:03:22 +00002119 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 }
2121 return NULL;
2122}
2123
2124static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002125 __acquires(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002127 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002128 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002129 struct mr_table *mrt;
2130
2131 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2132 if (mrt == NULL)
2133 return ERR_PTR(-ENOENT);
2134
2135 iter->mrt = mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002136
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 read_lock(&mrt_lock);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002138 return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 : SEQ_START_TOKEN;
2140}
2141
2142static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2143{
2144 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002145 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002146 struct mr_table *mrt = iter->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147
2148 ++*pos;
2149 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002150 return ipmr_vif_seq_idx(net, iter, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002151
Patrick McHardy0c122952010-04-13 05:03:22 +00002152 while (++iter->ct < mrt->maxvif) {
2153 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 continue;
Patrick McHardy0c122952010-04-13 05:03:22 +00002155 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 }
2157 return NULL;
2158}
2159
2160static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002161 __releases(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162{
2163 read_unlock(&mrt_lock);
2164}
2165
2166static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
2167{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002168 struct ipmr_vif_iter *iter = seq->private;
2169 struct mr_table *mrt = iter->mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002172 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n");
2174 } else {
2175 const struct vif_device *vif = v;
2176 const char *name = vif->dev ? vif->dev->name : "none";
2177
2178 seq_printf(seq,
2179 "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n",
Patrick McHardy0c122952010-04-13 05:03:22 +00002180 vif - mrt->vif_table,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002181 name, vif->bytes_in, vif->pkt_in,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 vif->bytes_out, vif->pkt_out,
2183 vif->flags, vif->local, vif->remote);
2184 }
2185 return 0;
2186}
2187
Stephen Hemmingerf6908082007-03-12 14:34:29 -07002188static const struct seq_operations ipmr_vif_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 .start = ipmr_vif_seq_start,
2190 .next = ipmr_vif_seq_next,
2191 .stop = ipmr_vif_seq_stop,
2192 .show = ipmr_vif_seq_show,
2193};
2194
2195static int ipmr_vif_open(struct inode *inode, struct file *file)
2196{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002197 return seq_open_net(inode, file, &ipmr_vif_seq_ops,
2198 sizeof(struct ipmr_vif_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199}
2200
Arjan van de Ven9a321442007-02-12 00:55:35 -08002201static const struct file_operations ipmr_vif_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202 .owner = THIS_MODULE,
2203 .open = ipmr_vif_open,
2204 .read = seq_read,
2205 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002206 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207};
2208
2209struct ipmr_mfc_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002210 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002211 struct mr_table *mrt;
Patrick McHardy862465f2010-04-13 05:03:21 +00002212 struct list_head *cache;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 int ct;
2214};
2215
2216
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002217static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
2218 struct ipmr_mfc_iter *it, loff_t pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002220 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 struct mfc_cache *mfc;
2222
Eric Dumazeta8c94862010-10-01 16:15:08 +00002223 rcu_read_lock();
Patrick McHardy862465f2010-04-13 05:03:21 +00002224 for (it->ct = 0; it->ct < MFC_LINES; it->ct++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002225 it->cache = &mrt->mfc_cache_array[it->ct];
Eric Dumazeta8c94862010-10-01 16:15:08 +00002226 list_for_each_entry_rcu(mfc, it->cache, list)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002227 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 return mfc;
Patrick McHardy862465f2010-04-13 05:03:21 +00002229 }
Eric Dumazeta8c94862010-10-01 16:15:08 +00002230 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002233 it->cache = &mrt->mfc_unres_queue;
Patrick McHardy862465f2010-04-13 05:03:21 +00002234 list_for_each_entry(mfc, it->cache, list)
Patrick McHardye258beb2010-04-13 05:03:19 +00002235 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 return mfc;
2237 spin_unlock_bh(&mfc_unres_lock);
2238
2239 it->cache = NULL;
2240 return NULL;
2241}
2242
2243
2244static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
2245{
2246 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002247 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002248 struct mr_table *mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002249
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002250 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2251 if (mrt == NULL)
2252 return ERR_PTR(-ENOENT);
2253
2254 it->mrt = mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 it->cache = NULL;
2256 it->ct = 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002257 return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 : SEQ_START_TOKEN;
2259}
2260
2261static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2262{
2263 struct mfc_cache *mfc = v;
2264 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002265 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002266 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267
2268 ++*pos;
2269
2270 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002271 return ipmr_mfc_seq_idx(net, seq->private, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272
Patrick McHardy862465f2010-04-13 05:03:21 +00002273 if (mfc->list.next != it->cache)
2274 return list_entry(mfc->list.next, struct mfc_cache, list);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002275
Patrick McHardy0c122952010-04-13 05:03:22 +00002276 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 goto end_of_list;
2278
Patrick McHardy0c122952010-04-13 05:03:22 +00002279 BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
2281 while (++it->ct < MFC_LINES) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002282 it->cache = &mrt->mfc_cache_array[it->ct];
Patrick McHardy862465f2010-04-13 05:03:21 +00002283 if (list_empty(it->cache))
2284 continue;
2285 return list_first_entry(it->cache, struct mfc_cache, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 }
2287
2288 /* exhausted cache_array, show unresolved */
Eric Dumazeta8c94862010-10-01 16:15:08 +00002289 rcu_read_unlock();
Patrick McHardy0c122952010-04-13 05:03:22 +00002290 it->cache = &mrt->mfc_unres_queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 it->ct = 0;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002292
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00002294 if (!list_empty(it->cache))
2295 return list_first_entry(it->cache, struct mfc_cache, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296
2297 end_of_list:
2298 spin_unlock_bh(&mfc_unres_lock);
2299 it->cache = NULL;
2300
2301 return NULL;
2302}
2303
2304static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
2305{
2306 struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002307 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308
Patrick McHardy0c122952010-04-13 05:03:22 +00002309 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 spin_unlock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002311 else if (it->cache == &mrt->mfc_cache_array[it->ct])
Eric Dumazeta8c94862010-10-01 16:15:08 +00002312 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313}
2314
2315static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
2316{
2317 int n;
2318
2319 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002320 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 "Group Origin Iif Pkts Bytes Wrong Oifs\n");
2322 } else {
2323 const struct mfc_cache *mfc = v;
2324 const struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002325 const struct mr_table *mrt = it->mrt;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002326
Eric Dumazet0eae88f2010-04-20 19:06:52 -07002327 seq_printf(seq, "%08X %08X %-3hd",
2328 (__force u32) mfc->mfc_mcastgrp,
2329 (__force u32) mfc->mfc_origin,
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002330 mfc->mfc_parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331
Patrick McHardy0c122952010-04-13 05:03:22 +00002332 if (it->cache != &mrt->mfc_unres_queue) {
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002333 seq_printf(seq, " %8lu %8lu %8lu",
2334 mfc->mfc_un.res.pkt,
2335 mfc->mfc_un.res.bytes,
2336 mfc->mfc_un.res.wrong_if);
Stephen Hemminger132adf52007-03-08 20:44:43 -08002337 for (n = mfc->mfc_un.res.minvif;
2338 n < mfc->mfc_un.res.maxvif; n++ ) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002339 if (VIF_EXISTS(mrt, n) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +00002340 mfc->mfc_un.res.ttls[n] < 255)
2341 seq_printf(seq,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002342 " %2d:%-3d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 n, mfc->mfc_un.res.ttls[n]);
2344 }
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002345 } else {
2346 /* unresolved mfc_caches don't contain
2347 * pkt, bytes and wrong_if values
2348 */
2349 seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350 }
2351 seq_putc(seq, '\n');
2352 }
2353 return 0;
2354}
2355
Stephen Hemmingerf6908082007-03-12 14:34:29 -07002356static const struct seq_operations ipmr_mfc_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357 .start = ipmr_mfc_seq_start,
2358 .next = ipmr_mfc_seq_next,
2359 .stop = ipmr_mfc_seq_stop,
2360 .show = ipmr_mfc_seq_show,
2361};
2362
2363static int ipmr_mfc_open(struct inode *inode, struct file *file)
2364{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002365 return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
2366 sizeof(struct ipmr_mfc_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367}
2368
Arjan van de Ven9a321442007-02-12 00:55:35 -08002369static const struct file_operations ipmr_mfc_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 .owner = THIS_MODULE,
2371 .open = ipmr_mfc_open,
2372 .read = seq_read,
2373 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002374 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002376#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
2378#ifdef CONFIG_IP_PIMSM_V2
Alexey Dobriyan32613092009-09-14 12:21:47 +00002379static const struct net_protocol pim_protocol = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380 .handler = pim_rcv,
Tom Goff403dbb92009-06-14 03:16:13 -07002381 .netns_ok = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382};
2383#endif
2384
2385
2386/*
2387 * Setup for IP multicast routing
2388 */
Benjamin Therycf958ae32009-01-22 04:56:16 +00002389static int __net_init ipmr_net_init(struct net *net)
2390{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002391 int err;
Benjamin Therycf958ae32009-01-22 04:56:16 +00002392
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002393 err = ipmr_rules_init(net);
2394 if (err < 0)
Benjamin Therycf958ae32009-01-22 04:56:16 +00002395 goto fail;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002396
2397#ifdef CONFIG_PROC_FS
2398 err = -ENOMEM;
2399 if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops))
2400 goto proc_vif_fail;
2401 if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops))
2402 goto proc_cache_fail;
2403#endif
Benjamin Thery2bb8b262009-01-22 04:56:18 +00002404 return 0;
2405
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002406#ifdef CONFIG_PROC_FS
2407proc_cache_fail:
2408 proc_net_remove(net, "ip_mr_vif");
2409proc_vif_fail:
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002410 ipmr_rules_exit(net);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002411#endif
Benjamin Therycf958ae32009-01-22 04:56:16 +00002412fail:
2413 return err;
2414}
2415
2416static void __net_exit ipmr_net_exit(struct net *net)
2417{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002418#ifdef CONFIG_PROC_FS
2419 proc_net_remove(net, "ip_mr_cache");
2420 proc_net_remove(net, "ip_mr_vif");
2421#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002422 ipmr_rules_exit(net);
Benjamin Therycf958ae32009-01-22 04:56:16 +00002423}
2424
2425static struct pernet_operations ipmr_net_ops = {
2426 .init = ipmr_net_init,
2427 .exit = ipmr_net_exit,
2428};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002429
Wang Chen03d2f892008-07-03 12:13:36 +08002430int __init ip_mr_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431{
Wang Chen03d2f892008-07-03 12:13:36 +08002432 int err;
2433
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 mrt_cachep = kmem_cache_create("ip_mrt_cache",
2435 sizeof(struct mfc_cache),
Eric Dumazeta8c94862010-10-01 16:15:08 +00002436 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
Paul Mundt20c2df82007-07-20 10:11:58 +09002437 NULL);
Wang Chen03d2f892008-07-03 12:13:36 +08002438 if (!mrt_cachep)
2439 return -ENOMEM;
2440
Benjamin Therycf958ae32009-01-22 04:56:16 +00002441 err = register_pernet_subsys(&ipmr_net_ops);
2442 if (err)
2443 goto reg_pernet_fail;
2444
Wang Chen03d2f892008-07-03 12:13:36 +08002445 err = register_netdevice_notifier(&ip_mr_notifier);
2446 if (err)
2447 goto reg_notif_fail;
Tom Goff403dbb92009-06-14 03:16:13 -07002448#ifdef CONFIG_IP_PIMSM_V2
2449 if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) {
2450 printk(KERN_ERR "ip_mr_init: can't add PIM protocol\n");
2451 err = -EAGAIN;
2452 goto add_proto_fail;
2453 }
2454#endif
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002455 rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute);
Wang Chen03d2f892008-07-03 12:13:36 +08002456 return 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002457
Tom Goff403dbb92009-06-14 03:16:13 -07002458#ifdef CONFIG_IP_PIMSM_V2
2459add_proto_fail:
2460 unregister_netdevice_notifier(&ip_mr_notifier);
2461#endif
Benjamin Theryc3e38892008-11-19 14:07:41 -08002462reg_notif_fail:
Benjamin Therycf958ae32009-01-22 04:56:16 +00002463 unregister_pernet_subsys(&ipmr_net_ops);
2464reg_pernet_fail:
Benjamin Theryc3e38892008-11-19 14:07:41 -08002465 kmem_cache_destroy(mrt_cachep);
Wang Chen03d2f892008-07-03 12:13:36 +08002466 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467}