blob: 7f6273506eea46522a17ff85b9a451cb18fdc7d3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * IP multicast routing support for mrouted 3.6/3.8
3 *
Alan Cox113aa832008-10-13 19:01:08 -07004 * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Linux Consultancy and Custom Driver Development
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 * Fixes:
13 * Michael Chastain : Incorrect size of copying.
14 * Alan Cox : Added the cache manager code
15 * Alan Cox : Fixed the clone/copy bug and device race.
16 * Mike McLagan : Routing by source
17 * Malcolm Beattie : Buffer handling fixes.
18 * Alexey Kuznetsov : Double buffer free and other fixes.
19 * SVR Anand : Fixed several multicast bugs and problems.
20 * Alexey Kuznetsov : Status, optimisations and more.
21 * Brad Parker : Better behaviour on mrouted upcall
22 * overflow.
23 * Carlos Picoto : PIMv1 Support
24 * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header
Gilles Espinassef77f13e2010-03-29 15:41:47 +020025 * Relax this requirement to work with older peers.
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 *
27 */
28
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <asm/system.h>
30#include <asm/uaccess.h>
31#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080032#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/errno.h>
34#include <linux/timer.h>
35#include <linux/mm.h>
36#include <linux/kernel.h>
37#include <linux/fcntl.h>
38#include <linux/stat.h>
39#include <linux/socket.h>
40#include <linux/in.h>
41#include <linux/inet.h>
42#include <linux/netdevice.h>
43#include <linux/inetdevice.h>
44#include <linux/igmp.h>
45#include <linux/proc_fs.h>
46#include <linux/seq_file.h>
47#include <linux/mroute.h>
48#include <linux/init.h>
Kris Katterjohn46f25df2006-01-05 16:35:42 -080049#include <linux/if_ether.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090050#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020051#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <net/ip.h>
53#include <net/protocol.h>
54#include <linux/skbuff.h>
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020055#include <net/route.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#include <net/sock.h>
57#include <net/icmp.h>
58#include <net/udp.h>
59#include <net/raw.h>
60#include <linux/notifier.h>
61#include <linux/if_arp.h>
62#include <linux/netfilter_ipv4.h>
63#include <net/ipip.h>
64#include <net/checksum.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070065#include <net/netlink.h>
Patrick McHardyf0ad0862010-04-13 05:03:23 +000066#include <net/fib_rules.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
69#define CONFIG_IP_PIMSM 1
70#endif
71
Patrick McHardy0c122952010-04-13 05:03:22 +000072struct mr_table {
Patrick McHardyf0ad0862010-04-13 05:03:23 +000073 struct list_head list;
Patrick McHardy8de53df2010-04-15 13:29:28 +020074#ifdef CONFIG_NET_NS
75 struct net *net;
76#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +000077 u32 id;
Patrick McHardy0c122952010-04-13 05:03:22 +000078 struct sock *mroute_sk;
79 struct timer_list ipmr_expire_timer;
80 struct list_head mfc_unres_queue;
81 struct list_head mfc_cache_array[MFC_LINES];
82 struct vif_device vif_table[MAXVIFS];
83 int maxvif;
84 atomic_t cache_resolve_queue_len;
85 int mroute_do_assert;
86 int mroute_do_pim;
87#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
88 int mroute_reg_vif_num;
89#endif
90};
91
Patrick McHardyf0ad0862010-04-13 05:03:23 +000092struct ipmr_rule {
93 struct fib_rule common;
94};
95
96struct ipmr_result {
97 struct mr_table *mrt;
98};
99
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100/* Big lock, protecting vif table, mrt cache and mroute socket state.
101 Note that the changes are semaphored via rtnl_lock.
102 */
103
104static DEFINE_RWLOCK(mrt_lock);
105
106/*
107 * Multicast router control variables
108 */
109
Patrick McHardy0c122952010-04-13 05:03:22 +0000110#define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
112/* Special spinlock for queue of unresolved entries */
113static DEFINE_SPINLOCK(mfc_unres_lock);
114
115/* We return to original Alan's scheme. Hash table of resolved
116 entries is changed only in process context and protected
117 with weak lock mrt_lock. Queue of unresolved entries is protected
118 with strong spinlock mfc_unres_lock.
119
120 In this case data path is free of exclusive locks at all.
121 */
122
Christoph Lametere18b8902006-12-06 20:33:20 -0800123static struct kmem_cache *mrt_cachep __read_mostly;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000125static struct mr_table *ipmr_new_table(struct net *net, u32 id);
Patrick McHardy0c122952010-04-13 05:03:22 +0000126static int ip_mr_forward(struct net *net, struct mr_table *mrt,
127 struct sk_buff *skb, struct mfc_cache *cache,
128 int local);
129static int ipmr_cache_report(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000130 struct sk_buff *pkt, vifi_t vifi, int assert);
Patrick McHardycb6a4e42010-04-26 16:02:08 +0200131static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
132 struct mfc_cache *c, struct rtmsg *rtm);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000133static void ipmr_expire_process(unsigned long arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000135#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
136#define ipmr_for_each_table(mrt, net) \
137 list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list)
138
139static struct mr_table *ipmr_get_table(struct net *net, u32 id)
140{
141 struct mr_table *mrt;
142
143 ipmr_for_each_table(mrt, net) {
144 if (mrt->id == id)
145 return mrt;
146 }
147 return NULL;
148}
149
150static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
151 struct mr_table **mrt)
152{
153 struct ipmr_result res;
154 struct fib_lookup_arg arg = { .result = &res, };
155 int err;
156
157 err = fib_rules_lookup(net->ipv4.mr_rules_ops, flp, 0, &arg);
158 if (err < 0)
159 return err;
160 *mrt = res.mrt;
161 return 0;
162}
163
164static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp,
165 int flags, struct fib_lookup_arg *arg)
166{
167 struct ipmr_result *res = arg->result;
168 struct mr_table *mrt;
169
170 switch (rule->action) {
171 case FR_ACT_TO_TBL:
172 break;
173 case FR_ACT_UNREACHABLE:
174 return -ENETUNREACH;
175 case FR_ACT_PROHIBIT:
176 return -EACCES;
177 case FR_ACT_BLACKHOLE:
178 default:
179 return -EINVAL;
180 }
181
182 mrt = ipmr_get_table(rule->fr_net, rule->table);
183 if (mrt == NULL)
184 return -EAGAIN;
185 res->mrt = mrt;
186 return 0;
187}
188
189static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
190{
191 return 1;
192}
193
194static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = {
195 FRA_GENERIC_POLICY,
196};
197
198static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
199 struct fib_rule_hdr *frh, struct nlattr **tb)
200{
201 return 0;
202}
203
204static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
205 struct nlattr **tb)
206{
207 return 1;
208}
209
210static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
211 struct fib_rule_hdr *frh)
212{
213 frh->dst_len = 0;
214 frh->src_len = 0;
215 frh->tos = 0;
216 return 0;
217}
218
Patrick McHardy3d0c9c42010-04-26 16:02:04 +0200219static const struct fib_rules_ops __net_initdata ipmr_rules_ops_template = {
Patrick McHardy25239ce2010-04-26 16:02:05 +0200220 .family = RTNL_FAMILY_IPMR,
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000221 .rule_size = sizeof(struct ipmr_rule),
222 .addr_size = sizeof(u32),
223 .action = ipmr_rule_action,
224 .match = ipmr_rule_match,
225 .configure = ipmr_rule_configure,
226 .compare = ipmr_rule_compare,
227 .default_pref = fib_default_rule_pref,
228 .fill = ipmr_rule_fill,
229 .nlgroup = RTNLGRP_IPV4_RULE,
230 .policy = ipmr_rule_policy,
231 .owner = THIS_MODULE,
232};
233
234static int __net_init ipmr_rules_init(struct net *net)
235{
236 struct fib_rules_ops *ops;
237 struct mr_table *mrt;
238 int err;
239
240 ops = fib_rules_register(&ipmr_rules_ops_template, net);
241 if (IS_ERR(ops))
242 return PTR_ERR(ops);
243
244 INIT_LIST_HEAD(&net->ipv4.mr_tables);
245
246 mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
247 if (mrt == NULL) {
248 err = -ENOMEM;
249 goto err1;
250 }
251
252 err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0);
253 if (err < 0)
254 goto err2;
255
256 net->ipv4.mr_rules_ops = ops;
257 return 0;
258
259err2:
260 kfree(mrt);
261err1:
262 fib_rules_unregister(ops);
263 return err;
264}
265
266static void __net_exit ipmr_rules_exit(struct net *net)
267{
268 struct mr_table *mrt, *next;
269
Eric Dumazet035320d2010-06-06 23:48:40 +0000270 list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
271 list_del(&mrt->list);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000272 kfree(mrt);
Eric Dumazet035320d2010-06-06 23:48:40 +0000273 }
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000274 fib_rules_unregister(net->ipv4.mr_rules_ops);
275}
276#else
277#define ipmr_for_each_table(mrt, net) \
278 for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
279
280static struct mr_table *ipmr_get_table(struct net *net, u32 id)
281{
282 return net->ipv4.mrt;
283}
284
285static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
286 struct mr_table **mrt)
287{
288 *mrt = net->ipv4.mrt;
289 return 0;
290}
291
292static int __net_init ipmr_rules_init(struct net *net)
293{
294 net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
295 return net->ipv4.mrt ? 0 : -ENOMEM;
296}
297
298static void __net_exit ipmr_rules_exit(struct net *net)
299{
300 kfree(net->ipv4.mrt);
301}
302#endif
303
304static struct mr_table *ipmr_new_table(struct net *net, u32 id)
305{
306 struct mr_table *mrt;
307 unsigned int i;
308
309 mrt = ipmr_get_table(net, id);
310 if (mrt != NULL)
311 return mrt;
312
313 mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
314 if (mrt == NULL)
315 return NULL;
Patrick McHardy8de53df2010-04-15 13:29:28 +0200316 write_pnet(&mrt->net, net);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000317 mrt->id = id;
318
319 /* Forwarding cache */
320 for (i = 0; i < MFC_LINES; i++)
321 INIT_LIST_HEAD(&mrt->mfc_cache_array[i]);
322
323 INIT_LIST_HEAD(&mrt->mfc_unres_queue);
324
325 setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process,
326 (unsigned long)mrt);
327
328#ifdef CONFIG_IP_PIMSM
329 mrt->mroute_reg_vif_num = -1;
330#endif
331#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
332 list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables);
333#endif
334 return mrt;
335}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
338
Wang Chend6070322008-07-14 20:55:26 -0700339static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
340{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000341 struct net *net = dev_net(dev);
342
Wang Chend6070322008-07-14 20:55:26 -0700343 dev_close(dev);
344
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000345 dev = __dev_get_by_name(net, "tunl0");
Wang Chend6070322008-07-14 20:55:26 -0700346 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800347 const struct net_device_ops *ops = dev->netdev_ops;
Wang Chend6070322008-07-14 20:55:26 -0700348 struct ifreq ifr;
Wang Chend6070322008-07-14 20:55:26 -0700349 struct ip_tunnel_parm p;
350
351 memset(&p, 0, sizeof(p));
352 p.iph.daddr = v->vifc_rmt_addr.s_addr;
353 p.iph.saddr = v->vifc_lcl_addr.s_addr;
354 p.iph.version = 4;
355 p.iph.ihl = 5;
356 p.iph.protocol = IPPROTO_IPIP;
357 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
358 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
359
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800360 if (ops->ndo_do_ioctl) {
361 mm_segment_t oldfs = get_fs();
362
363 set_fs(KERNEL_DS);
364 ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL);
365 set_fs(oldfs);
366 }
Wang Chend6070322008-07-14 20:55:26 -0700367 }
368}
369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370static
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000371struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372{
373 struct net_device *dev;
374
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000375 dev = __dev_get_by_name(net, "tunl0");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800378 const struct net_device_ops *ops = dev->netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 int err;
380 struct ifreq ifr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 struct ip_tunnel_parm p;
382 struct in_device *in_dev;
383
384 memset(&p, 0, sizeof(p));
385 p.iph.daddr = v->vifc_rmt_addr.s_addr;
386 p.iph.saddr = v->vifc_lcl_addr.s_addr;
387 p.iph.version = 4;
388 p.iph.ihl = 5;
389 p.iph.protocol = IPPROTO_IPIP;
390 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
Stephen Hemmingerba93ef72008-01-21 17:28:59 -0800391 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800393 if (ops->ndo_do_ioctl) {
394 mm_segment_t oldfs = get_fs();
395
396 set_fs(KERNEL_DS);
397 err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
398 set_fs(oldfs);
399 } else
400 err = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
402 dev = NULL;
403
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000404 if (err == 0 &&
405 (dev = __dev_get_by_name(net, p.name)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 dev->flags |= IFF_MULTICAST;
407
Herbert Xue5ed6392005-10-03 14:35:55 -0700408 in_dev = __in_dev_get_rtnl(dev);
Herbert Xu71e27da2007-06-04 23:36:06 -0700409 if (in_dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 goto failure;
Herbert Xu71e27da2007-06-04 23:36:06 -0700411
412 ipv4_devconf_setall(in_dev);
413 IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
415 if (dev_open(dev))
416 goto failure;
Wang Chen7dc00c82008-07-14 20:56:34 -0700417 dev_hold(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
419 }
420 return dev;
421
422failure:
423 /* allow the register to be completed before unregistering. */
424 rtnl_unlock();
425 rtnl_lock();
426
427 unregister_netdevice(dev);
428 return NULL;
429}
430
431#ifdef CONFIG_IP_PIMSM
432
Stephen Hemminger6fef4c02009-08-31 19:50:41 +0000433static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000435 struct net *net = dev_net(dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000436 struct mr_table *mrt;
437 struct flowi fl = {
438 .oif = dev->ifindex,
439 .iif = skb->skb_iif,
440 .mark = skb->mark,
441 };
442 int err;
443
444 err = ipmr_fib_lookup(net, &fl, &mrt);
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
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000580static inline void ipmr_cache_free(struct mfc_cache *c)
581{
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000582 kmem_cache_free(mrt_cachep, c);
583}
584
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585/* Destroy an unresolved cache entry, killing queued skbs
586 and reporting error to netlink readers.
587 */
588
Patrick McHardy0c122952010-04-13 05:03:22 +0000589static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
Patrick McHardy8de53df2010-04-15 13:29:28 +0200591 struct net *net = read_pnet(&mrt->net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700593 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Patrick McHardy0c122952010-04-13 05:03:22 +0000595 atomic_dec(&mrt->cache_resolve_queue_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Jianjun Kongc354e122008-11-03 00:28:02 -0800597 while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700598 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
600 nlh->nlmsg_type = NLMSG_ERROR;
601 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
602 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700603 e = NLMSG_DATA(nlh);
604 e->error = -ETIMEDOUT;
605 memset(&e->msg, 0, sizeof(e->msg));
Thomas Graf2942e902006-08-15 00:30:25 -0700606
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000607 rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 } else
609 kfree_skb(skb);
610 }
611
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000612 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613}
614
615
Patrick McHardye258beb2010-04-13 05:03:19 +0000616/* Timer process for the unresolved queue. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617
Patrick McHardye258beb2010-04-13 05:03:19 +0000618static void ipmr_expire_process(unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619{
Patrick McHardy0c122952010-04-13 05:03:22 +0000620 struct mr_table *mrt = (struct mr_table *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 unsigned long now;
622 unsigned long expires;
Patrick McHardy862465f2010-04-13 05:03:21 +0000623 struct mfc_cache *c, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625 if (!spin_trylock(&mfc_unres_lock)) {
Patrick McHardy0c122952010-04-13 05:03:22 +0000626 mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 return;
628 }
629
Patrick McHardy0c122952010-04-13 05:03:22 +0000630 if (list_empty(&mrt->mfc_unres_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 goto out;
632
633 now = jiffies;
634 expires = 10*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Patrick McHardy0c122952010-04-13 05:03:22 +0000636 list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 if (time_after(c->mfc_un.unres.expires, now)) {
638 unsigned long interval = c->mfc_un.unres.expires - now;
639 if (interval < expires)
640 expires = interval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 continue;
642 }
643
Patrick McHardy862465f2010-04-13 05:03:21 +0000644 list_del(&c->list);
Patrick McHardy0c122952010-04-13 05:03:22 +0000645 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 }
647
Patrick McHardy0c122952010-04-13 05:03:22 +0000648 if (!list_empty(&mrt->mfc_unres_queue))
649 mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
651out:
652 spin_unlock(&mfc_unres_lock);
653}
654
655/* Fill oifs list. It is called under write locked mrt_lock. */
656
Patrick McHardy0c122952010-04-13 05:03:22 +0000657static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000658 unsigned char *ttls)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
660 int vifi;
661
662 cache->mfc_un.res.minvif = MAXVIFS;
663 cache->mfc_un.res.maxvif = 0;
664 memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
665
Patrick McHardy0c122952010-04-13 05:03:22 +0000666 for (vifi = 0; vifi < mrt->maxvif; vifi++) {
667 if (VIF_EXISTS(mrt, vifi) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +0000668 ttls[vifi] && ttls[vifi] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 cache->mfc_un.res.ttls[vifi] = ttls[vifi];
670 if (cache->mfc_un.res.minvif > vifi)
671 cache->mfc_un.res.minvif = vifi;
672 if (cache->mfc_un.res.maxvif <= vifi)
673 cache->mfc_un.res.maxvif = vifi + 1;
674 }
675 }
676}
677
Patrick McHardy0c122952010-04-13 05:03:22 +0000678static int vif_add(struct net *net, struct mr_table *mrt,
679 struct vifctl *vifc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680{
681 int vifi = vifc->vifc_vifi;
Patrick McHardy0c122952010-04-13 05:03:22 +0000682 struct vif_device *v = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 struct net_device *dev;
684 struct in_device *in_dev;
Wang Chend6070322008-07-14 20:55:26 -0700685 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
687 /* Is vif busy ? */
Patrick McHardy0c122952010-04-13 05:03:22 +0000688 if (VIF_EXISTS(mrt, vifi))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 return -EADDRINUSE;
690
691 switch (vifc->vifc_flags) {
692#ifdef CONFIG_IP_PIMSM
693 case VIFF_REGISTER:
694 /*
695 * Special Purpose VIF in PIM
696 * All the packets will be sent to the daemon
697 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000698 if (mrt->mroute_reg_vif_num >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 return -EADDRINUSE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000700 dev = ipmr_reg_vif(net, mrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 if (!dev)
702 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700703 err = dev_set_allmulti(dev, 1);
704 if (err) {
705 unregister_netdevice(dev);
Wang Chen7dc00c82008-07-14 20:56:34 -0700706 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700707 return err;
708 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 break;
710#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900711 case VIFF_TUNNEL:
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000712 dev = ipmr_new_tunnel(net, vifc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 if (!dev)
714 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700715 err = dev_set_allmulti(dev, 1);
716 if (err) {
717 ipmr_del_tunnel(dev, vifc);
Wang Chen7dc00c82008-07-14 20:56:34 -0700718 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700719 return err;
720 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 break;
Ilia Kee5e81f2009-09-16 05:53:07 +0000722
723 case VIFF_USE_IFINDEX:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 case 0:
Ilia Kee5e81f2009-09-16 05:53:07 +0000725 if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
726 dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex);
727 if (dev && dev->ip_ptr == NULL) {
728 dev_put(dev);
729 return -EADDRNOTAVAIL;
730 }
731 } else
732 dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
733
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 if (!dev)
735 return -EADDRNOTAVAIL;
Wang Chend6070322008-07-14 20:55:26 -0700736 err = dev_set_allmulti(dev, 1);
Wang Chen7dc00c82008-07-14 20:56:34 -0700737 if (err) {
738 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700739 return err;
Wang Chen7dc00c82008-07-14 20:56:34 -0700740 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 break;
742 default:
743 return -EINVAL;
744 }
745
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000746 if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) {
747 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 return -EADDRNOTAVAIL;
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000749 }
Herbert Xu42f811b2007-06-04 23:34:44 -0700750 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 ip_rt_multicast_event(in_dev);
752
753 /*
754 * Fill in the VIF structures
755 */
Jianjun Kongc354e122008-11-03 00:28:02 -0800756 v->rate_limit = vifc->vifc_rate_limit;
757 v->local = vifc->vifc_lcl_addr.s_addr;
758 v->remote = vifc->vifc_rmt_addr.s_addr;
759 v->flags = vifc->vifc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 if (!mrtsock)
761 v->flags |= VIFF_STATIC;
Jianjun Kongc354e122008-11-03 00:28:02 -0800762 v->threshold = vifc->vifc_threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 v->bytes_in = 0;
764 v->bytes_out = 0;
765 v->pkt_in = 0;
766 v->pkt_out = 0;
767 v->link = dev->ifindex;
768 if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))
769 v->link = dev->iflink;
770
771 /* And finish update writing critical data */
772 write_lock_bh(&mrt_lock);
Jianjun Kongc354e122008-11-03 00:28:02 -0800773 v->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774#ifdef CONFIG_IP_PIMSM
775 if (v->flags&VIFF_REGISTER)
Patrick McHardy0c122952010-04-13 05:03:22 +0000776 mrt->mroute_reg_vif_num = vifi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777#endif
Patrick McHardy0c122952010-04-13 05:03:22 +0000778 if (vifi+1 > mrt->maxvif)
779 mrt->maxvif = vifi+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 write_unlock_bh(&mrt_lock);
781 return 0;
782}
783
Patrick McHardy0c122952010-04-13 05:03:22 +0000784static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000785 __be32 origin,
786 __be32 mcastgrp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787{
Jianjun Kongc354e122008-11-03 00:28:02 -0800788 int line = MFC_HASH(mcastgrp, origin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 struct mfc_cache *c;
790
Patrick McHardy0c122952010-04-13 05:03:22 +0000791 list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
Patrick McHardy862465f2010-04-13 05:03:21 +0000792 if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp)
793 return c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 }
Patrick McHardy862465f2010-04-13 05:03:21 +0000795 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796}
797
798/*
799 * Allocate a multicast cache entry
800 */
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000801static struct mfc_cache *ipmr_cache_alloc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802{
Jianjun Kongc354e122008-11-03 00:28:02 -0800803 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
804 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 c->mfc_un.res.minvif = MAXVIFS;
807 return c;
808}
809
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000810static struct mfc_cache *ipmr_cache_alloc_unres(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Jianjun Kongc354e122008-11-03 00:28:02 -0800812 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
813 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 skb_queue_head_init(&c->mfc_un.unres.unresolved);
816 c->mfc_un.unres.expires = jiffies + 10*HZ;
817 return c;
818}
819
820/*
821 * A cache entry has gone into a resolved state from queued
822 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900823
Patrick McHardy0c122952010-04-13 05:03:22 +0000824static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
825 struct mfc_cache *uc, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
827 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700828 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
830 /*
831 * Play the pending entries through our router
832 */
833
Jianjun Kongc354e122008-11-03 00:28:02 -0800834 while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700835 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
837
Patrick McHardycb6a4e42010-04-26 16:02:08 +0200838 if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700839 nlh->nlmsg_len = (skb_tail_pointer(skb) -
840 (u8 *)nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 } else {
842 nlh->nlmsg_type = NLMSG_ERROR;
843 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
844 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700845 e = NLMSG_DATA(nlh);
846 e->error = -EMSGSIZE;
847 memset(&e->msg, 0, sizeof(e->msg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 }
Thomas Graf2942e902006-08-15 00:30:25 -0700849
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000850 rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 } else
Patrick McHardy0c122952010-04-13 05:03:22 +0000852 ip_mr_forward(net, mrt, skb, c, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 }
854}
855
856/*
857 * Bounce a cache query up to mrouted. We could use netlink for this but mrouted
858 * expects the following bizarre scheme.
859 *
860 * Called under mrt_lock.
861 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900862
Patrick McHardy0c122952010-04-13 05:03:22 +0000863static int ipmr_cache_report(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000864 struct sk_buff *pkt, vifi_t vifi, int assert)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865{
866 struct sk_buff *skb;
Arnaldo Carvalho de Meloc9bdd4b2007-03-12 20:09:15 -0300867 const int ihl = ip_hdrlen(pkt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 struct igmphdr *igmp;
869 struct igmpmsg *msg;
870 int ret;
871
872#ifdef CONFIG_IP_PIMSM
873 if (assert == IGMPMSG_WHOLEPKT)
874 skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
875 else
876#endif
877 skb = alloc_skb(128, GFP_ATOMIC);
878
Stephen Hemminger132adf52007-03-08 20:44:43 -0800879 if (!skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 return -ENOBUFS;
881
882#ifdef CONFIG_IP_PIMSM
883 if (assert == IGMPMSG_WHOLEPKT) {
884 /* Ugly, but we have no choice with this interface.
885 Duplicate old header, fix ihl, length etc.
886 And all this only to mangle msg->im_msgtype and
887 to set msg->im_mbz to "mbz" :-)
888 */
Arnaldo Carvalho de Melo878c8142007-03-11 22:38:29 -0300889 skb_push(skb, sizeof(struct iphdr));
890 skb_reset_network_header(skb);
Arnaldo Carvalho de Melobadff6d2007-03-13 13:06:52 -0300891 skb_reset_transport_header(skb);
Arnaldo Carvalho de Melo0272ffc2007-03-12 20:05:39 -0300892 msg = (struct igmpmsg *)skb_network_header(skb);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700893 memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 msg->im_msgtype = IGMPMSG_WHOLEPKT;
895 msg->im_mbz = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +0000896 msg->im_vif = mrt->mroute_reg_vif_num;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700897 ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
898 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
899 sizeof(struct iphdr));
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900900 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900902 {
903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 /*
905 * Copy the IP header
906 */
907
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700908 skb->network_header = skb->tail;
Arnaldo Carvalho de Meloddc7b8e2007-03-15 21:42:27 -0300909 skb_put(skb, ihl);
Arnaldo Carvalho de Melo27d7ff42007-03-31 11:55:19 -0300910 skb_copy_to_linear_data(skb, pkt->data, ihl);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700911 ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
912 msg = (struct igmpmsg *)skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 msg->im_vif = vifi;
Eric Dumazetadf30902009-06-02 05:19:30 +0000914 skb_dst_set(skb, dst_clone(skb_dst(pkt)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 /*
917 * Add our header
918 */
919
Jianjun Kongc354e122008-11-03 00:28:02 -0800920 igmp=(struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 igmp->type =
922 msg->im_msgtype = assert;
923 igmp->code = 0;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700924 ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700925 skb->transport_header = skb->network_header;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900926 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
Patrick McHardy0c122952010-04-13 05:03:22 +0000928 if (mrt->mroute_sk == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 kfree_skb(skb);
930 return -EINVAL;
931 }
932
933 /*
934 * Deliver to mrouted
935 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000936 ret = sock_queue_rcv_skb(mrt->mroute_sk, skb);
Benjamin Thery70a269e2009-01-22 04:56:15 +0000937 if (ret < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 if (net_ratelimit())
939 printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
940 kfree_skb(skb);
941 }
942
943 return ret;
944}
945
946/*
947 * Queue a packet for resolution. It gets locked cache entry!
948 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900949
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950static int
Patrick McHardy0c122952010-04-13 05:03:22 +0000951ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952{
Patrick McHardy862465f2010-04-13 05:03:21 +0000953 bool found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 int err;
955 struct mfc_cache *c;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700956 const struct iphdr *iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
958 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +0000959 list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +0000960 if (c->mfc_mcastgrp == iph->daddr &&
Patrick McHardy862465f2010-04-13 05:03:21 +0000961 c->mfc_origin == iph->saddr) {
962 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 break;
Patrick McHardy862465f2010-04-13 05:03:21 +0000964 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 }
966
Patrick McHardy862465f2010-04-13 05:03:21 +0000967 if (!found) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 /*
969 * Create a new entry if allowable
970 */
971
Patrick McHardy0c122952010-04-13 05:03:22 +0000972 if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000973 (c = ipmr_cache_alloc_unres()) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 spin_unlock_bh(&mfc_unres_lock);
975
976 kfree_skb(skb);
977 return -ENOBUFS;
978 }
979
980 /*
981 * Fill in the new cache entry
982 */
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700983 c->mfc_parent = -1;
984 c->mfc_origin = iph->saddr;
985 c->mfc_mcastgrp = iph->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
987 /*
988 * Reflect first query at mrouted.
989 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000990 err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000991 if (err < 0) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900992 /* If the report failed throw the cache entry
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 out - Brad Parker
994 */
995 spin_unlock_bh(&mfc_unres_lock);
996
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000997 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 kfree_skb(skb);
999 return err;
1000 }
1001
Patrick McHardy0c122952010-04-13 05:03:22 +00001002 atomic_inc(&mrt->cache_resolve_queue_len);
1003 list_add(&c->list, &mrt->mfc_unres_queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
David S. Miller278554b2010-05-12 00:05:35 -07001005 if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
1006 mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 }
1008
1009 /*
1010 * See if we can append the packet
1011 */
1012 if (c->mfc_un.unres.unresolved.qlen>3) {
1013 kfree_skb(skb);
1014 err = -ENOBUFS;
1015 } else {
Jianjun Kongc354e122008-11-03 00:28:02 -08001016 skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 err = 0;
1018 }
1019
1020 spin_unlock_bh(&mfc_unres_lock);
1021 return err;
1022}
1023
1024/*
1025 * MFC cache manipulation by user space mroute daemon
1026 */
1027
Patrick McHardy0c122952010-04-13 05:03:22 +00001028static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029{
1030 int line;
Patrick McHardy862465f2010-04-13 05:03:21 +00001031 struct mfc_cache *c, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Jianjun Kongc354e122008-11-03 00:28:02 -08001033 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034
Patrick McHardy0c122952010-04-13 05:03:22 +00001035 list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
1037 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
1038 write_lock_bh(&mrt_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00001039 list_del(&c->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 write_unlock_bh(&mrt_lock);
1041
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001042 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 return 0;
1044 }
1045 }
1046 return -ENOENT;
1047}
1048
Patrick McHardy0c122952010-04-13 05:03:22 +00001049static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
1050 struct mfcctl *mfc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051{
Patrick McHardy862465f2010-04-13 05:03:21 +00001052 bool found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 int line;
Patrick McHardy862465f2010-04-13 05:03:21 +00001054 struct mfc_cache *uc, *c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055
Patrick McHardya50436f22010-03-17 06:04:14 +00001056 if (mfc->mfcc_parent >= MAXVIFS)
1057 return -ENFILE;
1058
Jianjun Kongc354e122008-11-03 00:28:02 -08001059 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060
Patrick McHardy0c122952010-04-13 05:03:22 +00001061 list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
Patrick McHardy862465f2010-04-13 05:03:21 +00001063 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
1064 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 break;
Patrick McHardy862465f2010-04-13 05:03:21 +00001066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 }
1068
Patrick McHardy862465f2010-04-13 05:03:21 +00001069 if (found) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 write_lock_bh(&mrt_lock);
1071 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001072 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 if (!mrtsock)
1074 c->mfc_flags |= MFC_STATIC;
1075 write_unlock_bh(&mrt_lock);
1076 return 0;
1077 }
1078
Joe Perchesf97c1e02007-12-16 13:45:43 -08001079 if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 return -EINVAL;
1081
Patrick McHardyd658f8a2010-04-13 05:03:20 +00001082 c = ipmr_cache_alloc();
Jianjun Kongc354e122008-11-03 00:28:02 -08001083 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 return -ENOMEM;
1085
Jianjun Kongc354e122008-11-03 00:28:02 -08001086 c->mfc_origin = mfc->mfcc_origin.s_addr;
1087 c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
1088 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001089 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 if (!mrtsock)
1091 c->mfc_flags |= MFC_STATIC;
1092
1093 write_lock_bh(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001094 list_add(&c->list, &mrt->mfc_cache_array[line]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 write_unlock_bh(&mrt_lock);
1096
1097 /*
1098 * Check to see if we resolved a queued list. If so we
1099 * need to send on the frames and tidy up.
1100 */
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001101 found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001103 list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +00001104 if (uc->mfc_origin == c->mfc_origin &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 uc->mfc_mcastgrp == c->mfc_mcastgrp) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001106 list_del(&uc->list);
Patrick McHardy0c122952010-04-13 05:03:22 +00001107 atomic_dec(&mrt->cache_resolve_queue_len);
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001108 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 break;
1110 }
1111 }
Patrick McHardy0c122952010-04-13 05:03:22 +00001112 if (list_empty(&mrt->mfc_unres_queue))
1113 del_timer(&mrt->ipmr_expire_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 spin_unlock_bh(&mfc_unres_lock);
1115
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001116 if (found) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001117 ipmr_cache_resolve(net, mrt, uc, c);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001118 ipmr_cache_free(uc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 }
1120 return 0;
1121}
1122
1123/*
1124 * Close the multicast socket, and clear the vif tables etc
1125 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001126
Patrick McHardy0c122952010-04-13 05:03:22 +00001127static void mroute_clean_tables(struct mr_table *mrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128{
1129 int i;
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001130 LIST_HEAD(list);
Patrick McHardy862465f2010-04-13 05:03:21 +00001131 struct mfc_cache *c, *next;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 /*
1134 * Shut down all active vif entries
1135 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001136 for (i = 0; i < mrt->maxvif; i++) {
1137 if (!(mrt->vif_table[i].flags&VIFF_STATIC))
1138 vif_delete(mrt, i, 0, &list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 }
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001140 unregister_netdevice_many(&list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141
1142 /*
1143 * Wipe the cache
1144 */
Patrick McHardy862465f2010-04-13 05:03:21 +00001145 for (i = 0; i < MFC_LINES; i++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001146 list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001147 if (c->mfc_flags&MFC_STATIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 write_lock_bh(&mrt_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00001150 list_del(&c->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 write_unlock_bh(&mrt_lock);
1152
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001153 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 }
1155 }
1156
Patrick McHardy0c122952010-04-13 05:03:22 +00001157 if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001159 list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001160 list_del(&c->list);
Patrick McHardy0c122952010-04-13 05:03:22 +00001161 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 }
1163 spin_unlock_bh(&mfc_unres_lock);
1164 }
1165}
1166
1167static void mrtsock_destruct(struct sock *sk)
1168{
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001169 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001170 struct mr_table *mrt;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001171
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 rtnl_lock();
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001173 ipmr_for_each_table(mrt, net) {
1174 if (sk == mrt->mroute_sk) {
1175 IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001177 write_lock_bh(&mrt_lock);
1178 mrt->mroute_sk = NULL;
1179 write_unlock_bh(&mrt_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001181 mroute_clean_tables(mrt);
1182 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 }
1184 rtnl_unlock();
1185}
1186
1187/*
1188 * Socket options and virtual interface manipulation. The whole
1189 * virtual interface system is a complete heap, but unfortunately
1190 * that's how BSD mrouted happens to think. Maybe one day with a proper
1191 * MOSPF/PIM router set up we can clean this up.
1192 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001193
David S. Millerb7058842009-09-30 16:12:20 -07001194int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195{
1196 int ret;
1197 struct vifctl vif;
1198 struct mfcctl mfc;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001199 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001200 struct mr_table *mrt;
1201
1202 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1203 if (mrt == NULL)
1204 return -ENOENT;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001205
Stephen Hemminger132adf52007-03-08 20:44:43 -08001206 if (optname != MRT_INIT) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001207 if (sk != mrt->mroute_sk && !capable(CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 return -EACCES;
1209 }
1210
Stephen Hemminger132adf52007-03-08 20:44:43 -08001211 switch (optname) {
1212 case MRT_INIT:
1213 if (sk->sk_type != SOCK_RAW ||
Eric Dumazetc720c7e82009-10-15 06:30:45 +00001214 inet_sk(sk)->inet_num != IPPROTO_IGMP)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001215 return -EOPNOTSUPP;
Jianjun Kongc354e122008-11-03 00:28:02 -08001216 if (optlen != sizeof(int))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001217 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
Stephen Hemminger132adf52007-03-08 20:44:43 -08001219 rtnl_lock();
Patrick McHardy0c122952010-04-13 05:03:22 +00001220 if (mrt->mroute_sk) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 rtnl_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -08001222 return -EADDRINUSE;
1223 }
1224
1225 ret = ip_ra_control(sk, 1, mrtsock_destruct);
1226 if (ret == 0) {
1227 write_lock_bh(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001228 mrt->mroute_sk = sk;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001229 write_unlock_bh(&mrt_lock);
1230
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001231 IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001232 }
1233 rtnl_unlock();
1234 return ret;
1235 case MRT_DONE:
Patrick McHardy0c122952010-04-13 05:03:22 +00001236 if (sk != mrt->mroute_sk)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001237 return -EACCES;
1238 return ip_ra_control(sk, 0, NULL);
1239 case MRT_ADD_VIF:
1240 case MRT_DEL_VIF:
Jianjun Kongc354e122008-11-03 00:28:02 -08001241 if (optlen != sizeof(vif))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001242 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -08001243 if (copy_from_user(&vif, optval, sizeof(vif)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001244 return -EFAULT;
1245 if (vif.vifc_vifi >= MAXVIFS)
1246 return -ENFILE;
1247 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -08001248 if (optname == MRT_ADD_VIF) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001249 ret = vif_add(net, mrt, &vif, sk == mrt->mroute_sk);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001250 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00001251 ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001252 }
1253 rtnl_unlock();
1254 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255
1256 /*
1257 * Manipulate the forwarding caches. These live
1258 * in a sort of kernel/user symbiosis.
1259 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001260 case MRT_ADD_MFC:
1261 case MRT_DEL_MFC:
Jianjun Kongc354e122008-11-03 00:28:02 -08001262 if (optlen != sizeof(mfc))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001263 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -08001264 if (copy_from_user(&mfc, optval, sizeof(mfc)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001265 return -EFAULT;
1266 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -08001267 if (optname == MRT_DEL_MFC)
Patrick McHardy0c122952010-04-13 05:03:22 +00001268 ret = ipmr_mfc_delete(mrt, &mfc);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001269 else
Patrick McHardy0c122952010-04-13 05:03:22 +00001270 ret = ipmr_mfc_add(net, mrt, &mfc, sk == mrt->mroute_sk);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001271 rtnl_unlock();
1272 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 /*
1274 * Control PIM assert.
1275 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001276 case MRT_ASSERT:
1277 {
1278 int v;
1279 if (get_user(v,(int __user *)optval))
1280 return -EFAULT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001281 mrt->mroute_do_assert = (v) ? 1 : 0;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001282 return 0;
1283 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284#ifdef CONFIG_IP_PIMSM
Stephen Hemminger132adf52007-03-08 20:44:43 -08001285 case MRT_PIM:
1286 {
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001287 int v;
1288
Stephen Hemminger132adf52007-03-08 20:44:43 -08001289 if (get_user(v,(int __user *)optval))
1290 return -EFAULT;
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001291 v = (v) ? 1 : 0;
1292
Stephen Hemminger132adf52007-03-08 20:44:43 -08001293 rtnl_lock();
1294 ret = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +00001295 if (v != mrt->mroute_do_pim) {
1296 mrt->mroute_do_pim = v;
1297 mrt->mroute_do_assert = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 }
Stephen Hemminger132adf52007-03-08 20:44:43 -08001299 rtnl_unlock();
1300 return ret;
1301 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001303#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
1304 case MRT_TABLE:
1305 {
1306 u32 v;
1307
1308 if (optlen != sizeof(u32))
1309 return -EINVAL;
1310 if (get_user(v, (u32 __user *)optval))
1311 return -EFAULT;
1312 if (sk == mrt->mroute_sk)
1313 return -EBUSY;
1314
1315 rtnl_lock();
1316 ret = 0;
1317 if (!ipmr_new_table(net, v))
1318 ret = -ENOMEM;
1319 raw_sk(sk)->ipmr_table = v;
1320 rtnl_unlock();
1321 return ret;
1322 }
1323#endif
Stephen Hemminger132adf52007-03-08 20:44:43 -08001324 /*
1325 * Spurious command, or MRT_VERSION which you cannot
1326 * set.
1327 */
1328 default:
1329 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 }
1331}
1332
1333/*
1334 * Getsock opt support for the multicast routing system.
1335 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001336
Jianjun Kongc354e122008-11-03 00:28:02 -08001337int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338{
1339 int olr;
1340 int val;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001341 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001342 struct mr_table *mrt;
1343
1344 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1345 if (mrt == NULL)
1346 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347
Jianjun Kongc354e122008-11-03 00:28:02 -08001348 if (optname != MRT_VERSION &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349#ifdef CONFIG_IP_PIMSM
1350 optname!=MRT_PIM &&
1351#endif
1352 optname!=MRT_ASSERT)
1353 return -ENOPROTOOPT;
1354
1355 if (get_user(olr, optlen))
1356 return -EFAULT;
1357
1358 olr = min_t(unsigned int, olr, sizeof(int));
1359 if (olr < 0)
1360 return -EINVAL;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001361
Jianjun Kongc354e122008-11-03 00:28:02 -08001362 if (put_user(olr, optlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return -EFAULT;
Jianjun Kongc354e122008-11-03 00:28:02 -08001364 if (optname == MRT_VERSION)
1365 val = 0x0305;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366#ifdef CONFIG_IP_PIMSM
Jianjun Kongc354e122008-11-03 00:28:02 -08001367 else if (optname == MRT_PIM)
Patrick McHardy0c122952010-04-13 05:03:22 +00001368 val = mrt->mroute_do_pim;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369#endif
1370 else
Patrick McHardy0c122952010-04-13 05:03:22 +00001371 val = mrt->mroute_do_assert;
Jianjun Kongc354e122008-11-03 00:28:02 -08001372 if (copy_to_user(optval, &val, olr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 return -EFAULT;
1374 return 0;
1375}
1376
1377/*
1378 * The IP multicast ioctl support routines.
1379 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001380
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
1382{
1383 struct sioc_sg_req sr;
1384 struct sioc_vif_req vr;
1385 struct vif_device *vif;
1386 struct mfc_cache *c;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001387 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001388 struct mr_table *mrt;
1389
1390 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
1391 if (mrt == NULL)
1392 return -ENOENT;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001393
Stephen Hemminger132adf52007-03-08 20:44:43 -08001394 switch (cmd) {
1395 case SIOCGETVIFCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001396 if (copy_from_user(&vr, arg, sizeof(vr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001397 return -EFAULT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001398 if (vr.vifi >= mrt->maxvif)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001399 return -EINVAL;
1400 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001401 vif = &mrt->vif_table[vr.vifi];
1402 if (VIF_EXISTS(mrt, vr.vifi)) {
Jianjun Kongc354e122008-11-03 00:28:02 -08001403 vr.icount = vif->pkt_in;
1404 vr.ocount = vif->pkt_out;
1405 vr.ibytes = vif->bytes_in;
1406 vr.obytes = vif->bytes_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001408
Jianjun Kongc354e122008-11-03 00:28:02 -08001409 if (copy_to_user(arg, &vr, sizeof(vr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 return -EFAULT;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001411 return 0;
1412 }
1413 read_unlock(&mrt_lock);
1414 return -EADDRNOTAVAIL;
1415 case SIOCGETSGCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001416 if (copy_from_user(&sr, arg, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001417 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
Stephen Hemminger132adf52007-03-08 20:44:43 -08001419 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001420 c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001421 if (c) {
1422 sr.pktcnt = c->mfc_un.res.pkt;
1423 sr.bytecnt = c->mfc_un.res.bytes;
1424 sr.wrong_if = c->mfc_un.res.wrong_if;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001426
Jianjun Kongc354e122008-11-03 00:28:02 -08001427 if (copy_to_user(arg, &sr, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001428 return -EFAULT;
1429 return 0;
1430 }
1431 read_unlock(&mrt_lock);
1432 return -EADDRNOTAVAIL;
1433 default:
1434 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 }
1436}
1437
1438
1439static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
1440{
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001441 struct net_device *dev = ptr;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001442 struct net *net = dev_net(dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001443 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 struct vif_device *v;
1445 int ct;
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001446 LIST_HEAD(list);
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001447
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 if (event != NETDEV_UNREGISTER)
1449 return NOTIFY_DONE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001450
1451 ipmr_for_each_table(mrt, net) {
1452 v = &mrt->vif_table[0];
1453 for (ct = 0; ct < mrt->maxvif; ct++, v++) {
1454 if (v->dev == dev)
1455 vif_delete(mrt, ct, 1, &list);
1456 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 }
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001458 unregister_netdevice_many(&list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 return NOTIFY_DONE;
1460}
1461
1462
Jianjun Kongc354e122008-11-03 00:28:02 -08001463static struct notifier_block ip_mr_notifier = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 .notifier_call = ipmr_device_event,
1465};
1466
1467/*
1468 * Encapsulate a packet by attaching a valid IPIP header to it.
1469 * This avoids tunnel drivers and other mess and gives us the speed so
1470 * important for multicast video.
1471 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001472
Al Viro114c7842006-09-27 18:39:29 -07001473static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474{
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001475 struct iphdr *iph;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001476 struct iphdr *old_iph = ip_hdr(skb);
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001477
1478 skb_push(skb, sizeof(struct iphdr));
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001479 skb->transport_header = skb->network_header;
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001480 skb_reset_network_header(skb);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001481 iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482
1483 iph->version = 4;
Arnaldo Carvalho de Meloe023dd62007-03-12 20:09:36 -03001484 iph->tos = old_iph->tos;
1485 iph->ttl = old_iph->ttl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 iph->frag_off = 0;
1487 iph->daddr = daddr;
1488 iph->saddr = saddr;
1489 iph->protocol = IPPROTO_IPIP;
1490 iph->ihl = 5;
1491 iph->tot_len = htons(skb->len);
Eric Dumazetadf30902009-06-02 05:19:30 +00001492 ip_select_ident(iph, skb_dst(skb), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 ip_send_check(iph);
1494
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
1496 nf_reset(skb);
1497}
1498
1499static inline int ipmr_forward_finish(struct sk_buff *skb)
1500{
1501 struct ip_options * opt = &(IPCB(skb)->opt);
1502
Eric Dumazetadf30902009-06-02 05:19:30 +00001503 IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 if (unlikely(opt->optlen))
1506 ip_forward_options(skb);
1507
1508 return dst_output(skb);
1509}
1510
1511/*
1512 * Processing handlers for ipmr_forward
1513 */
1514
Patrick McHardy0c122952010-04-13 05:03:22 +00001515static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
1516 struct sk_buff *skb, struct mfc_cache *c, int vifi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517{
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001518 const struct iphdr *iph = ip_hdr(skb);
Patrick McHardy0c122952010-04-13 05:03:22 +00001519 struct vif_device *vif = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 struct net_device *dev;
1521 struct rtable *rt;
1522 int encap = 0;
1523
1524 if (vif->dev == NULL)
1525 goto out_free;
1526
1527#ifdef CONFIG_IP_PIMSM
1528 if (vif->flags & VIFF_REGISTER) {
1529 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001530 vif->bytes_out += skb->len;
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -07001531 vif->dev->stats.tx_bytes += skb->len;
1532 vif->dev->stats.tx_packets++;
Patrick McHardy0c122952010-04-13 05:03:22 +00001533 ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
Ilpo Järvinen69ebbf52009-02-06 23:46:51 -08001534 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 }
1536#endif
1537
1538 if (vif->flags&VIFF_TUNNEL) {
1539 struct flowi fl = { .oif = vif->link,
1540 .nl_u = { .ip4_u =
1541 { .daddr = vif->remote,
1542 .saddr = vif->local,
1543 .tos = RT_TOS(iph->tos) } },
1544 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001545 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 goto out_free;
1547 encap = sizeof(struct iphdr);
1548 } else {
1549 struct flowi fl = { .oif = vif->link,
1550 .nl_u = { .ip4_u =
1551 { .daddr = iph->daddr,
1552 .tos = RT_TOS(iph->tos) } },
1553 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001554 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 goto out_free;
1556 }
1557
1558 dev = rt->u.dst.dev;
1559
1560 if (skb->len+encap > dst_mtu(&rt->u.dst) && (ntohs(iph->frag_off) & IP_DF)) {
1561 /* Do not fragment multicasts. Alas, IPv4 does not
1562 allow to send ICMP, so that packets will disappear
1563 to blackhole.
1564 */
1565
Pavel Emelyanov7c73a6f2008-07-16 20:20:11 -07001566 IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 ip_rt_put(rt);
1568 goto out_free;
1569 }
1570
1571 encap += LL_RESERVED_SPACE(dev) + rt->u.dst.header_len;
1572
1573 if (skb_cow(skb, encap)) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001574 ip_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 goto out_free;
1576 }
1577
1578 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001579 vif->bytes_out += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
Eric Dumazetadf30902009-06-02 05:19:30 +00001581 skb_dst_drop(skb);
1582 skb_dst_set(skb, &rt->u.dst);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001583 ip_decrease_ttl(ip_hdr(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584
1585 /* FIXME: forward and output firewalls used to be called here.
1586 * What do we do with netfilter? -- RR */
1587 if (vif->flags & VIFF_TUNNEL) {
1588 ip_encap(skb, vif->local, vif->remote);
1589 /* FIXME: extra output firewall step used to be here. --RR */
Pavel Emelyanov2f4c02d2008-05-21 14:16:14 -07001590 vif->dev->stats.tx_packets++;
1591 vif->dev->stats.tx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 }
1593
1594 IPCB(skb)->flags |= IPSKB_FORWARDED;
1595
1596 /*
1597 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
1598 * not only before forwarding, but after forwarding on all output
1599 * interfaces. It is clear, if mrouter runs a multicasting
1600 * program, it should receive packets not depending to what interface
1601 * program is joined.
1602 * If we will not make it, the program will have to join on all
1603 * interfaces. On the other hand, multihoming host (or router, but
1604 * not mrouter) cannot join to more than one interface - it will
1605 * result in receiving multiple packets.
1606 */
Jan Engelhardt9bbc7682010-03-23 04:07:29 +01001607 NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 ipmr_forward_finish);
1609 return;
1610
1611out_free:
1612 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613}
1614
Patrick McHardy0c122952010-04-13 05:03:22 +00001615static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616{
1617 int ct;
Patrick McHardy0c122952010-04-13 05:03:22 +00001618
1619 for (ct = mrt->maxvif-1; ct >= 0; ct--) {
1620 if (mrt->vif_table[ct].dev == dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 break;
1622 }
1623 return ct;
1624}
1625
1626/* "local" means that we should preserve one skb (for local delivery) */
1627
Patrick McHardy0c122952010-04-13 05:03:22 +00001628static int ip_mr_forward(struct net *net, struct mr_table *mrt,
1629 struct sk_buff *skb, struct mfc_cache *cache,
1630 int local)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631{
1632 int psend = -1;
1633 int vif, ct;
1634
1635 vif = cache->mfc_parent;
1636 cache->mfc_un.res.pkt++;
1637 cache->mfc_un.res.bytes += skb->len;
1638
1639 /*
1640 * Wrong interface: drop packet and (maybe) send PIM assert.
1641 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001642 if (mrt->vif_table[vif].dev != skb->dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 int true_vifi;
1644
Eric Dumazet511c3f92009-06-02 05:14:27 +00001645 if (skb_rtable(skb)->fl.iif == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 /* It is our own packet, looped back.
1647 Very complicated situation...
1648
1649 The best workaround until routing daemons will be
1650 fixed is not to redistribute packet, if it was
1651 send through wrong interface. It means, that
1652 multicast applications WILL NOT work for
1653 (S,G), which have default multicast route pointing
1654 to wrong oif. In any case, it is not a good
1655 idea to use multicasting applications on router.
1656 */
1657 goto dont_forward;
1658 }
1659
1660 cache->mfc_un.res.wrong_if++;
Patrick McHardy0c122952010-04-13 05:03:22 +00001661 true_vifi = ipmr_find_vif(mrt, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
Patrick McHardy0c122952010-04-13 05:03:22 +00001663 if (true_vifi >= 0 && mrt->mroute_do_assert &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 /* pimsm uses asserts, when switching from RPT to SPT,
1665 so that we cannot check that packet arrived on an oif.
1666 It is bad, but otherwise we would need to move pretty
1667 large chunk of pimd to kernel. Ough... --ANK
1668 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001669 (mrt->mroute_do_pim ||
Benjamin Thery6f9374a2009-01-22 04:56:20 +00001670 cache->mfc_un.res.ttls[true_vifi] < 255) &&
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001671 time_after(jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
1673 cache->mfc_un.res.last_assert = jiffies;
Patrick McHardy0c122952010-04-13 05:03:22 +00001674 ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 }
1676 goto dont_forward;
1677 }
1678
Patrick McHardy0c122952010-04-13 05:03:22 +00001679 mrt->vif_table[vif].pkt_in++;
1680 mrt->vif_table[vif].bytes_in += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681
1682 /*
1683 * Forward the frame
1684 */
1685 for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001686 if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 if (psend != -1) {
1688 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1689 if (skb2)
Patrick McHardy0c122952010-04-13 05:03:22 +00001690 ipmr_queue_xmit(net, mrt, skb2, cache,
1691 psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 }
Jianjun Kongc354e122008-11-03 00:28:02 -08001693 psend = ct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 }
1695 }
1696 if (psend != -1) {
1697 if (local) {
1698 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1699 if (skb2)
Patrick McHardy0c122952010-04-13 05:03:22 +00001700 ipmr_queue_xmit(net, mrt, skb2, cache, psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00001702 ipmr_queue_xmit(net, mrt, skb, cache, psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 return 0;
1704 }
1705 }
1706
1707dont_forward:
1708 if (!local)
1709 kfree_skb(skb);
1710 return 0;
1711}
1712
1713
1714/*
1715 * Multicast packets for forwarding arrive here
1716 */
1717
1718int ip_mr_input(struct sk_buff *skb)
1719{
1720 struct mfc_cache *cache;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001721 struct net *net = dev_net(skb->dev);
Eric Dumazet511c3f92009-06-02 05:14:27 +00001722 int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001723 struct mr_table *mrt;
1724 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725
1726 /* Packet is looped back after forward, it should not be
1727 forwarded second time, but still can be delivered locally.
1728 */
1729 if (IPCB(skb)->flags&IPSKB_FORWARDED)
1730 goto dont_forward;
1731
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001732 err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt);
Ben Greeare40dbc52010-07-15 13:22:33 +00001733 if (err < 0) {
1734 kfree_skb(skb);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001735 return err;
Ben Greeare40dbc52010-07-15 13:22:33 +00001736 }
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001737
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 if (!local) {
1739 if (IPCB(skb)->opt.router_alert) {
1740 if (ip_call_ra_chain(skb))
1741 return 0;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001742 } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 /* IGMPv1 (and broken IGMPv2 implementations sort of
1744 Cisco IOS <= 11.2(8)) do not put router alert
1745 option to IGMP packets destined to routable
1746 groups. It is very bad, because it means
1747 that we can forward NO IGMP messages.
1748 */
1749 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001750 if (mrt->mroute_sk) {
Patrick McHardy2715bcf2005-06-21 14:06:24 -07001751 nf_reset(skb);
Patrick McHardy0c122952010-04-13 05:03:22 +00001752 raw_rcv(mrt->mroute_sk, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 read_unlock(&mrt_lock);
1754 return 0;
1755 }
1756 read_unlock(&mrt_lock);
1757 }
1758 }
1759
1760 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001761 cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762
1763 /*
1764 * No usable cache entry
1765 */
Jianjun Kongc354e122008-11-03 00:28:02 -08001766 if (cache == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767 int vif;
1768
1769 if (local) {
1770 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1771 ip_local_deliver(skb);
1772 if (skb2 == NULL) {
1773 read_unlock(&mrt_lock);
1774 return -ENOBUFS;
1775 }
1776 skb = skb2;
1777 }
1778
Patrick McHardy0c122952010-04-13 05:03:22 +00001779 vif = ipmr_find_vif(mrt, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780 if (vif >= 0) {
Eric Dumazet0eae88f2010-04-20 19:06:52 -07001781 int err2 = ipmr_cache_unresolved(mrt, vif, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 read_unlock(&mrt_lock);
1783
Eric Dumazet0eae88f2010-04-20 19:06:52 -07001784 return err2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785 }
1786 read_unlock(&mrt_lock);
1787 kfree_skb(skb);
1788 return -ENODEV;
1789 }
1790
Patrick McHardy0c122952010-04-13 05:03:22 +00001791 ip_mr_forward(net, mrt, skb, cache, local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
1793 read_unlock(&mrt_lock);
1794
1795 if (local)
1796 return ip_local_deliver(skb);
1797
1798 return 0;
1799
1800dont_forward:
1801 if (local)
1802 return ip_local_deliver(skb);
1803 kfree_skb(skb);
1804 return 0;
1805}
1806
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001807#ifdef CONFIG_IP_PIMSM
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001808static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
1809 unsigned int pimlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810{
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001811 struct net_device *reg_dev = NULL;
1812 struct iphdr *encap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001814 encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 /*
1816 Check that:
1817 a. packet is really destinted to a multicast group
1818 b. packet is not a NULL-REGISTER
1819 c. packet is not truncated
1820 */
Joe Perchesf97c1e02007-12-16 13:45:43 -08001821 if (!ipv4_is_multicast(encap->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 encap->tot_len == 0 ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001823 ntohs(encap->tot_len) + pimlen > skb->len)
1824 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
1826 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001827 if (mrt->mroute_reg_vif_num >= 0)
1828 reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 if (reg_dev)
1830 dev_hold(reg_dev);
1831 read_unlock(&mrt_lock);
1832
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001833 if (reg_dev == NULL)
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001834 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001836 skb->mac_header = skb->network_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 skb_pull(skb, (u8*)encap - skb->data);
Arnaldo Carvalho de Melo31c77112007-03-10 19:04:55 -03001838 skb_reset_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839 skb->protocol = htons(ETH_P_IP);
1840 skb->ip_summed = 0;
1841 skb->pkt_type = PACKET_HOST;
Eric Dumazetd19d56d2010-05-17 22:36:55 -07001842
1843 skb_tunnel_rx(skb, reg_dev);
1844
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 netif_rx(skb);
1846 dev_put(reg_dev);
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001847
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 return 0;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001849}
1850#endif
1851
1852#ifdef CONFIG_IP_PIMSM_V1
1853/*
1854 * Handle IGMP messages of PIMv1
1855 */
1856
1857int pim_rcv_v1(struct sk_buff * skb)
1858{
1859 struct igmphdr *pim;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001860 struct net *net = dev_net(skb->dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001861 struct mr_table *mrt;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001862
1863 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
1864 goto drop;
1865
1866 pim = igmp_hdr(skb);
1867
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001868 if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
1869 goto drop;
1870
Patrick McHardy0c122952010-04-13 05:03:22 +00001871 if (!mrt->mroute_do_pim ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001872 pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
1873 goto drop;
1874
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001875 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001876drop:
1877 kfree_skb(skb);
1878 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 return 0;
1880}
1881#endif
1882
1883#ifdef CONFIG_IP_PIMSM_V2
1884static int pim_rcv(struct sk_buff * skb)
1885{
1886 struct pimreghdr *pim;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001887 struct net *net = dev_net(skb->dev);
1888 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001890 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 goto drop;
1892
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001893 pim = (struct pimreghdr *)skb_transport_header(skb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001894 if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 (pim->flags&PIM_NULL_REGISTER) ||
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001896 (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
Al Virod3bc23e2006-11-14 21:24:49 -08001897 csum_fold(skb_checksum(skb, 0, skb->len, 0))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 goto drop;
1899
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001900 if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
1901 goto drop;
1902
1903 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001904drop:
1905 kfree_skb(skb);
1906 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 return 0;
1908}
1909#endif
1910
Patrick McHardycb6a4e42010-04-26 16:02:08 +02001911static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
1912 struct mfc_cache *c, struct rtmsg *rtm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913{
1914 int ct;
1915 struct rtnexthop *nhp;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001916 u8 *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 struct rtattr *mp_head;
1918
Nicolas Dichtel74381892010-03-25 23:45:35 +00001919 /* If cache is unresolved, don't try to parse IIF and OIF */
Dan Carpentered0f160a2010-05-26 00:38:56 -07001920 if (c->mfc_parent >= MAXVIFS)
Nicolas Dichtel74381892010-03-25 23:45:35 +00001921 return -ENOENT;
1922
Patrick McHardy0c122952010-04-13 05:03:22 +00001923 if (VIF_EXISTS(mrt, c->mfc_parent))
1924 RTA_PUT(skb, RTA_IIF, 4, &mrt->vif_table[c->mfc_parent].dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925
Jianjun Kongc354e122008-11-03 00:28:02 -08001926 mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927
1928 for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001929 if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
1931 goto rtattr_failure;
Jianjun Kongc354e122008-11-03 00:28:02 -08001932 nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 nhp->rtnh_flags = 0;
1934 nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
Patrick McHardy0c122952010-04-13 05:03:22 +00001935 nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 nhp->rtnh_len = sizeof(*nhp);
1937 }
1938 }
1939 mp_head->rta_type = RTA_MULTIPATH;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001940 mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 rtm->rtm_type = RTN_MULTICAST;
1942 return 1;
1943
1944rtattr_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -07001945 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 return -EMSGSIZE;
1947}
1948
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001949int ipmr_get_route(struct net *net,
1950 struct sk_buff *skb, struct rtmsg *rtm, int nowait)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951{
1952 int err;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001953 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 struct mfc_cache *cache;
Eric Dumazet511c3f92009-06-02 05:14:27 +00001955 struct rtable *rt = skb_rtable(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001957 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
1958 if (mrt == NULL)
1959 return -ENOENT;
1960
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001962 cache = ipmr_cache_find(mrt, rt->rt_src, rt->rt_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963
Jianjun Kongc354e122008-11-03 00:28:02 -08001964 if (cache == NULL) {
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001965 struct sk_buff *skb2;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001966 struct iphdr *iph;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 struct net_device *dev;
1968 int vif;
1969
1970 if (nowait) {
1971 read_unlock(&mrt_lock);
1972 return -EAGAIN;
1973 }
1974
1975 dev = skb->dev;
Patrick McHardy0c122952010-04-13 05:03:22 +00001976 if (dev == NULL || (vif = ipmr_find_vif(mrt, dev)) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 read_unlock(&mrt_lock);
1978 return -ENODEV;
1979 }
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001980 skb2 = skb_clone(skb, GFP_ATOMIC);
1981 if (!skb2) {
1982 read_unlock(&mrt_lock);
1983 return -ENOMEM;
1984 }
1985
Arnaldo Carvalho de Meloe2d1bca2007-04-10 20:46:21 -07001986 skb_push(skb2, sizeof(struct iphdr));
1987 skb_reset_network_header(skb2);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001988 iph = ip_hdr(skb2);
1989 iph->ihl = sizeof(struct iphdr) >> 2;
1990 iph->saddr = rt->rt_src;
1991 iph->daddr = rt->rt_dst;
1992 iph->version = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +00001993 err = ipmr_cache_unresolved(mrt, vif, skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 read_unlock(&mrt_lock);
1995 return err;
1996 }
1997
1998 if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
1999 cache->mfc_flags |= MFC_NOTIFY;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002000 err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 read_unlock(&mrt_lock);
2002 return err;
2003}
2004
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002005static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
2006 u32 pid, u32 seq, struct mfc_cache *c)
2007{
2008 struct nlmsghdr *nlh;
2009 struct rtmsg *rtm;
2010
2011 nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
2012 if (nlh == NULL)
2013 return -EMSGSIZE;
2014
2015 rtm = nlmsg_data(nlh);
2016 rtm->rtm_family = RTNL_FAMILY_IPMR;
2017 rtm->rtm_dst_len = 32;
2018 rtm->rtm_src_len = 32;
2019 rtm->rtm_tos = 0;
2020 rtm->rtm_table = mrt->id;
2021 NLA_PUT_U32(skb, RTA_TABLE, mrt->id);
2022 rtm->rtm_type = RTN_MULTICAST;
2023 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2024 rtm->rtm_protocol = RTPROT_UNSPEC;
2025 rtm->rtm_flags = 0;
2026
2027 NLA_PUT_BE32(skb, RTA_SRC, c->mfc_origin);
2028 NLA_PUT_BE32(skb, RTA_DST, c->mfc_mcastgrp);
2029
2030 if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0)
2031 goto nla_put_failure;
2032
2033 return nlmsg_end(skb, nlh);
2034
2035nla_put_failure:
2036 nlmsg_cancel(skb, nlh);
2037 return -EMSGSIZE;
2038}
2039
2040static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
2041{
2042 struct net *net = sock_net(skb->sk);
2043 struct mr_table *mrt;
2044 struct mfc_cache *mfc;
2045 unsigned int t = 0, s_t;
2046 unsigned int h = 0, s_h;
2047 unsigned int e = 0, s_e;
2048
2049 s_t = cb->args[0];
2050 s_h = cb->args[1];
2051 s_e = cb->args[2];
2052
2053 read_lock(&mrt_lock);
2054 ipmr_for_each_table(mrt, net) {
2055 if (t < s_t)
2056 goto next_table;
2057 if (t > s_t)
2058 s_h = 0;
2059 for (h = s_h; h < MFC_LINES; h++) {
2060 list_for_each_entry(mfc, &mrt->mfc_cache_array[h], list) {
2061 if (e < s_e)
2062 goto next_entry;
2063 if (ipmr_fill_mroute(mrt, skb,
2064 NETLINK_CB(cb->skb).pid,
2065 cb->nlh->nlmsg_seq,
2066 mfc) < 0)
2067 goto done;
2068next_entry:
2069 e++;
2070 }
2071 e = s_e = 0;
2072 }
2073 s_h = 0;
2074next_table:
2075 t++;
2076 }
2077done:
2078 read_unlock(&mrt_lock);
2079
2080 cb->args[2] = e;
2081 cb->args[1] = h;
2082 cb->args[0] = t;
2083
2084 return skb->len;
2085}
2086
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002087#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088/*
2089 * The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
2090 */
2091struct ipmr_vif_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002092 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002093 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 int ct;
2095};
2096
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002097static struct vif_device *ipmr_vif_seq_idx(struct net *net,
2098 struct ipmr_vif_iter *iter,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 loff_t pos)
2100{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002101 struct mr_table *mrt = iter->mrt;
Patrick McHardy0c122952010-04-13 05:03:22 +00002102
2103 for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) {
2104 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 continue;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002106 if (pos-- == 0)
Patrick McHardy0c122952010-04-13 05:03:22 +00002107 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 }
2109 return NULL;
2110}
2111
2112static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002113 __acquires(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002115 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002116 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002117 struct mr_table *mrt;
2118
2119 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2120 if (mrt == NULL)
2121 return ERR_PTR(-ENOENT);
2122
2123 iter->mrt = mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002124
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 read_lock(&mrt_lock);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002126 return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 : SEQ_START_TOKEN;
2128}
2129
2130static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2131{
2132 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002133 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002134 struct mr_table *mrt = iter->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
2136 ++*pos;
2137 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002138 return ipmr_vif_seq_idx(net, iter, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002139
Patrick McHardy0c122952010-04-13 05:03:22 +00002140 while (++iter->ct < mrt->maxvif) {
2141 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 continue;
Patrick McHardy0c122952010-04-13 05:03:22 +00002143 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 }
2145 return NULL;
2146}
2147
2148static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002149 __releases(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150{
2151 read_unlock(&mrt_lock);
2152}
2153
2154static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
2155{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002156 struct ipmr_vif_iter *iter = seq->private;
2157 struct mr_table *mrt = iter->mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002158
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002160 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n");
2162 } else {
2163 const struct vif_device *vif = v;
2164 const char *name = vif->dev ? vif->dev->name : "none";
2165
2166 seq_printf(seq,
2167 "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n",
Patrick McHardy0c122952010-04-13 05:03:22 +00002168 vif - mrt->vif_table,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002169 name, vif->bytes_in, vif->pkt_in,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 vif->bytes_out, vif->pkt_out,
2171 vif->flags, vif->local, vif->remote);
2172 }
2173 return 0;
2174}
2175
Stephen Hemmingerf6908082007-03-12 14:34:29 -07002176static const struct seq_operations ipmr_vif_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 .start = ipmr_vif_seq_start,
2178 .next = ipmr_vif_seq_next,
2179 .stop = ipmr_vif_seq_stop,
2180 .show = ipmr_vif_seq_show,
2181};
2182
2183static int ipmr_vif_open(struct inode *inode, struct file *file)
2184{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002185 return seq_open_net(inode, file, &ipmr_vif_seq_ops,
2186 sizeof(struct ipmr_vif_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187}
2188
Arjan van de Ven9a321442007-02-12 00:55:35 -08002189static const struct file_operations ipmr_vif_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 .owner = THIS_MODULE,
2191 .open = ipmr_vif_open,
2192 .read = seq_read,
2193 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002194 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195};
2196
2197struct ipmr_mfc_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002198 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002199 struct mr_table *mrt;
Patrick McHardy862465f2010-04-13 05:03:21 +00002200 struct list_head *cache;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 int ct;
2202};
2203
2204
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002205static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
2206 struct ipmr_mfc_iter *it, loff_t pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002208 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 struct mfc_cache *mfc;
2210
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 read_lock(&mrt_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00002212 for (it->ct = 0; it->ct < MFC_LINES; it->ct++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002213 it->cache = &mrt->mfc_cache_array[it->ct];
Patrick McHardy862465f2010-04-13 05:03:21 +00002214 list_for_each_entry(mfc, it->cache, list)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002215 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216 return mfc;
Patrick McHardy862465f2010-04-13 05:03:21 +00002217 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 read_unlock(&mrt_lock);
2219
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002221 it->cache = &mrt->mfc_unres_queue;
Patrick McHardy862465f2010-04-13 05:03:21 +00002222 list_for_each_entry(mfc, it->cache, list)
Patrick McHardye258beb2010-04-13 05:03:19 +00002223 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 return mfc;
2225 spin_unlock_bh(&mfc_unres_lock);
2226
2227 it->cache = NULL;
2228 return NULL;
2229}
2230
2231
2232static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
2233{
2234 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002235 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002236 struct mr_table *mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002237
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002238 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
2239 if (mrt == NULL)
2240 return ERR_PTR(-ENOENT);
2241
2242 it->mrt = mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 it->cache = NULL;
2244 it->ct = 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002245 return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 : SEQ_START_TOKEN;
2247}
2248
2249static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2250{
2251 struct mfc_cache *mfc = v;
2252 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002253 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002254 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255
2256 ++*pos;
2257
2258 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002259 return ipmr_mfc_seq_idx(net, seq->private, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260
Patrick McHardy862465f2010-04-13 05:03:21 +00002261 if (mfc->list.next != it->cache)
2262 return list_entry(mfc->list.next, struct mfc_cache, list);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002263
Patrick McHardy0c122952010-04-13 05:03:22 +00002264 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 goto end_of_list;
2266
Patrick McHardy0c122952010-04-13 05:03:22 +00002267 BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268
2269 while (++it->ct < MFC_LINES) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002270 it->cache = &mrt->mfc_cache_array[it->ct];
Patrick McHardy862465f2010-04-13 05:03:21 +00002271 if (list_empty(it->cache))
2272 continue;
2273 return list_first_entry(it->cache, struct mfc_cache, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 }
2275
2276 /* exhausted cache_array, show unresolved */
2277 read_unlock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002278 it->cache = &mrt->mfc_unres_queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 it->ct = 0;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002280
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00002282 if (!list_empty(it->cache))
2283 return list_first_entry(it->cache, struct mfc_cache, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284
2285 end_of_list:
2286 spin_unlock_bh(&mfc_unres_lock);
2287 it->cache = NULL;
2288
2289 return NULL;
2290}
2291
2292static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
2293{
2294 struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002295 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296
Patrick McHardy0c122952010-04-13 05:03:22 +00002297 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 spin_unlock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00002299 else if (it->cache == &mrt->mfc_cache_array[it->ct])
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 read_unlock(&mrt_lock);
2301}
2302
2303static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
2304{
2305 int n;
2306
2307 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002308 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309 "Group Origin Iif Pkts Bytes Wrong Oifs\n");
2310 } else {
2311 const struct mfc_cache *mfc = v;
2312 const struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002313 const struct mr_table *mrt = it->mrt;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002314
Eric Dumazet0eae88f2010-04-20 19:06:52 -07002315 seq_printf(seq, "%08X %08X %-3hd",
2316 (__force u32) mfc->mfc_mcastgrp,
2317 (__force u32) mfc->mfc_origin,
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002318 mfc->mfc_parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
Patrick McHardy0c122952010-04-13 05:03:22 +00002320 if (it->cache != &mrt->mfc_unres_queue) {
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002321 seq_printf(seq, " %8lu %8lu %8lu",
2322 mfc->mfc_un.res.pkt,
2323 mfc->mfc_un.res.bytes,
2324 mfc->mfc_un.res.wrong_if);
Stephen Hemminger132adf52007-03-08 20:44:43 -08002325 for (n = mfc->mfc_un.res.minvif;
2326 n < mfc->mfc_un.res.maxvif; n++ ) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002327 if (VIF_EXISTS(mrt, n) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +00002328 mfc->mfc_un.res.ttls[n] < 255)
2329 seq_printf(seq,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002330 " %2d:%-3d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 n, mfc->mfc_un.res.ttls[n]);
2332 }
Benjamin Thery1ea472e2008-12-03 22:21:47 -08002333 } else {
2334 /* unresolved mfc_caches don't contain
2335 * pkt, bytes and wrong_if values
2336 */
2337 seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 }
2339 seq_putc(seq, '\n');
2340 }
2341 return 0;
2342}
2343
Stephen Hemmingerf6908082007-03-12 14:34:29 -07002344static const struct seq_operations ipmr_mfc_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 .start = ipmr_mfc_seq_start,
2346 .next = ipmr_mfc_seq_next,
2347 .stop = ipmr_mfc_seq_stop,
2348 .show = ipmr_mfc_seq_show,
2349};
2350
2351static int ipmr_mfc_open(struct inode *inode, struct file *file)
2352{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002353 return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
2354 sizeof(struct ipmr_mfc_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355}
2356
Arjan van de Ven9a321442007-02-12 00:55:35 -08002357static const struct file_operations ipmr_mfc_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 .owner = THIS_MODULE,
2359 .open = ipmr_mfc_open,
2360 .read = seq_read,
2361 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002362 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002364#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365
2366#ifdef CONFIG_IP_PIMSM_V2
Alexey Dobriyan32613092009-09-14 12:21:47 +00002367static const struct net_protocol pim_protocol = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368 .handler = pim_rcv,
Tom Goff403dbb92009-06-14 03:16:13 -07002369 .netns_ok = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370};
2371#endif
2372
2373
2374/*
2375 * Setup for IP multicast routing
2376 */
Benjamin Therycf958ae32009-01-22 04:56:16 +00002377static int __net_init ipmr_net_init(struct net *net)
2378{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002379 int err;
Benjamin Therycf958ae32009-01-22 04:56:16 +00002380
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002381 err = ipmr_rules_init(net);
2382 if (err < 0)
Benjamin Therycf958ae32009-01-22 04:56:16 +00002383 goto fail;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002384
2385#ifdef CONFIG_PROC_FS
2386 err = -ENOMEM;
2387 if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops))
2388 goto proc_vif_fail;
2389 if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops))
2390 goto proc_cache_fail;
2391#endif
Benjamin Thery2bb8b262009-01-22 04:56:18 +00002392 return 0;
2393
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002394#ifdef CONFIG_PROC_FS
2395proc_cache_fail:
2396 proc_net_remove(net, "ip_mr_vif");
2397proc_vif_fail:
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002398 ipmr_rules_exit(net);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002399#endif
Benjamin Therycf958ae32009-01-22 04:56:16 +00002400fail:
2401 return err;
2402}
2403
2404static void __net_exit ipmr_net_exit(struct net *net)
2405{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002406#ifdef CONFIG_PROC_FS
2407 proc_net_remove(net, "ip_mr_cache");
2408 proc_net_remove(net, "ip_mr_vif");
2409#endif
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002410 ipmr_rules_exit(net);
Benjamin Therycf958ae32009-01-22 04:56:16 +00002411}
2412
2413static struct pernet_operations ipmr_net_ops = {
2414 .init = ipmr_net_init,
2415 .exit = ipmr_net_exit,
2416};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002417
Wang Chen03d2f892008-07-03 12:13:36 +08002418int __init ip_mr_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419{
Wang Chen03d2f892008-07-03 12:13:36 +08002420 int err;
2421
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422 mrt_cachep = kmem_cache_create("ip_mrt_cache",
2423 sizeof(struct mfc_cache),
Alexey Dobriyane5d679f332006-08-26 19:25:52 -07002424 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
Paul Mundt20c2df82007-07-20 10:11:58 +09002425 NULL);
Wang Chen03d2f892008-07-03 12:13:36 +08002426 if (!mrt_cachep)
2427 return -ENOMEM;
2428
Benjamin Therycf958ae32009-01-22 04:56:16 +00002429 err = register_pernet_subsys(&ipmr_net_ops);
2430 if (err)
2431 goto reg_pernet_fail;
2432
Wang Chen03d2f892008-07-03 12:13:36 +08002433 err = register_netdevice_notifier(&ip_mr_notifier);
2434 if (err)
2435 goto reg_notif_fail;
Tom Goff403dbb92009-06-14 03:16:13 -07002436#ifdef CONFIG_IP_PIMSM_V2
2437 if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) {
2438 printk(KERN_ERR "ip_mr_init: can't add PIM protocol\n");
2439 err = -EAGAIN;
2440 goto add_proto_fail;
2441 }
2442#endif
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002443 rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute);
Wang Chen03d2f892008-07-03 12:13:36 +08002444 return 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002445
Tom Goff403dbb92009-06-14 03:16:13 -07002446#ifdef CONFIG_IP_PIMSM_V2
2447add_proto_fail:
2448 unregister_netdevice_notifier(&ip_mr_notifier);
2449#endif
Benjamin Theryc3e38892008-11-19 14:07:41 -08002450reg_notif_fail:
Benjamin Therycf958ae32009-01-22 04:56:16 +00002451 unregister_pernet_subsys(&ipmr_net_ops);
2452reg_pernet_fail:
Benjamin Theryc3e38892008-11-19 14:07:41 -08002453 kmem_cache_destroy(mrt_cachep);
Wang Chen03d2f892008-07-03 12:13:36 +08002454 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455}