blob: 292a8e80bdfaa8242240ace8ee6b35e51d541239 [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 Torvalds7c0f6ba2016-12-24 11:46:01 -080029#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080031#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/errno.h>
33#include <linux/timer.h>
34#include <linux/mm.h>
35#include <linux/kernel.h>
36#include <linux/fcntl.h>
37#include <linux/stat.h>
38#include <linux/socket.h>
39#include <linux/in.h>
40#include <linux/inet.h>
41#include <linux/netdevice.h>
42#include <linux/inetdevice.h>
43#include <linux/igmp.h>
44#include <linux/proc_fs.h>
45#include <linux/seq_file.h>
46#include <linux/mroute.h>
47#include <linux/init.h>
Kris Katterjohn46f25df2006-01-05 16:35:42 -080048#include <linux/if_ether.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090049#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020050#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include <net/ip.h>
52#include <net/protocol.h>
53#include <linux/skbuff.h>
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020054#include <net/route.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#include <net/sock.h>
56#include <net/icmp.h>
57#include <net/udp.h>
58#include <net/raw.h>
59#include <linux/notifier.h>
60#include <linux/if_arp.h>
61#include <linux/netfilter_ipv4.h>
Eric W. Biederman709b46e2011-01-29 16:15:56 +000062#include <linux/compat.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040063#include <linux/export.h>
Pravin B Shelarc5441932013-03-25 14:49:35 +000064#include <net/ip_tunnels.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#include <net/checksum.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070066#include <net/netlink.h>
Patrick McHardyf0ad0862010-04-13 05:03:23 +000067#include <net/fib_rules.h>
Nicolas Dichteld67b8c62012-12-04 01:13:35 +000068#include <linux/netconf.h>
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +010069#include <net/nexthop.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
Patrick McHardyf0ad0862010-04-13 05:03:23 +000071struct ipmr_rule {
72 struct fib_rule common;
73};
74
75struct ipmr_result {
76 struct mr_table *mrt;
77};
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079/* Big lock, protecting vif table, mrt cache and mroute socket state.
Eric Dumazeta8cb16d2010-10-01 16:15:29 +000080 * Note that the changes are semaphored via rtnl_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 */
82
83static DEFINE_RWLOCK(mrt_lock);
84
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +010085/* Multicast router control variables */
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Linus Torvalds1da177e2005-04-16 15:20:36 -070087/* Special spinlock for queue of unresolved entries */
88static DEFINE_SPINLOCK(mfc_unres_lock);
89
90/* We return to original Alan's scheme. Hash table of resolved
Eric Dumazeta8cb16d2010-10-01 16:15:29 +000091 * entries is changed only in process context and protected
92 * with weak lock mrt_lock. Queue of unresolved entries is protected
93 * with strong spinlock mfc_unres_lock.
94 *
95 * In this case data path is free of exclusive locks at all.
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 */
97
Christoph Lametere18b8902006-12-06 20:33:20 -080098static struct kmem_cache *mrt_cachep __read_mostly;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000100static struct mr_table *ipmr_new_table(struct net *net, u32 id);
Francesco Ruggeriacbb2192012-08-24 07:38:35 +0000101static void ipmr_free_table(struct mr_table *mrt);
102
Rami Rosenc4854ec2013-07-20 15:09:28 +0300103static void ip_mr_forward(struct net *net, struct mr_table *mrt,
Donald Sharp4b1f0d32017-06-10 16:30:17 -0400104 struct net_device *dev, struct sk_buff *skb,
105 struct mfc_cache *cache, int local);
Patrick McHardy0c122952010-04-13 05:03:22 +0000106static int ipmr_cache_report(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000107 struct sk_buff *pkt, vifi_t vifi, int assert);
Patrick McHardycb6a4e42010-04-26 16:02:08 +0200108static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
109 struct mfc_cache *c, struct rtmsg *rtm);
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +0000110static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
111 int cmd);
Julien Gomes5a645dd2017-06-20 13:54:17 -0700112static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt);
Nikolay Aleksandrov0e615e92015-11-20 13:54:19 +0100113static void mroute_clean_tables(struct mr_table *mrt, bool all);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000114static void ipmr_expire_process(unsigned long arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000116#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
117#define ipmr_for_each_table(mrt, net) \
118 list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list)
119
120static struct mr_table *ipmr_get_table(struct net *net, u32 id)
121{
122 struct mr_table *mrt;
123
124 ipmr_for_each_table(mrt, net) {
125 if (mrt->id == id)
126 return mrt;
127 }
128 return NULL;
129}
130
David S. Millerda919812011-03-12 02:04:50 -0500131static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000132 struct mr_table **mrt)
133{
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000134 int err;
Hannes Frederic Sowa95f4a452014-01-13 02:45:22 +0100135 struct ipmr_result res;
136 struct fib_lookup_arg arg = {
137 .result = &res,
138 .flags = FIB_LOOKUP_NOREF,
139 };
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000140
David Aherne58e4152016-10-31 15:54:00 -0700141 /* update flow if oif or iif point to device enslaved to l3mdev */
142 l3mdev_update_flow(net, flowi4_to_flowi(flp4));
143
David S. Millerda919812011-03-12 02:04:50 -0500144 err = fib_rules_lookup(net->ipv4.mr_rules_ops,
145 flowi4_to_flowi(flp4), 0, &arg);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000146 if (err < 0)
147 return err;
148 *mrt = res.mrt;
149 return 0;
150}
151
152static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp,
153 int flags, struct fib_lookup_arg *arg)
154{
155 struct ipmr_result *res = arg->result;
156 struct mr_table *mrt;
157
158 switch (rule->action) {
159 case FR_ACT_TO_TBL:
160 break;
161 case FR_ACT_UNREACHABLE:
162 return -ENETUNREACH;
163 case FR_ACT_PROHIBIT:
164 return -EACCES;
165 case FR_ACT_BLACKHOLE:
166 default:
167 return -EINVAL;
168 }
169
David Aherne58e4152016-10-31 15:54:00 -0700170 arg->table = fib_rule_get_table(rule, arg);
171
172 mrt = ipmr_get_table(rule->fr_net, arg->table);
Ian Morris51456b22015-04-03 09:17:26 +0100173 if (!mrt)
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000174 return -EAGAIN;
175 res->mrt = mrt;
176 return 0;
177}
178
179static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
180{
181 return 1;
182}
183
184static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = {
185 FRA_GENERIC_POLICY,
186};
187
188static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
189 struct fib_rule_hdr *frh, struct nlattr **tb)
190{
191 return 0;
192}
193
194static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
195 struct nlattr **tb)
196{
197 return 1;
198}
199
200static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
201 struct fib_rule_hdr *frh)
202{
203 frh->dst_len = 0;
204 frh->src_len = 0;
205 frh->tos = 0;
206 return 0;
207}
208
Andi Kleen04a6f822012-10-04 17:12:11 -0700209static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = {
Patrick McHardy25239ce2010-04-26 16:02:05 +0200210 .family = RTNL_FAMILY_IPMR,
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000211 .rule_size = sizeof(struct ipmr_rule),
212 .addr_size = sizeof(u32),
213 .action = ipmr_rule_action,
214 .match = ipmr_rule_match,
215 .configure = ipmr_rule_configure,
216 .compare = ipmr_rule_compare,
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000217 .fill = ipmr_rule_fill,
218 .nlgroup = RTNLGRP_IPV4_RULE,
219 .policy = ipmr_rule_policy,
220 .owner = THIS_MODULE,
221};
222
223static int __net_init ipmr_rules_init(struct net *net)
224{
225 struct fib_rules_ops *ops;
226 struct mr_table *mrt;
227 int err;
228
229 ops = fib_rules_register(&ipmr_rules_ops_template, net);
230 if (IS_ERR(ops))
231 return PTR_ERR(ops);
232
233 INIT_LIST_HEAD(&net->ipv4.mr_tables);
234
235 mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
Nikolay Aleksandrov1113ebb2015-11-21 15:57:24 +0100236 if (IS_ERR(mrt)) {
237 err = PTR_ERR(mrt);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000238 goto err1;
239 }
240
241 err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0);
242 if (err < 0)
243 goto err2;
244
245 net->ipv4.mr_rules_ops = ops;
246 return 0;
247
248err2:
WANG Congf243e5a2015-03-25 14:45:03 -0700249 ipmr_free_table(mrt);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000250err1:
251 fib_rules_unregister(ops);
252 return err;
253}
254
255static void __net_exit ipmr_rules_exit(struct net *net)
256{
257 struct mr_table *mrt, *next;
258
WANG Conged785302015-03-31 11:01:45 -0700259 rtnl_lock();
Eric Dumazet035320d2010-06-06 23:48:40 +0000260 list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
261 list_del(&mrt->list);
Francesco Ruggeriacbb2192012-08-24 07:38:35 +0000262 ipmr_free_table(mrt);
Eric Dumazet035320d2010-06-06 23:48:40 +0000263 }
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000264 fib_rules_unregister(net->ipv4.mr_rules_ops);
WANG Cong419df122015-03-31 11:01:46 -0700265 rtnl_unlock();
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000266}
Yotam Gigi4d65b942017-09-27 08:23:13 +0200267
268static int ipmr_rules_dump(struct net *net, struct notifier_block *nb)
269{
270 return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR);
271}
272
273static unsigned int ipmr_rules_seq_read(struct net *net)
274{
275 return fib_rules_seq_read(net, RTNL_FAMILY_IPMR);
276}
Yotam Gigi478e4c22017-09-27 08:23:16 +0200277
278bool ipmr_rule_default(const struct fib_rule *rule)
279{
280 return fib_rule_matchall(rule) && rule->table == RT_TABLE_DEFAULT;
281}
282EXPORT_SYMBOL(ipmr_rule_default);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000283#else
284#define ipmr_for_each_table(mrt, net) \
285 for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
286
287static struct mr_table *ipmr_get_table(struct net *net, u32 id)
288{
289 return net->ipv4.mrt;
290}
291
David S. Millerda919812011-03-12 02:04:50 -0500292static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000293 struct mr_table **mrt)
294{
295 *mrt = net->ipv4.mrt;
296 return 0;
297}
298
299static int __net_init ipmr_rules_init(struct net *net)
300{
Nikolay Aleksandrov1113ebb2015-11-21 15:57:24 +0100301 struct mr_table *mrt;
302
303 mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
304 if (IS_ERR(mrt))
305 return PTR_ERR(mrt);
306 net->ipv4.mrt = mrt;
307 return 0;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000308}
309
310static void __net_exit ipmr_rules_exit(struct net *net)
311{
WANG Conged785302015-03-31 11:01:45 -0700312 rtnl_lock();
Francesco Ruggeriacbb2192012-08-24 07:38:35 +0000313 ipmr_free_table(net->ipv4.mrt);
WANG Conged785302015-03-31 11:01:45 -0700314 net->ipv4.mrt = NULL;
315 rtnl_unlock();
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000316}
Yotam Gigi4d65b942017-09-27 08:23:13 +0200317
318static int ipmr_rules_dump(struct net *net, struct notifier_block *nb)
319{
320 return 0;
321}
322
323static unsigned int ipmr_rules_seq_read(struct net *net)
324{
325 return 0;
326}
Yotam Gigi478e4c22017-09-27 08:23:16 +0200327
328bool ipmr_rule_default(const struct fib_rule *rule)
329{
330 return true;
331}
332EXPORT_SYMBOL(ipmr_rule_default);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000333#endif
334
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +0100335static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg,
336 const void *ptr)
337{
338 const struct mfc_cache_cmp_arg *cmparg = arg->key;
339 struct mfc_cache *c = (struct mfc_cache *)ptr;
340
341 return cmparg->mfc_mcastgrp != c->mfc_mcastgrp ||
342 cmparg->mfc_origin != c->mfc_origin;
343}
344
345static const struct rhashtable_params ipmr_rht_params = {
346 .head_offset = offsetof(struct mfc_cache, mnode),
347 .key_offset = offsetof(struct mfc_cache, cmparg),
348 .key_len = sizeof(struct mfc_cache_cmp_arg),
349 .nelem_hint = 3,
350 .locks_mul = 1,
351 .obj_cmpfn = ipmr_hash_cmp,
352 .automatic_shrinking = true,
353};
354
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000355static struct mr_table *ipmr_new_table(struct net *net, u32 id)
356{
357 struct mr_table *mrt;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000358
Nikolay Aleksandrov1113ebb2015-11-21 15:57:24 +0100359 /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */
360 if (id != RT_TABLE_DEFAULT && id >= 1000000000)
361 return ERR_PTR(-EINVAL);
362
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000363 mrt = ipmr_get_table(net, id);
Ian Morris00db4122015-04-03 09:17:27 +0100364 if (mrt)
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000365 return mrt;
366
367 mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
Ian Morris51456b22015-04-03 09:17:26 +0100368 if (!mrt)
Nikolay Aleksandrov1113ebb2015-11-21 15:57:24 +0100369 return ERR_PTR(-ENOMEM);
Patrick McHardy8de53df2010-04-15 13:29:28 +0200370 write_pnet(&mrt->net, net);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000371 mrt->id = id;
372
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +0100373 rhltable_init(&mrt->mfc_hash, &ipmr_rht_params);
374 INIT_LIST_HEAD(&mrt->mfc_cache_list);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000375 INIT_LIST_HEAD(&mrt->mfc_unres_queue);
376
377 setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process,
378 (unsigned long)mrt);
379
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000380 mrt->mroute_reg_vif_num = -1;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000381#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
382 list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables);
383#endif
384 return mrt;
385}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Francesco Ruggeriacbb2192012-08-24 07:38:35 +0000387static void ipmr_free_table(struct mr_table *mrt)
388{
389 del_timer_sync(&mrt->ipmr_expire_timer);
Nikolay Aleksandrov0e615e92015-11-20 13:54:19 +0100390 mroute_clean_tables(mrt, true);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +0100391 rhltable_destroy(&mrt->mfc_hash);
Francesco Ruggeriacbb2192012-08-24 07:38:35 +0000392 kfree(mrt);
393}
394
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
396
Wang Chend6070322008-07-14 20:55:26 -0700397static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
398{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000399 struct net *net = dev_net(dev);
400
Wang Chend6070322008-07-14 20:55:26 -0700401 dev_close(dev);
402
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000403 dev = __dev_get_by_name(net, "tunl0");
Wang Chend6070322008-07-14 20:55:26 -0700404 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800405 const struct net_device_ops *ops = dev->netdev_ops;
Wang Chend6070322008-07-14 20:55:26 -0700406 struct ifreq ifr;
Wang Chend6070322008-07-14 20:55:26 -0700407 struct ip_tunnel_parm p;
408
409 memset(&p, 0, sizeof(p));
410 p.iph.daddr = v->vifc_rmt_addr.s_addr;
411 p.iph.saddr = v->vifc_lcl_addr.s_addr;
412 p.iph.version = 4;
413 p.iph.ihl = 5;
414 p.iph.protocol = IPPROTO_IPIP;
415 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
416 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
417
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800418 if (ops->ndo_do_ioctl) {
419 mm_segment_t oldfs = get_fs();
420
421 set_fs(KERNEL_DS);
422 ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL);
423 set_fs(oldfs);
424 }
Wang Chend6070322008-07-14 20:55:26 -0700425 }
426}
427
Nikolay Aleksandrova0b47732015-11-21 15:57:32 +0100428/* Initialize ipmr pimreg/tunnel in_device */
429static bool ipmr_init_vif_indev(const struct net_device *dev)
430{
431 struct in_device *in_dev;
432
433 ASSERT_RTNL();
434
435 in_dev = __in_dev_get_rtnl(dev);
436 if (!in_dev)
437 return false;
438 ipv4_devconf_setall(in_dev);
439 neigh_parms_data_state_setall(in_dev->arp_parms);
440 IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
441
442 return true;
443}
444
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +0100445static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446{
447 struct net_device *dev;
448
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000449 dev = __dev_get_by_name(net, "tunl0");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
451 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800452 const struct net_device_ops *ops = dev->netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 int err;
454 struct ifreq ifr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 struct ip_tunnel_parm p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
457 memset(&p, 0, sizeof(p));
458 p.iph.daddr = v->vifc_rmt_addr.s_addr;
459 p.iph.saddr = v->vifc_lcl_addr.s_addr;
460 p.iph.version = 4;
461 p.iph.ihl = 5;
462 p.iph.protocol = IPPROTO_IPIP;
463 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
Stephen Hemmingerba93ef72008-01-21 17:28:59 -0800464 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800466 if (ops->ndo_do_ioctl) {
467 mm_segment_t oldfs = get_fs();
468
469 set_fs(KERNEL_DS);
470 err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
471 set_fs(oldfs);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000472 } else {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800473 err = -EOPNOTSUPP;
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000474 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 dev = NULL;
476
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000477 if (err == 0 &&
478 (dev = __dev_get_by_name(net, p.name)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 dev->flags |= IFF_MULTICAST;
Nikolay Aleksandrova0b47732015-11-21 15:57:32 +0100480 if (!ipmr_init_vif_indev(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 goto failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 if (dev_open(dev))
483 goto failure;
Wang Chen7dc00c82008-07-14 20:56:34 -0700484 dev_hold(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 }
486 }
487 return dev;
488
489failure:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 unregister_netdevice(dev);
491 return NULL;
492}
493
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +0100494#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
Stephen Hemminger6fef4c02009-08-31 19:50:41 +0000495static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000497 struct net *net = dev_net(dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000498 struct mr_table *mrt;
David S. Millerda919812011-03-12 02:04:50 -0500499 struct flowi4 fl4 = {
500 .flowi4_oif = dev->ifindex,
Cong Wang6a662712014-04-15 16:25:34 -0700501 .flowi4_iif = skb->skb_iif ? : LOOPBACK_IFINDEX,
David S. Millerda919812011-03-12 02:04:50 -0500502 .flowi4_mark = skb->mark,
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000503 };
504 int err;
505
David S. Millerda919812011-03-12 02:04:50 -0500506 err = ipmr_fib_lookup(net, &fl4, &mrt);
Ben Greeare40dbc52010-07-15 13:22:33 +0000507 if (err < 0) {
508 kfree_skb(skb);
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000509 return err;
Ben Greeare40dbc52010-07-15 13:22:33 +0000510 }
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 read_lock(&mrt_lock);
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -0700513 dev->stats.tx_bytes += skb->len;
514 dev->stats.tx_packets++;
Patrick McHardy0c122952010-04-13 05:03:22 +0000515 ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 read_unlock(&mrt_lock);
517 kfree_skb(skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +0000518 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519}
520
Nicolas Dichtelee9b9592015-04-02 17:07:03 +0200521static int reg_vif_get_iflink(const struct net_device *dev)
522{
523 return 0;
524}
525
Stephen Hemminger007c3832008-11-20 20:28:35 -0800526static const struct net_device_ops reg_vif_netdev_ops = {
527 .ndo_start_xmit = reg_vif_xmit,
Nicolas Dichtelee9b9592015-04-02 17:07:03 +0200528 .ndo_get_iflink = reg_vif_get_iflink,
Stephen Hemminger007c3832008-11-20 20:28:35 -0800529};
530
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531static void reg_vif_setup(struct net_device *dev)
532{
533 dev->type = ARPHRD_PIMREG;
Kris Katterjohn46f25df2006-01-05 16:35:42 -0800534 dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 dev->flags = IFF_NOARP;
Himangi Saraogi70cb4a42014-05-30 21:10:48 +0530536 dev->netdev_ops = &reg_vif_netdev_ops;
David S. Millercf124db2017-05-08 12:52:56 -0400537 dev->needs_free_netdev = true;
Tom Goff403dbb92009-06-14 03:16:13 -0700538 dev->features |= NETIF_F_NETNS_LOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000541static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542{
543 struct net_device *dev;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000544 char name[IFNAMSIZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000546 if (mrt->id == RT_TABLE_DEFAULT)
547 sprintf(name, "pimreg");
548 else
549 sprintf(name, "pimreg%u", mrt->id);
550
Tom Gundersenc835a672014-07-14 16:37:24 +0200551 dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Ian Morris51456b22015-04-03 09:17:26 +0100553 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 return NULL;
555
Tom Goff403dbb92009-06-14 03:16:13 -0700556 dev_net_set(dev, net);
557
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 if (register_netdevice(dev)) {
559 free_netdev(dev);
560 return NULL;
561 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
Nikolay Aleksandrova0b47732015-11-21 15:57:32 +0100563 if (!ipmr_init_vif_indev(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 goto failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 if (dev_open(dev))
566 goto failure;
567
Wang Chen7dc00c82008-07-14 20:56:34 -0700568 dev_hold(dev);
569
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 return dev;
571
572failure:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 unregister_netdevice(dev);
574 return NULL;
575}
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +0100576
577/* called with rcu_read_lock() */
578static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
579 unsigned int pimlen)
580{
581 struct net_device *reg_dev = NULL;
582 struct iphdr *encap;
583
584 encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +0100585 /* Check that:
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +0100586 * a. packet is really sent to a multicast group
587 * b. packet is not a NULL-REGISTER
588 * c. packet is not truncated
589 */
590 if (!ipv4_is_multicast(encap->daddr) ||
591 encap->tot_len == 0 ||
592 ntohs(encap->tot_len) + pimlen > skb->len)
593 return 1;
594
595 read_lock(&mrt_lock);
596 if (mrt->mroute_reg_vif_num >= 0)
597 reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
598 read_unlock(&mrt_lock);
599
600 if (!reg_dev)
601 return 1;
602
603 skb->mac_header = skb->network_header;
604 skb_pull(skb, (u8 *)encap - skb->data);
605 skb_reset_network_header(skb);
606 skb->protocol = htons(ETH_P_IP);
607 skb->ip_summed = CHECKSUM_NONE;
608
609 skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
610
611 netif_rx(skb);
612
613 return NET_RX_SUCCESS;
614}
615#else
616static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
617{
618 return NULL;
619}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620#endif
621
Yotam Gigi4d65b942017-09-27 08:23:13 +0200622static int call_ipmr_vif_entry_notifier(struct notifier_block *nb,
623 struct net *net,
624 enum fib_event_type event_type,
625 struct vif_device *vif,
626 vifi_t vif_index, u32 tb_id)
627{
628 struct vif_entry_notifier_info info = {
629 .info = {
630 .family = RTNL_FAMILY_IPMR,
631 .net = net,
632 },
633 .dev = vif->dev,
634 .vif_index = vif_index,
635 .vif_flags = vif->flags,
636 .tb_id = tb_id,
637 };
638
639 return call_fib_notifier(nb, net, event_type, &info.info);
640}
641
Yotam Gigib3620532017-09-27 08:23:14 +0200642static int call_ipmr_vif_entry_notifiers(struct net *net,
643 enum fib_event_type event_type,
644 struct vif_device *vif,
645 vifi_t vif_index, u32 tb_id)
646{
647 struct vif_entry_notifier_info info = {
648 .info = {
649 .family = RTNL_FAMILY_IPMR,
650 .net = net,
651 },
652 .dev = vif->dev,
653 .vif_index = vif_index,
654 .vif_flags = vif->flags,
655 .tb_id = tb_id,
656 };
657
658 ASSERT_RTNL();
659 net->ipv4.ipmr_seq++;
660 return call_fib_notifiers(net, event_type, &info.info);
661}
662
Yotam Gigi4d65b942017-09-27 08:23:13 +0200663static int call_ipmr_mfc_entry_notifier(struct notifier_block *nb,
664 struct net *net,
665 enum fib_event_type event_type,
666 struct mfc_cache *mfc, u32 tb_id)
667{
668 struct mfc_entry_notifier_info info = {
669 .info = {
670 .family = RTNL_FAMILY_IPMR,
671 .net = net,
672 },
673 .mfc = mfc,
674 .tb_id = tb_id
675 };
676
677 return call_fib_notifier(nb, net, event_type, &info.info);
678}
679
Yotam Gigib3620532017-09-27 08:23:14 +0200680static int call_ipmr_mfc_entry_notifiers(struct net *net,
681 enum fib_event_type event_type,
682 struct mfc_cache *mfc, u32 tb_id)
683{
684 struct mfc_entry_notifier_info info = {
685 .info = {
686 .family = RTNL_FAMILY_IPMR,
687 .net = net,
688 },
689 .mfc = mfc,
690 .tb_id = tb_id
691 };
692
693 ASSERT_RTNL();
694 net->ipv4.ipmr_seq++;
695 return call_fib_notifiers(net, event_type, &info.info);
696}
697
Ben Hutchings2c530402012-07-10 10:55:09 +0000698/**
699 * vif_delete - Delete a VIF entry
Wang Chen7dc00c82008-07-14 20:56:34 -0700700 * @notify: Set to 1, if the caller is a notifier_call
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000702static int vif_delete(struct mr_table *mrt, int vifi, int notify,
Eric Dumazetd17fa6f2009-10-28 05:21:38 +0000703 struct list_head *head)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704{
Yotam Gigib3620532017-09-27 08:23:14 +0200705 struct net *net = read_pnet(&mrt->net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 struct vif_device *v;
707 struct net_device *dev;
708 struct in_device *in_dev;
709
Patrick McHardy0c122952010-04-13 05:03:22 +0000710 if (vifi < 0 || vifi >= mrt->maxvif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 return -EADDRNOTAVAIL;
712
Patrick McHardy0c122952010-04-13 05:03:22 +0000713 v = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Yotam Gigib3620532017-09-27 08:23:14 +0200715 if (VIF_EXISTS(mrt, vifi))
716 call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_DEL, v, vifi,
717 mrt->id);
718
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 write_lock_bh(&mrt_lock);
720 dev = v->dev;
721 v->dev = NULL;
722
723 if (!dev) {
724 write_unlock_bh(&mrt_lock);
725 return -EADDRNOTAVAIL;
726 }
727
Patrick McHardy0c122952010-04-13 05:03:22 +0000728 if (vifi == mrt->mroute_reg_vif_num)
729 mrt->mroute_reg_vif_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000731 if (vifi + 1 == mrt->maxvif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 int tmp;
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000733
734 for (tmp = vifi - 1; tmp >= 0; tmp--) {
Patrick McHardy0c122952010-04-13 05:03:22 +0000735 if (VIF_EXISTS(mrt, tmp))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 break;
737 }
Patrick McHardy0c122952010-04-13 05:03:22 +0000738 mrt->maxvif = tmp+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 }
740
741 write_unlock_bh(&mrt_lock);
742
743 dev_set_allmulti(dev, -1);
744
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000745 in_dev = __in_dev_get_rtnl(dev);
746 if (in_dev) {
Herbert Xu42f811b2007-06-04 23:34:44 -0700747 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
David Ahern3b022862017-03-28 14:28:02 -0700748 inet_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
Nicolas Dichteld67b8c62012-12-04 01:13:35 +0000749 NETCONFA_MC_FORWARDING,
750 dev->ifindex, &in_dev->cnf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 ip_rt_multicast_event(in_dev);
752 }
753
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000754 if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify)
Eric Dumazetd17fa6f2009-10-28 05:21:38 +0000755 unregister_netdevice_queue(dev, head);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757 dev_put(dev);
758 return 0;
759}
760
Eric Dumazeta8c94862010-10-01 16:15:08 +0000761static void ipmr_cache_free_rcu(struct rcu_head *head)
762{
763 struct mfc_cache *c = container_of(head, struct mfc_cache, rcu);
764
765 kmem_cache_free(mrt_cachep, c);
766}
767
Yotam Gigi310ebbb2017-09-27 08:23:12 +0200768void ipmr_cache_free(struct mfc_cache *c)
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000769{
Eric Dumazeta8c94862010-10-01 16:15:08 +0000770 call_rcu(&c->rcu, ipmr_cache_free_rcu);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000771}
Yotam Gigi310ebbb2017-09-27 08:23:12 +0200772EXPORT_SYMBOL(ipmr_cache_free);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774/* Destroy an unresolved cache entry, killing queued skbs
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000775 * and reporting error to netlink readers.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000777static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
Patrick McHardy8de53df2010-04-15 13:29:28 +0200779 struct net *net = read_pnet(&mrt->net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700781 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Patrick McHardy0c122952010-04-13 05:03:22 +0000783 atomic_dec(&mrt->cache_resolve_queue_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Jianjun Kongc354e122008-11-03 00:28:02 -0800785 while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700786 if (ip_hdr(skb)->version == 0) {
Johannes Bergaf728682017-06-16 14:29:22 +0200787 struct nlmsghdr *nlh = skb_pull(skb,
788 sizeof(struct iphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 nlh->nlmsg_type = NLMSG_ERROR;
Hong zhi guo573ce262013-03-27 06:47:04 +0000790 nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 skb_trim(skb, nlh->nlmsg_len);
Hong zhi guo573ce262013-03-27 06:47:04 +0000792 e = nlmsg_data(nlh);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700793 e->error = -ETIMEDOUT;
794 memset(&e->msg, 0, sizeof(e->msg));
Thomas Graf2942e902006-08-15 00:30:25 -0700795
Eric W. Biederman15e47302012-09-07 20:12:54 +0000796 rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000797 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 kfree_skb(skb);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000799 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 }
801
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000802 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803}
804
Patrick McHardye258beb2010-04-13 05:03:19 +0000805/* Timer process for the unresolved queue. */
Patrick McHardye258beb2010-04-13 05:03:19 +0000806static void ipmr_expire_process(unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807{
Patrick McHardy0c122952010-04-13 05:03:22 +0000808 struct mr_table *mrt = (struct mr_table *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 unsigned long now;
810 unsigned long expires;
Patrick McHardy862465f2010-04-13 05:03:21 +0000811 struct mfc_cache *c, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
813 if (!spin_trylock(&mfc_unres_lock)) {
Patrick McHardy0c122952010-04-13 05:03:22 +0000814 mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 return;
816 }
817
Patrick McHardy0c122952010-04-13 05:03:22 +0000818 if (list_empty(&mrt->mfc_unres_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 goto out;
820
821 now = jiffies;
822 expires = 10*HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Patrick McHardy0c122952010-04-13 05:03:22 +0000824 list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 if (time_after(c->mfc_un.unres.expires, now)) {
826 unsigned long interval = c->mfc_un.unres.expires - now;
827 if (interval < expires)
828 expires = interval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 continue;
830 }
831
Patrick McHardy862465f2010-04-13 05:03:21 +0000832 list_del(&c->list);
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +0000833 mroute_netlink_event(mrt, c, RTM_DELROUTE);
Patrick McHardy0c122952010-04-13 05:03:22 +0000834 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 }
836
Patrick McHardy0c122952010-04-13 05:03:22 +0000837 if (!list_empty(&mrt->mfc_unres_queue))
838 mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840out:
841 spin_unlock(&mfc_unres_lock);
842}
843
844/* Fill oifs list. It is called under write locked mrt_lock. */
Patrick McHardy0c122952010-04-13 05:03:22 +0000845static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
Patrick McHardyd658f8a2010-04-13 05:03:20 +0000846 unsigned char *ttls)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847{
848 int vifi;
849
850 cache->mfc_un.res.minvif = MAXVIFS;
851 cache->mfc_un.res.maxvif = 0;
852 memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
853
Patrick McHardy0c122952010-04-13 05:03:22 +0000854 for (vifi = 0; vifi < mrt->maxvif; vifi++) {
855 if (VIF_EXISTS(mrt, vifi) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +0000856 ttls[vifi] && ttls[vifi] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 cache->mfc_un.res.ttls[vifi] = ttls[vifi];
858 if (cache->mfc_un.res.minvif > vifi)
859 cache->mfc_un.res.minvif = vifi;
860 if (cache->mfc_un.res.maxvif <= vifi)
861 cache->mfc_un.res.maxvif = vifi + 1;
862 }
863 }
Nikolay Aleksandrov90b5ca12016-07-26 18:54:52 +0200864 cache->mfc_un.res.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865}
866
Patrick McHardy0c122952010-04-13 05:03:22 +0000867static int vif_add(struct net *net, struct mr_table *mrt,
868 struct vifctl *vifc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869{
870 int vifi = vifc->vifc_vifi;
Patrick McHardy0c122952010-04-13 05:03:22 +0000871 struct vif_device *v = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 struct net_device *dev;
873 struct in_device *in_dev;
Wang Chend6070322008-07-14 20:55:26 -0700874 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876 /* Is vif busy ? */
Patrick McHardy0c122952010-04-13 05:03:22 +0000877 if (VIF_EXISTS(mrt, vifi))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 return -EADDRINUSE;
879
880 switch (vifc->vifc_flags) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 case VIFF_REGISTER:
Nikolay Aleksandrov1973a4e2015-11-26 15:23:48 +0100882 if (!ipmr_pimsm_enabled())
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +0100883 return -EINVAL;
884 /* Special Purpose VIF in PIM
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 * All the packets will be sent to the daemon
886 */
Patrick McHardy0c122952010-04-13 05:03:22 +0000887 if (mrt->mroute_reg_vif_num >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 return -EADDRINUSE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +0000889 dev = ipmr_reg_vif(net, mrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 if (!dev)
891 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700892 err = dev_set_allmulti(dev, 1);
893 if (err) {
894 unregister_netdevice(dev);
Wang Chen7dc00c82008-07-14 20:56:34 -0700895 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700896 return err;
897 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 break;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900899 case VIFF_TUNNEL:
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000900 dev = ipmr_new_tunnel(net, vifc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 if (!dev)
902 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700903 err = dev_set_allmulti(dev, 1);
904 if (err) {
905 ipmr_del_tunnel(dev, vifc);
Wang Chen7dc00c82008-07-14 20:56:34 -0700906 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700907 return err;
908 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 break;
Ilia Kee5e81f2009-09-16 05:53:07 +0000910 case VIFF_USE_IFINDEX:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 case 0:
Ilia Kee5e81f2009-09-16 05:53:07 +0000912 if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
913 dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex);
Ian Morris51456b22015-04-03 09:17:26 +0100914 if (dev && !__in_dev_get_rtnl(dev)) {
Ilia Kee5e81f2009-09-16 05:53:07 +0000915 dev_put(dev);
916 return -EADDRNOTAVAIL;
917 }
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000918 } else {
Ilia Kee5e81f2009-09-16 05:53:07 +0000919 dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000920 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 if (!dev)
922 return -EADDRNOTAVAIL;
Wang Chend6070322008-07-14 20:55:26 -0700923 err = dev_set_allmulti(dev, 1);
Wang Chen7dc00c82008-07-14 20:56:34 -0700924 if (err) {
925 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700926 return err;
Wang Chen7dc00c82008-07-14 20:56:34 -0700927 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 break;
929 default:
930 return -EINVAL;
931 }
932
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000933 in_dev = __in_dev_get_rtnl(dev);
934 if (!in_dev) {
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000935 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 return -EADDRNOTAVAIL;
Dan Carpenterd0490cf2009-11-11 02:03:54 +0000937 }
Herbert Xu42f811b2007-06-04 23:34:44 -0700938 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
David Ahern3b022862017-03-28 14:28:02 -0700939 inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING,
940 dev->ifindex, &in_dev->cnf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 ip_rt_multicast_event(in_dev);
942
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000943 /* Fill in the VIF structures */
944
Jianjun Kongc354e122008-11-03 00:28:02 -0800945 v->rate_limit = vifc->vifc_rate_limit;
946 v->local = vifc->vifc_lcl_addr.s_addr;
947 v->remote = vifc->vifc_rmt_addr.s_addr;
948 v->flags = vifc->vifc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 if (!mrtsock)
950 v->flags |= VIFF_STATIC;
Jianjun Kongc354e122008-11-03 00:28:02 -0800951 v->threshold = vifc->vifc_threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 v->bytes_in = 0;
953 v->bytes_out = 0;
954 v->pkt_in = 0;
955 v->pkt_out = 0;
956 v->link = dev->ifindex;
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000957 if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER))
Nicolas Dichtela54acb32015-04-02 17:07:00 +0200958 v->link = dev_get_iflink(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
960 /* And finish update writing critical data */
961 write_lock_bh(&mrt_lock);
Jianjun Kongc354e122008-11-03 00:28:02 -0800962 v->dev = dev;
Eric Dumazeta8cb16d2010-10-01 16:15:29 +0000963 if (v->flags & VIFF_REGISTER)
Patrick McHardy0c122952010-04-13 05:03:22 +0000964 mrt->mroute_reg_vif_num = vifi;
Patrick McHardy0c122952010-04-13 05:03:22 +0000965 if (vifi+1 > mrt->maxvif)
966 mrt->maxvif = vifi+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 write_unlock_bh(&mrt_lock);
Yotam Gigib3620532017-09-27 08:23:14 +0200968 call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD, v, vifi, mrt->id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 return 0;
970}
971
Eric Dumazeta8c94862010-10-01 16:15:08 +0000972/* called with rcu_read_lock() */
Patrick McHardy0c122952010-04-13 05:03:22 +0000973static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000974 __be32 origin,
975 __be32 mcastgrp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976{
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +0100977 struct mfc_cache_cmp_arg arg = {
978 .mfc_mcastgrp = mcastgrp,
979 .mfc_origin = origin
980 };
981 struct rhlist_head *tmp, *list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 struct mfc_cache *c;
983
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +0100984 list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
985 rhl_for_each_entry_rcu(c, tmp, list, mnode)
986 return c;
987
Patrick McHardy862465f2010-04-13 05:03:21 +0000988 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989}
990
Nicolas Dichtel660b26d2013-01-21 06:00:26 +0000991/* Look for a (*,*,oif) entry */
992static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt,
993 int vifi)
994{
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +0100995 struct mfc_cache_cmp_arg arg = {
996 .mfc_mcastgrp = htonl(INADDR_ANY),
997 .mfc_origin = htonl(INADDR_ANY)
998 };
999 struct rhlist_head *tmp, *list;
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001000 struct mfc_cache *c;
1001
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001002 list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
1003 rhl_for_each_entry_rcu(c, tmp, list, mnode)
1004 if (c->mfc_un.res.ttls[vifi] < 255)
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001005 return c;
1006
1007 return NULL;
1008}
1009
1010/* Look for a (*,G) entry */
1011static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt,
1012 __be32 mcastgrp, int vifi)
1013{
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001014 struct mfc_cache_cmp_arg arg = {
1015 .mfc_mcastgrp = mcastgrp,
1016 .mfc_origin = htonl(INADDR_ANY)
1017 };
1018 struct rhlist_head *tmp, *list;
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001019 struct mfc_cache *c, *proxy;
1020
Nicolas Dichtel360eb5d2013-01-22 11:18:03 +01001021 if (mcastgrp == htonl(INADDR_ANY))
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001022 goto skip;
1023
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001024 list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
1025 rhl_for_each_entry_rcu(c, tmp, list, mnode) {
1026 if (c->mfc_un.res.ttls[vifi] < 255)
1027 return c;
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001028
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001029 /* It's ok if the vifi is part of the static tree */
1030 proxy = ipmr_cache_find_any_parent(mrt, c->mfc_parent);
1031 if (proxy && proxy->mfc_un.res.ttls[vifi] < 255)
1032 return c;
1033 }
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001034
1035skip:
1036 return ipmr_cache_find_any_parent(mrt, vifi);
1037}
1038
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001039/* Look for a (S,G,iif) entry if parent != -1 */
1040static struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt,
1041 __be32 origin, __be32 mcastgrp,
1042 int parent)
1043{
1044 struct mfc_cache_cmp_arg arg = {
1045 .mfc_mcastgrp = mcastgrp,
1046 .mfc_origin = origin,
1047 };
1048 struct rhlist_head *tmp, *list;
1049 struct mfc_cache *c;
1050
1051 list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
1052 rhl_for_each_entry_rcu(c, tmp, list, mnode)
1053 if (parent == -1 || parent == c->mfc_parent)
1054 return c;
1055
1056 return NULL;
1057}
1058
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001059/* Allocate a multicast cache entry */
Patrick McHardyd658f8a2010-04-13 05:03:20 +00001060static struct mfc_cache *ipmr_cache_alloc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061{
Jianjun Kongc354e122008-11-03 00:28:02 -08001062 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
Eric Dumazeta8c94862010-10-01 16:15:08 +00001063
Tom Goff70a0dec2016-06-23 16:11:57 -04001064 if (c) {
1065 c->mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
Eric Dumazeta8c94862010-10-01 16:15:08 +00001066 c->mfc_un.res.minvif = MAXVIFS;
Yotam Gigi310ebbb2017-09-27 08:23:12 +02001067 refcount_set(&c->mfc_un.res.refcount, 1);
Tom Goff70a0dec2016-06-23 16:11:57 -04001068 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return c;
1070}
1071
Patrick McHardyd658f8a2010-04-13 05:03:20 +00001072static struct mfc_cache *ipmr_cache_alloc_unres(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073{
Jianjun Kongc354e122008-11-03 00:28:02 -08001074 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
Eric Dumazeta8c94862010-10-01 16:15:08 +00001075
1076 if (c) {
1077 skb_queue_head_init(&c->mfc_un.unres.unresolved);
1078 c->mfc_un.unres.expires = jiffies + 10*HZ;
1079 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 return c;
1081}
1082
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001083/* A cache entry has gone into a resolved state from queued */
Patrick McHardy0c122952010-04-13 05:03:22 +00001084static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
1085 struct mfc_cache *uc, struct mfc_cache *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086{
1087 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -07001088 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001090 /* Play the pending entries through our router */
Jianjun Kongc354e122008-11-03 00:28:02 -08001091 while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001092 if (ip_hdr(skb)->version == 0) {
Johannes Bergaf728682017-06-16 14:29:22 +02001093 struct nlmsghdr *nlh = skb_pull(skb,
1094 sizeof(struct iphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
Hong zhi guo573ce262013-03-27 06:47:04 +00001096 if (__ipmr_fill_mroute(mrt, skb, c, nlmsg_data(nlh)) > 0) {
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001097 nlh->nlmsg_len = skb_tail_pointer(skb) -
1098 (u8 *)nlh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 } else {
1100 nlh->nlmsg_type = NLMSG_ERROR;
Hong zhi guo573ce262013-03-27 06:47:04 +00001101 nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 skb_trim(skb, nlh->nlmsg_len);
Hong zhi guo573ce262013-03-27 06:47:04 +00001103 e = nlmsg_data(nlh);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -07001104 e->error = -EMSGSIZE;
1105 memset(&e->msg, 0, sizeof(e->msg));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 }
Thomas Graf2942e902006-08-15 00:30:25 -07001107
Eric W. Biederman15e47302012-09-07 20:12:54 +00001108 rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001109 } else {
Donald Sharp4b1f0d32017-06-10 16:30:17 -04001110 ip_mr_forward(net, mrt, skb->dev, skb, c, 0);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001111 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 }
1113}
1114
Julien Gomes5a645dd2017-06-20 13:54:17 -07001115/* Bounce a cache query up to mrouted and netlink.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 *
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +01001117 * Called under mrt_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 */
Patrick McHardy0c122952010-04-13 05:03:22 +00001119static int ipmr_cache_report(struct mr_table *mrt,
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001120 struct sk_buff *pkt, vifi_t vifi, int assert)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121{
Arnaldo Carvalho de Meloc9bdd4b2007-03-12 20:09:15 -03001122 const int ihl = ip_hdrlen(pkt);
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +01001123 struct sock *mroute_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 struct igmphdr *igmp;
1125 struct igmpmsg *msg;
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +01001126 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 int ret;
1128
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 if (assert == IGMPMSG_WHOLEPKT)
1130 skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
1131 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 skb = alloc_skb(128, GFP_ATOMIC);
1133
Stephen Hemminger132adf52007-03-08 20:44:43 -08001134 if (!skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 return -ENOBUFS;
1136
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (assert == IGMPMSG_WHOLEPKT) {
1138 /* Ugly, but we have no choice with this interface.
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001139 * Duplicate old header, fix ihl, length etc.
1140 * And all this only to mangle msg->im_msgtype and
1141 * to set msg->im_mbz to "mbz" :-)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 */
Arnaldo Carvalho de Melo878c8142007-03-11 22:38:29 -03001143 skb_push(skb, sizeof(struct iphdr));
1144 skb_reset_network_header(skb);
Arnaldo Carvalho de Melobadff6d2007-03-13 13:06:52 -03001145 skb_reset_transport_header(skb);
Arnaldo Carvalho de Melo0272ffc2007-03-12 20:05:39 -03001146 msg = (struct igmpmsg *)skb_network_header(skb);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -07001147 memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 msg->im_msgtype = IGMPMSG_WHOLEPKT;
1149 msg->im_mbz = 0;
Patrick McHardy0c122952010-04-13 05:03:22 +00001150 msg->im_vif = mrt->mroute_reg_vif_num;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001151 ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
1152 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
1153 sizeof(struct iphdr));
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +01001154 } else {
1155 /* Copy the IP header */
1156 skb_set_network_header(skb, skb->len);
1157 skb_put(skb, ihl);
1158 skb_copy_to_linear_data(skb, pkt->data, ihl);
1159 /* Flag to the kernel this is a route add */
1160 ip_hdr(skb)->protocol = 0;
1161 msg = (struct igmpmsg *)skb_network_header(skb);
1162 msg->im_vif = vifi;
1163 skb_dst_set(skb, dst_clone(skb_dst(pkt)));
1164 /* Add our header */
Johannes Berg4df864c2017-06-16 14:29:21 +02001165 igmp = skb_put(skb, sizeof(struct igmphdr));
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +01001166 igmp->type = assert;
1167 msg->im_msgtype = assert;
1168 igmp->code = 0;
1169 ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
1170 skb->transport_header = skb->network_header;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001171 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
Eric Dumazet4c968702010-10-01 16:15:01 +00001173 rcu_read_lock();
1174 mroute_sk = rcu_dereference(mrt->mroute_sk);
Ian Morris51456b22015-04-03 09:17:26 +01001175 if (!mroute_sk) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001176 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 kfree_skb(skb);
1178 return -EINVAL;
1179 }
1180
Julien Gomes5a645dd2017-06-20 13:54:17 -07001181 igmpmsg_netlink_event(mrt, skb);
1182
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001183 /* Deliver to mrouted */
Eric Dumazet4c968702010-10-01 16:15:01 +00001184 ret = sock_queue_rcv_skb(mroute_sk, skb);
1185 rcu_read_unlock();
Benjamin Thery70a269e2009-01-22 04:56:15 +00001186 if (ret < 0) {
Joe Perchese87cc472012-05-13 21:56:26 +00001187 net_warn_ratelimited("mroute: pending queue full, dropping entries\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 kfree_skb(skb);
1189 }
1190
1191 return ret;
1192}
1193
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001194/* Queue a packet for resolution. It gets locked cache entry! */
1195static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
Donald Sharp4b1f0d32017-06-10 16:30:17 -04001196 struct sk_buff *skb, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197{
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001198 const struct iphdr *iph = ip_hdr(skb);
1199 struct mfc_cache *c;
Patrick McHardy862465f2010-04-13 05:03:21 +00001200 bool found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
1203 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001204 list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +00001205 if (c->mfc_mcastgrp == iph->daddr &&
Patrick McHardy862465f2010-04-13 05:03:21 +00001206 c->mfc_origin == iph->saddr) {
1207 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 break;
Patrick McHardy862465f2010-04-13 05:03:21 +00001209 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 }
1211
Patrick McHardy862465f2010-04-13 05:03:21 +00001212 if (!found) {
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001213 /* Create a new entry if allowable */
Patrick McHardy0c122952010-04-13 05:03:22 +00001214 if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
Patrick McHardyd658f8a2010-04-13 05:03:20 +00001215 (c = ipmr_cache_alloc_unres()) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 spin_unlock_bh(&mfc_unres_lock);
1217
1218 kfree_skb(skb);
1219 return -ENOBUFS;
1220 }
1221
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001222 /* Fill in the new cache entry */
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001223 c->mfc_parent = -1;
1224 c->mfc_origin = iph->saddr;
1225 c->mfc_mcastgrp = iph->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001227 /* Reflect first query at mrouted. */
Patrick McHardy0c122952010-04-13 05:03:22 +00001228 err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001229 if (err < 0) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001230 /* If the report failed throw the cache entry
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 out - Brad Parker
1232 */
1233 spin_unlock_bh(&mfc_unres_lock);
1234
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001235 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 kfree_skb(skb);
1237 return err;
1238 }
1239
Patrick McHardy0c122952010-04-13 05:03:22 +00001240 atomic_inc(&mrt->cache_resolve_queue_len);
1241 list_add(&c->list, &mrt->mfc_unres_queue);
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00001242 mroute_netlink_event(mrt, c, RTM_NEWROUTE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
David S. Miller278554b2010-05-12 00:05:35 -07001244 if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
1245 mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 }
1247
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001248 /* See if we can append the packet */
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001249 if (c->mfc_un.unres.unresolved.qlen > 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 kfree_skb(skb);
1251 err = -ENOBUFS;
1252 } else {
Donald Sharp4b1f0d32017-06-10 16:30:17 -04001253 if (dev) {
1254 skb->dev = dev;
1255 skb->skb_iif = dev->ifindex;
1256 }
Jianjun Kongc354e122008-11-03 00:28:02 -08001257 skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 err = 0;
1259 }
1260
1261 spin_unlock_bh(&mfc_unres_lock);
1262 return err;
1263}
1264
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001265/* MFC cache manipulation by user space mroute daemon */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001267static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268{
Yotam Gigib3620532017-09-27 08:23:14 +02001269 struct net *net = read_pnet(&mrt->net);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001270 struct mfc_cache *c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001272 /* The entries are added/deleted only under RTNL */
1273 rcu_read_lock();
1274 c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr,
1275 mfc->mfcc_mcastgrp.s_addr, parent);
1276 rcu_read_unlock();
1277 if (!c)
1278 return -ENOENT;
1279 rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params);
1280 list_del_rcu(&c->list);
Yotam Gigib3620532017-09-27 08:23:14 +02001281 call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001282 mroute_netlink_event(mrt, c, RTM_DELROUTE);
Yotam Gigi310ebbb2017-09-27 08:23:12 +02001283 ipmr_cache_put(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001285 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286}
1287
Patrick McHardy0c122952010-04-13 05:03:22 +00001288static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001289 struct mfcctl *mfc, int mrtsock, int parent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
Patrick McHardy862465f2010-04-13 05:03:21 +00001291 struct mfc_cache *uc, *c;
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001292 bool found;
1293 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Patrick McHardya50436f22010-03-17 06:04:14 +00001295 if (mfc->mfcc_parent >= MAXVIFS)
1296 return -ENFILE;
1297
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001298 /* The entries are added/deleted only under RTNL */
1299 rcu_read_lock();
1300 c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr,
1301 mfc->mfcc_mcastgrp.s_addr, parent);
1302 rcu_read_unlock();
1303 if (c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 write_lock_bh(&mrt_lock);
1305 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001306 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 if (!mrtsock)
1308 c->mfc_flags |= MFC_STATIC;
1309 write_unlock_bh(&mrt_lock);
Yotam Gigib3620532017-09-27 08:23:14 +02001310 call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c,
1311 mrt->id);
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00001312 mroute_netlink_event(mrt, c, RTM_NEWROUTE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 return 0;
1314 }
1315
Nicolas Dichtel360eb5d2013-01-22 11:18:03 +01001316 if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) &&
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001317 !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 return -EINVAL;
1319
Patrick McHardyd658f8a2010-04-13 05:03:20 +00001320 c = ipmr_cache_alloc();
Ian Morris51456b22015-04-03 09:17:26 +01001321 if (!c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 return -ENOMEM;
1323
Jianjun Kongc354e122008-11-03 00:28:02 -08001324 c->mfc_origin = mfc->mfcc_origin.s_addr;
1325 c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
1326 c->mfc_parent = mfc->mfcc_parent;
Patrick McHardy0c122952010-04-13 05:03:22 +00001327 ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 if (!mrtsock)
1329 c->mfc_flags |= MFC_STATIC;
1330
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001331 ret = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->mnode,
1332 ipmr_rht_params);
1333 if (ret) {
1334 pr_err("ipmr: rhtable insert error %d\n", ret);
1335 ipmr_cache_free(c);
1336 return ret;
1337 }
1338 list_add_tail_rcu(&c->list, &mrt->mfc_cache_list);
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001339 /* Check to see if we resolved a queued list. If so we
1340 * need to send on the frames and tidy up.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 */
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001342 found = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001344 list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
Patrick McHardye258beb2010-04-13 05:03:19 +00001345 if (uc->mfc_origin == c->mfc_origin &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 uc->mfc_mcastgrp == c->mfc_mcastgrp) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001347 list_del(&uc->list);
Patrick McHardy0c122952010-04-13 05:03:22 +00001348 atomic_dec(&mrt->cache_resolve_queue_len);
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001349 found = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 break;
1351 }
1352 }
Patrick McHardy0c122952010-04-13 05:03:22 +00001353 if (list_empty(&mrt->mfc_unres_queue))
1354 del_timer(&mrt->ipmr_expire_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 spin_unlock_bh(&mfc_unres_lock);
1356
Patrick McHardyb0ebb732010-04-15 13:29:28 +02001357 if (found) {
Patrick McHardy0c122952010-04-13 05:03:22 +00001358 ipmr_cache_resolve(net, mrt, uc, c);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +00001359 ipmr_cache_free(uc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 }
Yotam Gigib3620532017-09-27 08:23:14 +02001361 call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, c, mrt->id);
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00001362 mroute_netlink_event(mrt, c, RTM_NEWROUTE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return 0;
1364}
1365
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001366/* Close the multicast socket, and clear the vif tables etc */
Nikolay Aleksandrov0e615e92015-11-20 13:54:19 +01001367static void mroute_clean_tables(struct mr_table *mrt, bool all)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368{
Yotam Gigib3620532017-09-27 08:23:14 +02001369 struct net *net = read_pnet(&mrt->net);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001370 struct mfc_cache *c, *tmp;
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001371 LIST_HEAD(list);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001372 int i;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001373
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001374 /* Shut down all active vif entries */
Patrick McHardy0c122952010-04-13 05:03:22 +00001375 for (i = 0; i < mrt->maxvif; i++) {
Nikolay Aleksandrov0e615e92015-11-20 13:54:19 +01001376 if (!all && (mrt->vif_table[i].flags & VIFF_STATIC))
1377 continue;
1378 vif_delete(mrt, i, 0, &list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 }
Eric Dumazetd17fa6f2009-10-28 05:21:38 +00001380 unregister_netdevice_many(&list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001382 /* Wipe the cache */
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001383 list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
1384 if (!all && (c->mfc_flags & MFC_STATIC))
1385 continue;
1386 rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params);
1387 list_del_rcu(&c->list);
Yotam Gigib3620532017-09-27 08:23:14 +02001388 call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c,
1389 mrt->id);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001390 mroute_netlink_event(mrt, c, RTM_DELROUTE);
Yotam Gigi310ebbb2017-09-27 08:23:12 +02001391 ipmr_cache_put(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 }
1393
Patrick McHardy0c122952010-04-13 05:03:22 +00001394 if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 spin_lock_bh(&mfc_unres_lock);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01001396 list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
Patrick McHardy862465f2010-04-13 05:03:21 +00001397 list_del(&c->list);
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00001398 mroute_netlink_event(mrt, c, RTM_DELROUTE);
Patrick McHardy0c122952010-04-13 05:03:22 +00001399 ipmr_destroy_unres(mrt, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 }
1401 spin_unlock_bh(&mfc_unres_lock);
1402 }
1403}
1404
Eric Dumazet4c968702010-10-01 16:15:01 +00001405/* called from ip_ra_control(), before an RCU grace period,
1406 * we dont need to call synchronize_rcu() here
1407 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408static void mrtsock_destruct(struct sock *sk)
1409{
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001410 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001411 struct mr_table *mrt;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001412
WANG Cong1215e512017-04-12 12:32:13 -07001413 ASSERT_RTNL();
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001414 ipmr_for_each_table(mrt, net) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001415 if (sk == rtnl_dereference(mrt->mroute_sk)) {
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001416 IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
David Ahern3b022862017-03-28 14:28:02 -07001417 inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
1418 NETCONFA_MC_FORWARDING,
Nicolas Dichteld67b8c62012-12-04 01:13:35 +00001419 NETCONFA_IFINDEX_ALL,
1420 net->ipv4.devconf_all);
Stephen Hemmingera9b3cd72011-08-01 16:19:00 +00001421 RCU_INIT_POINTER(mrt->mroute_sk, NULL);
Nikolay Aleksandrov0e615e92015-11-20 13:54:19 +01001422 mroute_clean_tables(mrt, false);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001423 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425}
1426
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001427/* Socket options and virtual interface manipulation. The whole
1428 * virtual interface system is a complete heap, but unfortunately
1429 * that's how BSD mrouted happens to think. Maybe one day with a proper
1430 * MOSPF/PIM router set up we can clean this up.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001432
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001433int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval,
1434 unsigned int optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435{
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001436 struct net *net = sock_net(sk);
1437 int val, ret = 0, parent = 0;
1438 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 struct vifctl vif;
1440 struct mfcctl mfc;
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001441 u32 uval;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001442
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001443 /* There's one exception to the lock - MRT_DONE which needs to unlock */
1444 rtnl_lock();
Eric Dumazet5e1859f2012-11-25 06:41:45 +00001445 if (sk->sk_type != SOCK_RAW ||
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001446 inet_sk(sk)->inet_num != IPPROTO_IGMP) {
1447 ret = -EOPNOTSUPP;
1448 goto out_unlock;
1449 }
Eric Dumazet5e1859f2012-11-25 06:41:45 +00001450
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001451 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001452 if (!mrt) {
1453 ret = -ENOENT;
1454 goto out_unlock;
1455 }
Stephen Hemminger132adf52007-03-08 20:44:43 -08001456 if (optname != MRT_INIT) {
Eric Dumazet33d480c2011-08-11 19:30:52 +00001457 if (sk != rcu_access_pointer(mrt->mroute_sk) &&
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001458 !ns_capable(net->user_ns, CAP_NET_ADMIN)) {
1459 ret = -EACCES;
1460 goto out_unlock;
1461 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 }
1463
Stephen Hemminger132adf52007-03-08 20:44:43 -08001464 switch (optname) {
1465 case MRT_INIT:
Nikolay Aleksandrov42e6b892015-11-26 15:23:49 +01001466 if (optlen != sizeof(int)) {
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001467 ret = -EINVAL;
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001468 break;
Nikolay Aleksandrov42e6b892015-11-26 15:23:49 +01001469 }
1470 if (rtnl_dereference(mrt->mroute_sk)) {
1471 ret = -EADDRINUSE;
1472 break;
1473 }
Stephen Hemminger132adf52007-03-08 20:44:43 -08001474
1475 ret = ip_ra_control(sk, 1, mrtsock_destruct);
1476 if (ret == 0) {
Eric Dumazetcf778b02012-01-12 04:41:32 +00001477 rcu_assign_pointer(mrt->mroute_sk, sk);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001478 IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
David Ahern3b022862017-03-28 14:28:02 -07001479 inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
1480 NETCONFA_MC_FORWARDING,
Nicolas Dichteld67b8c62012-12-04 01:13:35 +00001481 NETCONFA_IFINDEX_ALL,
1482 net->ipv4.devconf_all);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001483 }
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001484 break;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001485 case MRT_DONE:
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001486 if (sk != rcu_access_pointer(mrt->mroute_sk)) {
1487 ret = -EACCES;
1488 } else {
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001489 ret = ip_ra_control(sk, 0, NULL);
WANG Cong1215e512017-04-12 12:32:13 -07001490 goto out_unlock;
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001491 }
1492 break;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001493 case MRT_ADD_VIF:
1494 case MRT_DEL_VIF:
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001495 if (optlen != sizeof(vif)) {
1496 ret = -EINVAL;
1497 break;
1498 }
1499 if (copy_from_user(&vif, optval, sizeof(vif))) {
1500 ret = -EFAULT;
1501 break;
1502 }
1503 if (vif.vifc_vifi >= MAXVIFS) {
1504 ret = -ENFILE;
1505 break;
1506 }
Jianjun Kongc354e122008-11-03 00:28:02 -08001507 if (optname == MRT_ADD_VIF) {
Eric Dumazet4c968702010-10-01 16:15:01 +00001508 ret = vif_add(net, mrt, &vif,
1509 sk == rtnl_dereference(mrt->mroute_sk));
Stephen Hemminger132adf52007-03-08 20:44:43 -08001510 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00001511 ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001512 }
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001513 break;
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001514 /* Manipulate the forwarding caches. These live
1515 * in a sort of kernel/user symbiosis.
1516 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001517 case MRT_ADD_MFC:
1518 case MRT_DEL_MFC:
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001519 parent = -1;
1520 case MRT_ADD_MFC_PROXY:
1521 case MRT_DEL_MFC_PROXY:
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001522 if (optlen != sizeof(mfc)) {
1523 ret = -EINVAL;
1524 break;
1525 }
1526 if (copy_from_user(&mfc, optval, sizeof(mfc))) {
1527 ret = -EFAULT;
1528 break;
1529 }
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001530 if (parent == 0)
1531 parent = mfc.mfcc_parent;
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001532 if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
1533 ret = ipmr_mfc_delete(mrt, &mfc, parent);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001534 else
Eric Dumazet4c968702010-10-01 16:15:01 +00001535 ret = ipmr_mfc_add(net, mrt, &mfc,
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001536 sk == rtnl_dereference(mrt->mroute_sk),
1537 parent);
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001538 break;
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001539 /* Control PIM assert. */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001540 case MRT_ASSERT:
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001541 if (optlen != sizeof(val)) {
1542 ret = -EINVAL;
1543 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 }
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001545 if (get_user(val, (int __user *)optval)) {
1546 ret = -EFAULT;
1547 break;
1548 }
1549 mrt->mroute_do_assert = val;
1550 break;
1551 case MRT_PIM:
Nikolay Aleksandrov1973a4e2015-11-26 15:23:48 +01001552 if (!ipmr_pimsm_enabled()) {
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001553 ret = -ENOPROTOOPT;
1554 break;
1555 }
1556 if (optlen != sizeof(val)) {
1557 ret = -EINVAL;
1558 break;
1559 }
1560 if (get_user(val, (int __user *)optval)) {
1561 ret = -EFAULT;
1562 break;
1563 }
1564
1565 val = !!val;
1566 if (val != mrt->mroute_do_pim) {
1567 mrt->mroute_do_pim = val;
1568 mrt->mroute_do_assert = val;
1569 }
1570 break;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001571 case MRT_TABLE:
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001572 if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) {
1573 ret = -ENOPROTOOPT;
1574 break;
1575 }
1576 if (optlen != sizeof(uval)) {
1577 ret = -EINVAL;
1578 break;
1579 }
1580 if (get_user(uval, (u32 __user *)optval)) {
1581 ret = -EFAULT;
1582 break;
1583 }
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001584
Eric Dumazet4c968702010-10-01 16:15:01 +00001585 if (sk == rtnl_dereference(mrt->mroute_sk)) {
1586 ret = -EBUSY;
1587 } else {
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001588 mrt = ipmr_new_table(net, uval);
Nikolay Aleksandrov1113ebb2015-11-21 15:57:24 +01001589 if (IS_ERR(mrt))
1590 ret = PTR_ERR(mrt);
Eric Dumazet5e1859f2012-11-25 06:41:45 +00001591 else
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001592 raw_sk(sk)->ipmr_table = uval;
Eric Dumazet4c968702010-10-01 16:15:01 +00001593 }
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001594 break;
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001595 /* Spurious command, or MRT_VERSION which you cannot set. */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001596 default:
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001597 ret = -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 }
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001599out_unlock:
1600 rtnl_unlock();
Nikolay Aleksandrov29e97d22015-11-21 15:57:31 +01001601 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602}
1603
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001604/* Getsock opt support for the multicast routing system. */
Jianjun Kongc354e122008-11-03 00:28:02 -08001605int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606{
1607 int olr;
1608 int val;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001609 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001610 struct mr_table *mrt;
1611
Eric Dumazet5e1859f2012-11-25 06:41:45 +00001612 if (sk->sk_type != SOCK_RAW ||
1613 inet_sk(sk)->inet_num != IPPROTO_IGMP)
1614 return -EOPNOTSUPP;
1615
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001616 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
Ian Morris51456b22015-04-03 09:17:26 +01001617 if (!mrt)
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001618 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619
Nikolay Aleksandrovfe9ef3c2015-11-21 15:57:28 +01001620 switch (optname) {
1621 case MRT_VERSION:
Jianjun Kongc354e122008-11-03 00:28:02 -08001622 val = 0x0305;
Nikolay Aleksandrovfe9ef3c2015-11-21 15:57:28 +01001623 break;
1624 case MRT_PIM:
Nikolay Aleksandrov1973a4e2015-11-26 15:23:48 +01001625 if (!ipmr_pimsm_enabled())
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +01001626 return -ENOPROTOOPT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001627 val = mrt->mroute_do_pim;
Nikolay Aleksandrovfe9ef3c2015-11-21 15:57:28 +01001628 break;
1629 case MRT_ASSERT:
Patrick McHardy0c122952010-04-13 05:03:22 +00001630 val = mrt->mroute_do_assert;
Nikolay Aleksandrovfe9ef3c2015-11-21 15:57:28 +01001631 break;
1632 default:
1633 return -ENOPROTOOPT;
Nikolay Aleksandrovc316c622015-11-21 15:57:26 +01001634 }
Nikolay Aleksandrovfe9ef3c2015-11-21 15:57:28 +01001635
1636 if (get_user(olr, optlen))
1637 return -EFAULT;
1638 olr = min_t(unsigned int, olr, sizeof(int));
1639 if (olr < 0)
1640 return -EINVAL;
1641 if (put_user(olr, optlen))
1642 return -EFAULT;
Jianjun Kongc354e122008-11-03 00:28:02 -08001643 if (copy_to_user(optval, &val, olr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 return -EFAULT;
1645 return 0;
1646}
1647
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001648/* The IP multicast ioctl support routines. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
1650{
1651 struct sioc_sg_req sr;
1652 struct sioc_vif_req vr;
1653 struct vif_device *vif;
1654 struct mfc_cache *c;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001655 struct net *net = sock_net(sk);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001656 struct mr_table *mrt;
1657
1658 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
Ian Morris51456b22015-04-03 09:17:26 +01001659 if (!mrt)
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001660 return -ENOENT;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001661
Stephen Hemminger132adf52007-03-08 20:44:43 -08001662 switch (cmd) {
1663 case SIOCGETVIFCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001664 if (copy_from_user(&vr, arg, sizeof(vr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001665 return -EFAULT;
Patrick McHardy0c122952010-04-13 05:03:22 +00001666 if (vr.vifi >= mrt->maxvif)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001667 return -EINVAL;
1668 read_lock(&mrt_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00001669 vif = &mrt->vif_table[vr.vifi];
1670 if (VIF_EXISTS(mrt, vr.vifi)) {
Jianjun Kongc354e122008-11-03 00:28:02 -08001671 vr.icount = vif->pkt_in;
1672 vr.ocount = vif->pkt_out;
1673 vr.ibytes = vif->bytes_in;
1674 vr.obytes = vif->bytes_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001676
Jianjun Kongc354e122008-11-03 00:28:02 -08001677 if (copy_to_user(arg, &vr, sizeof(vr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 return -EFAULT;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001679 return 0;
1680 }
1681 read_unlock(&mrt_lock);
1682 return -EADDRNOTAVAIL;
1683 case SIOCGETSGCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001684 if (copy_from_user(&sr, arg, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001685 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Eric Dumazeta8c94862010-10-01 16:15:08 +00001687 rcu_read_lock();
Patrick McHardy0c122952010-04-13 05:03:22 +00001688 c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001689 if (c) {
1690 sr.pktcnt = c->mfc_un.res.pkt;
1691 sr.bytecnt = c->mfc_un.res.bytes;
1692 sr.wrong_if = c->mfc_un.res.wrong_if;
Eric Dumazeta8c94862010-10-01 16:15:08 +00001693 rcu_read_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -08001694
Jianjun Kongc354e122008-11-03 00:28:02 -08001695 if (copy_to_user(arg, &sr, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001696 return -EFAULT;
1697 return 0;
1698 }
Eric Dumazeta8c94862010-10-01 16:15:08 +00001699 rcu_read_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -08001700 return -EADDRNOTAVAIL;
1701 default:
1702 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 }
1704}
1705
Eric W. Biederman709b46e2011-01-29 16:15:56 +00001706#ifdef CONFIG_COMPAT
1707struct compat_sioc_sg_req {
1708 struct in_addr src;
1709 struct in_addr grp;
1710 compat_ulong_t pktcnt;
1711 compat_ulong_t bytecnt;
1712 compat_ulong_t wrong_if;
1713};
1714
David S. Millerca6b8bb02011-02-03 17:24:28 -08001715struct compat_sioc_vif_req {
1716 vifi_t vifi; /* Which iface */
1717 compat_ulong_t icount;
1718 compat_ulong_t ocount;
1719 compat_ulong_t ibytes;
1720 compat_ulong_t obytes;
1721};
1722
Eric W. Biederman709b46e2011-01-29 16:15:56 +00001723int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
1724{
David S. Miller0033d5a2011-02-03 17:21:31 -08001725 struct compat_sioc_sg_req sr;
David S. Millerca6b8bb02011-02-03 17:24:28 -08001726 struct compat_sioc_vif_req vr;
1727 struct vif_device *vif;
Eric W. Biederman709b46e2011-01-29 16:15:56 +00001728 struct mfc_cache *c;
1729 struct net *net = sock_net(sk);
1730 struct mr_table *mrt;
1731
1732 mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
Ian Morris51456b22015-04-03 09:17:26 +01001733 if (!mrt)
Eric W. Biederman709b46e2011-01-29 16:15:56 +00001734 return -ENOENT;
1735
1736 switch (cmd) {
David S. Millerca6b8bb02011-02-03 17:24:28 -08001737 case SIOCGETVIFCNT:
1738 if (copy_from_user(&vr, arg, sizeof(vr)))
1739 return -EFAULT;
1740 if (vr.vifi >= mrt->maxvif)
1741 return -EINVAL;
1742 read_lock(&mrt_lock);
1743 vif = &mrt->vif_table[vr.vifi];
1744 if (VIF_EXISTS(mrt, vr.vifi)) {
1745 vr.icount = vif->pkt_in;
1746 vr.ocount = vif->pkt_out;
1747 vr.ibytes = vif->bytes_in;
1748 vr.obytes = vif->bytes_out;
1749 read_unlock(&mrt_lock);
1750
1751 if (copy_to_user(arg, &vr, sizeof(vr)))
1752 return -EFAULT;
1753 return 0;
1754 }
1755 read_unlock(&mrt_lock);
1756 return -EADDRNOTAVAIL;
Eric W. Biederman709b46e2011-01-29 16:15:56 +00001757 case SIOCGETSGCNT:
1758 if (copy_from_user(&sr, arg, sizeof(sr)))
1759 return -EFAULT;
1760
1761 rcu_read_lock();
1762 c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
1763 if (c) {
1764 sr.pktcnt = c->mfc_un.res.pkt;
1765 sr.bytecnt = c->mfc_un.res.bytes;
1766 sr.wrong_if = c->mfc_un.res.wrong_if;
1767 rcu_read_unlock();
1768
1769 if (copy_to_user(arg, &sr, sizeof(sr)))
1770 return -EFAULT;
1771 return 0;
1772 }
1773 rcu_read_unlock();
1774 return -EADDRNOTAVAIL;
1775 default:
1776 return -ENOIOCTLCMD;
1777 }
1778}
1779#endif
1780
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
1782{
Jiri Pirko351638e2013-05-28 01:30:21 +00001783 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001784 struct net *net = dev_net(dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001785 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 struct vif_device *v;
1787 int ct;
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001788
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 if (event != NETDEV_UNREGISTER)
1790 return NOTIFY_DONE;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001791
1792 ipmr_for_each_table(mrt, net) {
1793 v = &mrt->vif_table[0];
1794 for (ct = 0; ct < mrt->maxvif; ct++, v++) {
1795 if (v->dev == dev)
RongQing.Lie92036a2011-11-23 23:10:52 +00001796 vif_delete(mrt, ct, 1, NULL);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00001797 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 }
1799 return NOTIFY_DONE;
1800}
1801
Jianjun Kongc354e122008-11-03 00:28:02 -08001802static struct notifier_block ip_mr_notifier = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 .notifier_call = ipmr_device_event,
1804};
1805
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001806/* Encapsulate a packet by attaching a valid IPIP header to it.
1807 * This avoids tunnel drivers and other mess and gives us the speed so
1808 * important for multicast video.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 */
Hannes Frederic Sowab6a77192015-03-25 17:07:44 +01001810static void ip_encap(struct net *net, struct sk_buff *skb,
1811 __be32 saddr, __be32 daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812{
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001813 struct iphdr *iph;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001814 const struct iphdr *old_iph = ip_hdr(skb);
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001815
1816 skb_push(skb, sizeof(struct iphdr));
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001817 skb->transport_header = skb->network_header;
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001818 skb_reset_network_header(skb);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001819 iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001821 iph->version = 4;
Arnaldo Carvalho de Meloe023dd62007-03-12 20:09:36 -03001822 iph->tos = old_iph->tos;
1823 iph->ttl = old_iph->ttl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 iph->frag_off = 0;
1825 iph->daddr = daddr;
1826 iph->saddr = saddr;
1827 iph->protocol = IPPROTO_IPIP;
1828 iph->ihl = 5;
1829 iph->tot_len = htons(skb->len);
Hannes Frederic Sowab6a77192015-03-25 17:07:44 +01001830 ip_select_ident(net, skb, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831 ip_send_check(iph);
1832
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
1834 nf_reset(skb);
1835}
1836
Eric W. Biederman0c4b51f2015-09-15 20:04:18 -05001837static inline int ipmr_forward_finish(struct net *net, struct sock *sk,
1838 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839{
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001840 struct ip_options *opt = &(IPCB(skb)->opt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841
David S. Miller73186df2015-11-03 13:41:45 -05001842 IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
1843 IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844
1845 if (unlikely(opt->optlen))
1846 ip_forward_options(skb);
1847
Eric W. Biederman13206b62015-10-07 16:48:35 -05001848 return dst_output(net, sk, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849}
1850
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001851/* Processing handlers for ipmr_forward */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852
Patrick McHardy0c122952010-04-13 05:03:22 +00001853static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
1854 struct sk_buff *skb, struct mfc_cache *c, int vifi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855{
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001856 const struct iphdr *iph = ip_hdr(skb);
Patrick McHardy0c122952010-04-13 05:03:22 +00001857 struct vif_device *vif = &mrt->vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858 struct net_device *dev;
1859 struct rtable *rt;
David S. Miller31e4543d2011-05-03 20:25:42 -07001860 struct flowi4 fl4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861 int encap = 0;
1862
Ian Morris51456b22015-04-03 09:17:26 +01001863 if (!vif->dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864 goto out_free;
1865
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 if (vif->flags & VIFF_REGISTER) {
1867 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001868 vif->bytes_out += skb->len;
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -07001869 vif->dev->stats.tx_bytes += skb->len;
1870 vif->dev->stats.tx_packets++;
Patrick McHardy0c122952010-04-13 05:03:22 +00001871 ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
Ilpo Järvinen69ebbf52009-02-06 23:46:51 -08001872 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001875 if (vif->flags & VIFF_TUNNEL) {
David S. Miller31e4543d2011-05-03 20:25:42 -07001876 rt = ip_route_output_ports(net, &fl4, NULL,
David S. Miller78fbfd82011-03-12 00:00:52 -05001877 vif->remote, vif->local,
1878 0, 0,
1879 IPPROTO_IPIP,
1880 RT_TOS(iph->tos), vif->link);
David S. Millerb23dd4f2011-03-02 14:31:35 -08001881 if (IS_ERR(rt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 goto out_free;
1883 encap = sizeof(struct iphdr);
1884 } else {
David S. Miller31e4543d2011-05-03 20:25:42 -07001885 rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0,
David S. Miller78fbfd82011-03-12 00:00:52 -05001886 0, 0,
1887 IPPROTO_IPIP,
1888 RT_TOS(iph->tos), vif->link);
David S. Millerb23dd4f2011-03-02 14:31:35 -08001889 if (IS_ERR(rt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 goto out_free;
1891 }
1892
Changli Gaod8d1f302010-06-10 23:31:35 -07001893 dev = rt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894
Changli Gaod8d1f302010-06-10 23:31:35 -07001895 if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 /* Do not fragment multicasts. Alas, IPv4 does not
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001897 * allow to send ICMP, so that packets will disappear
1898 * to blackhole.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 */
David S. Miller73186df2015-11-03 13:41:45 -05001900 IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 ip_rt_put(rt);
1902 goto out_free;
1903 }
1904
Changli Gaod8d1f302010-06-10 23:31:35 -07001905 encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906
1907 if (skb_cow(skb, encap)) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001908 ip_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 goto out_free;
1910 }
1911
1912 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001913 vif->bytes_out += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914
Eric Dumazetadf30902009-06-02 05:19:30 +00001915 skb_dst_drop(skb);
Changli Gaod8d1f302010-06-10 23:31:35 -07001916 skb_dst_set(skb, &rt->dst);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001917 ip_decrease_ttl(ip_hdr(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918
1919 /* FIXME: forward and output firewalls used to be called here.
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001920 * What do we do with netfilter? -- RR
1921 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 if (vif->flags & VIFF_TUNNEL) {
Hannes Frederic Sowab6a77192015-03-25 17:07:44 +01001923 ip_encap(net, skb, vif->local, vif->remote);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 /* FIXME: extra output firewall step used to be here. --RR */
Pavel Emelyanov2f4c02d2008-05-21 14:16:14 -07001925 vif->dev->stats.tx_packets++;
1926 vif->dev->stats.tx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 }
1928
Lance Richardson9ee6c5d2016-11-02 16:36:17 -04001929 IPCB(skb)->flags |= IPSKB_FORWARDED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001931 /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 * not only before forwarding, but after forwarding on all output
1933 * interfaces. It is clear, if mrouter runs a multicasting
1934 * program, it should receive packets not depending to what interface
1935 * program is joined.
1936 * If we will not make it, the program will have to join on all
1937 * interfaces. On the other hand, multihoming host (or router, but
1938 * not mrouter) cannot join to more than one interface - it will
1939 * result in receiving multiple packets.
1940 */
Eric W. Biederman29a26a52015-09-15 20:04:16 -05001941 NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
1942 net, NULL, skb, skb->dev, dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 ipmr_forward_finish);
1944 return;
1945
1946out_free:
1947 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948}
1949
Patrick McHardy0c122952010-04-13 05:03:22 +00001950static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951{
1952 int ct;
Patrick McHardy0c122952010-04-13 05:03:22 +00001953
1954 for (ct = mrt->maxvif-1; ct >= 0; ct--) {
1955 if (mrt->vif_table[ct].dev == dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 break;
1957 }
1958 return ct;
1959}
1960
1961/* "local" means that we should preserve one skb (for local delivery) */
Rami Rosenc4854ec2013-07-20 15:09:28 +03001962static void ip_mr_forward(struct net *net, struct mr_table *mrt,
Donald Sharp4b1f0d32017-06-10 16:30:17 -04001963 struct net_device *dev, struct sk_buff *skb,
1964 struct mfc_cache *cache, int local)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965{
Donald Sharp4b1f0d32017-06-10 16:30:17 -04001966 int true_vifi = ipmr_find_vif(mrt, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 int psend = -1;
1968 int vif, ct;
1969
1970 vif = cache->mfc_parent;
1971 cache->mfc_un.res.pkt++;
1972 cache->mfc_un.res.bytes += skb->len;
Nikolay Aleksandrov43b9e122016-07-14 19:28:27 +03001973 cache->mfc_un.res.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974
Nicolas Dichtel360eb5d2013-01-22 11:18:03 +01001975 if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) {
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00001976 struct mfc_cache *cache_proxy;
1977
1978 /* For an (*,G) entry, we only check that the incomming
1979 * interface is part of the static tree.
1980 */
1981 cache_proxy = ipmr_cache_find_any_parent(mrt, vif);
1982 if (cache_proxy &&
1983 cache_proxy->mfc_un.res.ttls[true_vifi] < 255)
1984 goto forward;
1985 }
1986
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01001987 /* Wrong interface: drop packet and (maybe) send PIM assert. */
Donald Sharp4b1f0d32017-06-10 16:30:17 -04001988 if (mrt->vif_table[vif].dev != dev) {
David S. Millerc7537962010-11-11 17:07:48 -08001989 if (rt_is_output_route(skb_rtable(skb))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 /* It is our own packet, looped back.
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00001991 * Very complicated situation...
1992 *
1993 * The best workaround until routing daemons will be
1994 * fixed is not to redistribute packet, if it was
1995 * send through wrong interface. It means, that
1996 * multicast applications WILL NOT work for
1997 * (S,G), which have default multicast route pointing
1998 * to wrong oif. In any case, it is not a good
1999 * idea to use multicasting applications on router.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 */
2001 goto dont_forward;
2002 }
2003
2004 cache->mfc_un.res.wrong_if++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005
Patrick McHardy0c122952010-04-13 05:03:22 +00002006 if (true_vifi >= 0 && mrt->mroute_do_assert &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 /* pimsm uses asserts, when switching from RPT to SPT,
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002008 * so that we cannot check that packet arrived on an oif.
2009 * It is bad, but otherwise we would need to move pretty
2010 * large chunk of pimd to kernel. Ough... --ANK
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 */
Patrick McHardy0c122952010-04-13 05:03:22 +00002012 (mrt->mroute_do_pim ||
Benjamin Thery6f9374a2009-01-22 04:56:20 +00002013 cache->mfc_un.res.ttls[true_vifi] < 255) &&
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002014 time_after(jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
2016 cache->mfc_un.res.last_assert = jiffies;
Patrick McHardy0c122952010-04-13 05:03:22 +00002017 ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 }
2019 goto dont_forward;
2020 }
2021
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002022forward:
Patrick McHardy0c122952010-04-13 05:03:22 +00002023 mrt->vif_table[vif].pkt_in++;
2024 mrt->vif_table[vif].bytes_in += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01002026 /* Forward the frame */
Nicolas Dichtel360eb5d2013-01-22 11:18:03 +01002027 if (cache->mfc_origin == htonl(INADDR_ANY) &&
2028 cache->mfc_mcastgrp == htonl(INADDR_ANY)) {
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002029 if (true_vifi >= 0 &&
2030 true_vifi != cache->mfc_parent &&
2031 ip_hdr(skb)->ttl >
2032 cache->mfc_un.res.ttls[cache->mfc_parent]) {
2033 /* It's an (*,*) entry and the packet is not coming from
2034 * the upstream: forward the packet to the upstream
2035 * only.
2036 */
2037 psend = cache->mfc_parent;
2038 goto last_forward;
2039 }
2040 goto dont_forward;
2041 }
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002042 for (ct = cache->mfc_un.res.maxvif - 1;
2043 ct >= cache->mfc_un.res.minvif; ct--) {
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002044 /* For (*,G) entry, don't forward to the incoming interface */
Nicolas Dichtel360eb5d2013-01-22 11:18:03 +01002045 if ((cache->mfc_origin != htonl(INADDR_ANY) ||
2046 ct != true_vifi) &&
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002047 ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 if (psend != -1) {
2049 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002050
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 if (skb2)
Patrick McHardy0c122952010-04-13 05:03:22 +00002052 ipmr_queue_xmit(net, mrt, skb2, cache,
2053 psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 }
Jianjun Kongc354e122008-11-03 00:28:02 -08002055 psend = ct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 }
2057 }
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002058last_forward:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 if (psend != -1) {
2060 if (local) {
2061 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 if (skb2)
Patrick McHardy0c122952010-04-13 05:03:22 +00002064 ipmr_queue_xmit(net, mrt, skb2, cache, psend);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 } else {
Patrick McHardy0c122952010-04-13 05:03:22 +00002066 ipmr_queue_xmit(net, mrt, skb, cache, psend);
Rami Rosenc4854ec2013-07-20 15:09:28 +03002067 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 }
2069 }
2070
2071dont_forward:
2072 if (!local)
2073 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074}
2075
David S. Miller417da662011-05-03 19:42:43 -07002076static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb)
David S. Milleree3f1aa2011-03-09 14:06:20 -08002077{
David S. Miller417da662011-05-03 19:42:43 -07002078 struct rtable *rt = skb_rtable(skb);
2079 struct iphdr *iph = ip_hdr(skb);
David S. Millerda919812011-03-12 02:04:50 -05002080 struct flowi4 fl4 = {
David S. Miller417da662011-05-03 19:42:43 -07002081 .daddr = iph->daddr,
2082 .saddr = iph->saddr,
Julian Anastasovb0fe4a32011-07-23 02:00:41 +00002083 .flowi4_tos = RT_TOS(iph->tos),
David S. Miller4fd551d2012-07-17 14:39:44 -07002084 .flowi4_oif = (rt_is_output_route(rt) ?
2085 skb->dev->ifindex : 0),
2086 .flowi4_iif = (rt_is_output_route(rt) ?
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002087 LOOPBACK_IFINDEX :
David S. Miller4fd551d2012-07-17 14:39:44 -07002088 skb->dev->ifindex),
David Millerb4869882012-07-01 02:03:01 +00002089 .flowi4_mark = skb->mark,
David S. Milleree3f1aa2011-03-09 14:06:20 -08002090 };
2091 struct mr_table *mrt;
2092 int err;
2093
David S. Millerda919812011-03-12 02:04:50 -05002094 err = ipmr_fib_lookup(net, &fl4, &mrt);
David S. Milleree3f1aa2011-03-09 14:06:20 -08002095 if (err)
2096 return ERR_PTR(err);
2097 return mrt;
2098}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01002100/* Multicast packets for forwarding arrive here
2101 * Called with rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103int ip_mr_input(struct sk_buff *skb)
2104{
2105 struct mfc_cache *cache;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00002106 struct net *net = dev_net(skb->dev);
Eric Dumazet511c3f92009-06-02 05:14:27 +00002107 int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002108 struct mr_table *mrt;
Thomas Winterbcfc7d32017-05-16 10:14:44 +12002109 struct net_device *dev;
2110
2111 /* skb->dev passed in is the loX master dev for vrfs.
2112 * As there are no vifs associated with loopback devices,
2113 * get the proper interface that does have a vif associated with it.
2114 */
2115 dev = skb->dev;
2116 if (netif_is_l3_master(skb->dev)) {
2117 dev = dev_get_by_index_rcu(net, IPCB(skb)->iif);
2118 if (!dev) {
2119 kfree_skb(skb);
2120 return -ENODEV;
2121 }
2122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123
2124 /* Packet is looped back after forward, it should not be
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002125 * forwarded second time, but still can be delivered locally.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 */
Eric Dumazet4c968702010-10-01 16:15:01 +00002127 if (IPCB(skb)->flags & IPSKB_FORWARDED)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 goto dont_forward;
2129
David S. Miller417da662011-05-03 19:42:43 -07002130 mrt = ipmr_rt_fib_lookup(net, skb);
David S. Milleree3f1aa2011-03-09 14:06:20 -08002131 if (IS_ERR(mrt)) {
2132 kfree_skb(skb);
2133 return PTR_ERR(mrt);
Ben Greeare40dbc52010-07-15 13:22:33 +00002134 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 if (!local) {
Eric Dumazet4c968702010-10-01 16:15:01 +00002136 if (IPCB(skb)->opt.router_alert) {
2137 if (ip_call_ra_chain(skb))
2138 return 0;
2139 } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) {
2140 /* IGMPv1 (and broken IGMPv2 implementations sort of
2141 * Cisco IOS <= 11.2(8)) do not put router alert
2142 * option to IGMP packets destined to routable
2143 * groups. It is very bad, because it means
2144 * that we can forward NO IGMP messages.
2145 */
2146 struct sock *mroute_sk;
2147
2148 mroute_sk = rcu_dereference(mrt->mroute_sk);
2149 if (mroute_sk) {
2150 nf_reset(skb);
2151 raw_rcv(mroute_sk, skb);
2152 return 0;
2153 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 }
2155 }
2156
Eric Dumazeta8c94862010-10-01 16:15:08 +00002157 /* already under rcu_read_lock() */
Patrick McHardy0c122952010-04-13 05:03:22 +00002158 cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
Ian Morris51456b22015-04-03 09:17:26 +01002159 if (!cache) {
Thomas Winterbcfc7d32017-05-16 10:14:44 +12002160 int vif = ipmr_find_vif(mrt, dev);
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002161
2162 if (vif >= 0)
2163 cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr,
2164 vif);
2165 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01002167 /* No usable cache entry */
Ian Morris51456b22015-04-03 09:17:26 +01002168 if (!cache) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 int vif;
2170
2171 if (local) {
2172 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
2173 ip_local_deliver(skb);
Ian Morris51456b22015-04-03 09:17:26 +01002174 if (!skb2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 skb = skb2;
2177 }
2178
Eric Dumazeta8c94862010-10-01 16:15:08 +00002179 read_lock(&mrt_lock);
Thomas Winterbcfc7d32017-05-16 10:14:44 +12002180 vif = ipmr_find_vif(mrt, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181 if (vif >= 0) {
Donald Sharp4b1f0d32017-06-10 16:30:17 -04002182 int err2 = ipmr_cache_unresolved(mrt, vif, skb, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 read_unlock(&mrt_lock);
2184
Eric Dumazet0eae88f2010-04-20 19:06:52 -07002185 return err2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186 }
2187 read_unlock(&mrt_lock);
2188 kfree_skb(skb);
2189 return -ENODEV;
2190 }
2191
Eric Dumazeta8c94862010-10-01 16:15:08 +00002192 read_lock(&mrt_lock);
Donald Sharp4b1f0d32017-06-10 16:30:17 -04002193 ip_mr_forward(net, mrt, dev, skb, cache, local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 read_unlock(&mrt_lock);
2195
2196 if (local)
2197 return ip_local_deliver(skb);
2198
2199 return 0;
2200
2201dont_forward:
2202 if (local)
2203 return ip_local_deliver(skb);
2204 kfree_skb(skb);
2205 return 0;
2206}
2207
Ilpo Järvinenb1879202008-12-16 01:15:11 -08002208#ifdef CONFIG_IP_PIMSM_V1
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01002209/* Handle IGMP messages of PIMv1 */
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002210int pim_rcv_v1(struct sk_buff *skb)
Ilpo Järvinenb1879202008-12-16 01:15:11 -08002211{
2212 struct igmphdr *pim;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00002213 struct net *net = dev_net(skb->dev);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002214 struct mr_table *mrt;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08002215
2216 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
2217 goto drop;
2218
2219 pim = igmp_hdr(skb);
2220
David S. Miller417da662011-05-03 19:42:43 -07002221 mrt = ipmr_rt_fib_lookup(net, skb);
David S. Milleree3f1aa2011-03-09 14:06:20 -08002222 if (IS_ERR(mrt))
2223 goto drop;
Patrick McHardy0c122952010-04-13 05:03:22 +00002224 if (!mrt->mroute_do_pim ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08002225 pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
2226 goto drop;
2227
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002228 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08002229drop:
2230 kfree_skb(skb);
2231 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 return 0;
2233}
2234#endif
2235
2236#ifdef CONFIG_IP_PIMSM_V2
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002237static int pim_rcv(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238{
2239 struct pimreghdr *pim;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002240 struct net *net = dev_net(skb->dev);
2241 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242
Ilpo Järvinenb1879202008-12-16 01:15:11 -08002243 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 goto drop;
2245
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07002246 pim = (struct pimreghdr *)skb_transport_header(skb);
Nikolay Aleksandrov56245ca2016-10-31 13:21:04 +01002247 if (pim->type != ((PIM_VERSION << 4) | (PIM_TYPE_REGISTER)) ||
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002248 (pim->flags & PIM_NULL_REGISTER) ||
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002249 (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
Al Virod3bc23e2006-11-14 21:24:49 -08002250 csum_fold(skb_checksum(skb, 0, skb->len, 0))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 goto drop;
2252
David S. Miller417da662011-05-03 19:42:43 -07002253 mrt = ipmr_rt_fib_lookup(net, skb);
David S. Milleree3f1aa2011-03-09 14:06:20 -08002254 if (IS_ERR(mrt))
2255 goto drop;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002256 if (__pim_rcv(mrt, skb, sizeof(*pim))) {
Ilpo Järvinenb1879202008-12-16 01:15:11 -08002257drop:
2258 kfree_skb(skb);
2259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 return 0;
2261}
2262#endif
2263
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002264static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
2265 struct mfc_cache *c, struct rtmsg *rtm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266{
Nicolas Dichteladfa85e2012-12-04 01:13:37 +00002267 struct rta_mfc_stats mfcs;
Nikolay Aleksandrov43b9e122016-07-14 19:28:27 +03002268 struct nlattr *mp_attr;
2269 struct rtnexthop *nhp;
Nikolay Aleksandrovb5036cd2016-09-20 16:17:22 +02002270 unsigned long lastuse;
Nikolay Aleksandrov43b9e122016-07-14 19:28:27 +03002271 int ct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272
Nicolas Dichtel74381892010-03-25 23:45:35 +00002273 /* If cache is unresolved, don't try to parse IIF and OIF */
Nikolay Aleksandrov1708ebc2017-01-03 12:13:39 +01002274 if (c->mfc_parent >= MAXVIFS) {
2275 rtm->rtm_flags |= RTNH_F_UNRESOLVED;
Nicolas Dichtel74381892010-03-25 23:45:35 +00002276 return -ENOENT;
Nikolay Aleksandrov1708ebc2017-01-03 12:13:39 +01002277 }
Nicolas Dichtel74381892010-03-25 23:45:35 +00002278
Thomas Graf92a395e2012-06-26 23:36:13 +00002279 if (VIF_EXISTS(mrt, c->mfc_parent) &&
2280 nla_put_u32(skb, RTA_IIF, mrt->vif_table[c->mfc_parent].dev->ifindex) < 0)
2281 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282
Yotam Gigic7c0bbe2017-09-27 08:23:15 +02002283 if (c->mfc_flags & MFC_OFFLOAD)
2284 rtm->rtm_flags |= RTNH_F_OFFLOAD;
2285
Thomas Graf92a395e2012-06-26 23:36:13 +00002286 if (!(mp_attr = nla_nest_start(skb, RTA_MULTIPATH)))
2287 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288
2289 for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00002290 if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
Thomas Graf92a395e2012-06-26 23:36:13 +00002291 if (!(nhp = nla_reserve_nohdr(skb, sizeof(*nhp)))) {
2292 nla_nest_cancel(skb, mp_attr);
2293 return -EMSGSIZE;
2294 }
2295
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 nhp->rtnh_flags = 0;
2297 nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
Patrick McHardy0c122952010-04-13 05:03:22 +00002298 nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 nhp->rtnh_len = sizeof(*nhp);
2300 }
2301 }
Thomas Graf92a395e2012-06-26 23:36:13 +00002302
2303 nla_nest_end(skb, mp_attr);
2304
Nikolay Aleksandrovb5036cd2016-09-20 16:17:22 +02002305 lastuse = READ_ONCE(c->mfc_un.res.lastuse);
2306 lastuse = time_after_eq(jiffies, lastuse) ? jiffies - lastuse : 0;
2307
Nicolas Dichteladfa85e2012-12-04 01:13:37 +00002308 mfcs.mfcs_packets = c->mfc_un.res.pkt;
2309 mfcs.mfcs_bytes = c->mfc_un.res.bytes;
2310 mfcs.mfcs_wrong_if = c->mfc_un.res.wrong_if;
Nikolay Aleksandrov43b9e122016-07-14 19:28:27 +03002311 if (nla_put_64bit(skb, RTA_MFC_STATS, sizeof(mfcs), &mfcs, RTA_PAD) ||
Nikolay Aleksandrovb5036cd2016-09-20 16:17:22 +02002312 nla_put_u64_64bit(skb, RTA_EXPIRES, jiffies_to_clock_t(lastuse),
Nikolay Aleksandrov43b9e122016-07-14 19:28:27 +03002313 RTA_PAD))
Nicolas Dichteladfa85e2012-12-04 01:13:37 +00002314 return -EMSGSIZE;
2315
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316 rtm->rtm_type = RTN_MULTICAST;
2317 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318}
2319
David S. Miller9a1b9492011-05-04 12:18:54 -07002320int ipmr_get_route(struct net *net, struct sk_buff *skb,
2321 __be32 saddr, __be32 daddr,
David Ahern9f09eae2017-01-06 17:39:06 -08002322 struct rtmsg *rtm, u32 portid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 struct mfc_cache *cache;
David S. Miller9a1b9492011-05-04 12:18:54 -07002325 struct mr_table *mrt;
2326 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002328 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
Ian Morris51456b22015-04-03 09:17:26 +01002329 if (!mrt)
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002330 return -ENOENT;
2331
Eric Dumazeta8c94862010-10-01 16:15:08 +00002332 rcu_read_lock();
David S. Miller9a1b9492011-05-04 12:18:54 -07002333 cache = ipmr_cache_find(mrt, saddr, daddr);
Ian Morris51456b22015-04-03 09:17:26 +01002334 if (!cache && skb->dev) {
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002335 int vif = ipmr_find_vif(mrt, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336
Nicolas Dichtel660b26d2013-01-21 06:00:26 +00002337 if (vif >= 0)
2338 cache = ipmr_cache_find_any(mrt, daddr, vif);
2339 }
Ian Morris51456b22015-04-03 09:17:26 +01002340 if (!cache) {
Alexey Kuznetsov72287492006-07-25 16:45:12 -07002341 struct sk_buff *skb2;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07002342 struct iphdr *iph;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 struct net_device *dev;
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002344 int vif = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346 dev = skb->dev;
Eric Dumazeta8c94862010-10-01 16:15:08 +00002347 read_lock(&mrt_lock);
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00002348 if (dev)
2349 vif = ipmr_find_vif(mrt, dev);
2350 if (vif < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00002352 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 return -ENODEV;
2354 }
Alexey Kuznetsov72287492006-07-25 16:45:12 -07002355 skb2 = skb_clone(skb, GFP_ATOMIC);
2356 if (!skb2) {
2357 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00002358 rcu_read_unlock();
Alexey Kuznetsov72287492006-07-25 16:45:12 -07002359 return -ENOMEM;
2360 }
2361
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02002362 NETLINK_CB(skb2).portid = portid;
Arnaldo Carvalho de Meloe2d1bca2007-04-10 20:46:21 -07002363 skb_push(skb2, sizeof(struct iphdr));
2364 skb_reset_network_header(skb2);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07002365 iph = ip_hdr(skb2);
2366 iph->ihl = sizeof(struct iphdr) >> 2;
David S. Miller9a1b9492011-05-04 12:18:54 -07002367 iph->saddr = saddr;
2368 iph->daddr = daddr;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07002369 iph->version = 0;
Donald Sharp4b1f0d32017-06-10 16:30:17 -04002370 err = ipmr_cache_unresolved(mrt, vif, skb2, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00002372 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373 return err;
2374 }
2375
Eric Dumazeta8c94862010-10-01 16:15:08 +00002376 read_lock(&mrt_lock);
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002377 err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 read_unlock(&mrt_lock);
Eric Dumazeta8c94862010-10-01 16:15:08 +00002379 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380 return err;
2381}
2382
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002383static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
Nicolas Dichtel65886f42014-03-19 17:47:50 +01002384 u32 portid, u32 seq, struct mfc_cache *c, int cmd,
2385 int flags)
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002386{
2387 struct nlmsghdr *nlh;
2388 struct rtmsg *rtm;
Nicolas Dichtel1eb99af2012-12-04 01:13:39 +00002389 int err;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002390
Nicolas Dichtel65886f42014-03-19 17:47:50 +01002391 nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags);
Ian Morris51456b22015-04-03 09:17:26 +01002392 if (!nlh)
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002393 return -EMSGSIZE;
2394
2395 rtm = nlmsg_data(nlh);
2396 rtm->rtm_family = RTNL_FAMILY_IPMR;
2397 rtm->rtm_dst_len = 32;
2398 rtm->rtm_src_len = 32;
2399 rtm->rtm_tos = 0;
2400 rtm->rtm_table = mrt->id;
David S. Millerf3756b72012-04-01 20:39:02 -04002401 if (nla_put_u32(skb, RTA_TABLE, mrt->id))
2402 goto nla_put_failure;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002403 rtm->rtm_type = RTN_MULTICAST;
2404 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
Nicolas Dichtel9a68ac72012-12-04 01:13:38 +00002405 if (c->mfc_flags & MFC_STATIC)
2406 rtm->rtm_protocol = RTPROT_STATIC;
2407 else
2408 rtm->rtm_protocol = RTPROT_MROUTED;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002409 rtm->rtm_flags = 0;
2410
Jiri Benc930345e2015-03-29 16:59:25 +02002411 if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) ||
2412 nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp))
David S. Millerf3756b72012-04-01 20:39:02 -04002413 goto nla_put_failure;
Nicolas Dichtel1eb99af2012-12-04 01:13:39 +00002414 err = __ipmr_fill_mroute(mrt, skb, c, rtm);
2415 /* do not break the dump if cache is unresolved */
2416 if (err < 0 && err != -ENOENT)
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002417 goto nla_put_failure;
2418
Johannes Berg053c0952015-01-16 22:09:00 +01002419 nlmsg_end(skb, nlh);
2420 return 0;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002421
2422nla_put_failure:
2423 nlmsg_cancel(skb, nlh);
2424 return -EMSGSIZE;
2425}
2426
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00002427static size_t mroute_msgsize(bool unresolved, int maxvif)
2428{
2429 size_t len =
2430 NLMSG_ALIGN(sizeof(struct rtmsg))
2431 + nla_total_size(4) /* RTA_TABLE */
2432 + nla_total_size(4) /* RTA_SRC */
2433 + nla_total_size(4) /* RTA_DST */
2434 ;
2435
2436 if (!unresolved)
2437 len = len
2438 + nla_total_size(4) /* RTA_IIF */
2439 + nla_total_size(0) /* RTA_MULTIPATH */
2440 + maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
2441 /* RTA_MFC_STATS */
Nicolas Dichtela9a08042016-04-21 18:58:26 +02002442 + nla_total_size_64bit(sizeof(struct rta_mfc_stats))
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00002443 ;
2444
2445 return len;
2446}
2447
2448static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
2449 int cmd)
2450{
2451 struct net *net = read_pnet(&mrt->net);
2452 struct sk_buff *skb;
2453 int err = -ENOBUFS;
2454
2455 skb = nlmsg_new(mroute_msgsize(mfc->mfc_parent >= MAXVIFS, mrt->maxvif),
2456 GFP_ATOMIC);
Ian Morris51456b22015-04-03 09:17:26 +01002457 if (!skb)
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00002458 goto errout;
2459
Nicolas Dichtel65886f42014-03-19 17:47:50 +01002460 err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0);
Nicolas Dichtel8cd3ac92012-12-04 01:13:40 +00002461 if (err < 0)
2462 goto errout;
2463
2464 rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC);
2465 return;
2466
2467errout:
2468 kfree_skb(skb);
2469 if (err < 0)
2470 rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err);
2471}
2472
Julien Gomes5a645dd2017-06-20 13:54:17 -07002473static size_t igmpmsg_netlink_msgsize(size_t payloadlen)
2474{
2475 size_t len =
2476 NLMSG_ALIGN(sizeof(struct rtgenmsg))
2477 + nla_total_size(1) /* IPMRA_CREPORT_MSGTYPE */
2478 + nla_total_size(4) /* IPMRA_CREPORT_VIF_ID */
2479 + nla_total_size(4) /* IPMRA_CREPORT_SRC_ADDR */
2480 + nla_total_size(4) /* IPMRA_CREPORT_DST_ADDR */
2481 /* IPMRA_CREPORT_PKT */
2482 + nla_total_size(payloadlen)
2483 ;
2484
2485 return len;
2486}
2487
2488static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt)
2489{
2490 struct net *net = read_pnet(&mrt->net);
2491 struct nlmsghdr *nlh;
2492 struct rtgenmsg *rtgenm;
2493 struct igmpmsg *msg;
2494 struct sk_buff *skb;
2495 struct nlattr *nla;
2496 int payloadlen;
2497
2498 payloadlen = pkt->len - sizeof(struct igmpmsg);
2499 msg = (struct igmpmsg *)skb_network_header(pkt);
2500
2501 skb = nlmsg_new(igmpmsg_netlink_msgsize(payloadlen), GFP_ATOMIC);
2502 if (!skb)
2503 goto errout;
2504
2505 nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT,
2506 sizeof(struct rtgenmsg), 0);
2507 if (!nlh)
2508 goto errout;
2509 rtgenm = nlmsg_data(nlh);
2510 rtgenm->rtgen_family = RTNL_FAMILY_IPMR;
2511 if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) ||
2512 nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif) ||
2513 nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR,
2514 msg->im_src.s_addr) ||
2515 nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR,
2516 msg->im_dst.s_addr))
2517 goto nla_put_failure;
2518
2519 nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen);
2520 if (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg),
2521 nla_data(nla), payloadlen))
2522 goto nla_put_failure;
2523
2524 nlmsg_end(skb, nlh);
2525
2526 rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC);
2527 return;
2528
2529nla_put_failure:
2530 nlmsg_cancel(skb, nlh);
2531errout:
2532 kfree_skb(skb);
2533 rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS);
2534}
2535
Donald Sharp4f75ba62017-06-28 13:58:57 -04002536static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
2537 struct netlink_ext_ack *extack)
2538{
2539 struct net *net = sock_net(in_skb->sk);
2540 struct nlattr *tb[RTA_MAX + 1];
2541 struct sk_buff *skb = NULL;
2542 struct mfc_cache *cache;
2543 struct mr_table *mrt;
2544 struct rtmsg *rtm;
2545 __be32 src, grp;
2546 u32 tableid;
2547 int err;
2548
2549 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
2550 rtm_ipv4_policy, extack);
2551 if (err < 0)
2552 goto errout;
2553
2554 rtm = nlmsg_data(nlh);
2555
2556 src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0;
2557 grp = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0;
2558 tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0;
2559
2560 mrt = ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT);
Dan Carpenter2e3d2322017-07-12 10:56:47 +03002561 if (!mrt) {
2562 err = -ENOENT;
Donald Sharp4f75ba62017-06-28 13:58:57 -04002563 goto errout_free;
2564 }
2565
2566 /* entries are added/deleted only under RTNL */
2567 rcu_read_lock();
2568 cache = ipmr_cache_find(mrt, src, grp);
2569 rcu_read_unlock();
2570 if (!cache) {
2571 err = -ENOENT;
2572 goto errout_free;
2573 }
2574
2575 skb = nlmsg_new(mroute_msgsize(false, mrt->maxvif), GFP_KERNEL);
2576 if (!skb) {
2577 err = -ENOBUFS;
2578 goto errout_free;
2579 }
2580
2581 err = ipmr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid,
2582 nlh->nlmsg_seq, cache,
2583 RTM_NEWROUTE, 0);
2584 if (err < 0)
2585 goto errout_free;
2586
2587 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
2588
2589errout:
2590 return err;
2591
2592errout_free:
2593 kfree_skb(skb);
2594 goto errout;
2595}
2596
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002597static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
2598{
2599 struct net *net = sock_net(skb->sk);
2600 struct mr_table *mrt;
2601 struct mfc_cache *mfc;
2602 unsigned int t = 0, s_t;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002603 unsigned int e = 0, s_e;
2604
2605 s_t = cb->args[0];
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01002606 s_e = cb->args[1];
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002607
Eric Dumazeta8c94862010-10-01 16:15:08 +00002608 rcu_read_lock();
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002609 ipmr_for_each_table(mrt, net) {
2610 if (t < s_t)
2611 goto next_table;
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01002612 list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) {
2613 if (e < s_e)
2614 goto next_entry;
2615 if (ipmr_fill_mroute(mrt, skb,
2616 NETLINK_CB(cb->skb).portid,
2617 cb->nlh->nlmsg_seq,
2618 mfc, RTM_NEWROUTE,
2619 NLM_F_MULTI) < 0)
2620 goto done;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002621next_entry:
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01002622 e++;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002623 }
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01002624 e = 0;
2625 s_e = 0;
2626
Nicolas Dichtel1eb99af2012-12-04 01:13:39 +00002627 spin_lock_bh(&mfc_unres_lock);
2628 list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) {
2629 if (e < s_e)
2630 goto next_entry2;
2631 if (ipmr_fill_mroute(mrt, skb,
2632 NETLINK_CB(cb->skb).portid,
2633 cb->nlh->nlmsg_seq,
Nicolas Dichtel65886f42014-03-19 17:47:50 +01002634 mfc, RTM_NEWROUTE,
2635 NLM_F_MULTI) < 0) {
Nicolas Dichtel1eb99af2012-12-04 01:13:39 +00002636 spin_unlock_bh(&mfc_unres_lock);
2637 goto done;
2638 }
2639next_entry2:
2640 e++;
2641 }
2642 spin_unlock_bh(&mfc_unres_lock);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01002643 e = 0;
2644 s_e = 0;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002645next_table:
2646 t++;
2647 }
2648done:
Eric Dumazeta8c94862010-10-01 16:15:08 +00002649 rcu_read_unlock();
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002650
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01002651 cb->args[1] = e;
Patrick McHardycb6a4e42010-04-26 16:02:08 +02002652 cb->args[0] = t;
2653
2654 return skb->len;
2655}
2656
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +01002657static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = {
2658 [RTA_SRC] = { .type = NLA_U32 },
2659 [RTA_DST] = { .type = NLA_U32 },
2660 [RTA_IIF] = { .type = NLA_U32 },
2661 [RTA_TABLE] = { .type = NLA_U32 },
2662 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
2663};
2664
2665static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol)
2666{
2667 switch (rtm_protocol) {
2668 case RTPROT_STATIC:
2669 case RTPROT_MROUTED:
2670 return true;
2671 }
2672 return false;
2673}
2674
2675static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc)
2676{
2677 struct rtnexthop *rtnh = nla_data(nla);
2678 int remaining = nla_len(nla), vifi = 0;
2679
2680 while (rtnh_ok(rtnh, remaining)) {
2681 mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops;
2682 if (++vifi == MAXVIFS)
2683 break;
2684 rtnh = rtnh_next(rtnh, &remaining);
2685 }
2686
2687 return remaining > 0 ? -EINVAL : vifi;
2688}
2689
2690/* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */
2691static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh,
2692 struct mfcctl *mfcc, int *mrtsock,
David Ahernc21ef3e2017-04-16 09:48:24 -07002693 struct mr_table **mrtret,
2694 struct netlink_ext_ack *extack)
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +01002695{
2696 struct net_device *dev = NULL;
2697 u32 tblid = RT_TABLE_DEFAULT;
2698 struct mr_table *mrt;
2699 struct nlattr *attr;
2700 struct rtmsg *rtm;
2701 int ret, rem;
2702
Johannes Bergfceb6432017-04-12 14:34:07 +02002703 ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy,
David Ahernc21ef3e2017-04-16 09:48:24 -07002704 extack);
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +01002705 if (ret < 0)
2706 goto out;
2707 rtm = nlmsg_data(nlh);
2708
2709 ret = -EINVAL;
2710 if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 ||
2711 rtm->rtm_type != RTN_MULTICAST ||
2712 rtm->rtm_scope != RT_SCOPE_UNIVERSE ||
2713 !ipmr_rtm_validate_proto(rtm->rtm_protocol))
2714 goto out;
2715
2716 memset(mfcc, 0, sizeof(*mfcc));
2717 mfcc->mfcc_parent = -1;
2718 ret = 0;
2719 nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) {
2720 switch (nla_type(attr)) {
2721 case RTA_SRC:
2722 mfcc->mfcc_origin.s_addr = nla_get_be32(attr);
2723 break;
2724 case RTA_DST:
2725 mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr);
2726 break;
2727 case RTA_IIF:
2728 dev = __dev_get_by_index(net, nla_get_u32(attr));
2729 if (!dev) {
2730 ret = -ENODEV;
2731 goto out;
2732 }
2733 break;
2734 case RTA_MULTIPATH:
2735 if (ipmr_nla_get_ttls(attr, mfcc) < 0) {
2736 ret = -EINVAL;
2737 goto out;
2738 }
2739 break;
2740 case RTA_PREFSRC:
2741 ret = 1;
2742 break;
2743 case RTA_TABLE:
2744 tblid = nla_get_u32(attr);
2745 break;
2746 }
2747 }
2748 mrt = ipmr_get_table(net, tblid);
2749 if (!mrt) {
2750 ret = -ENOENT;
2751 goto out;
2752 }
2753 *mrtret = mrt;
2754 *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0;
2755 if (dev)
2756 mfcc->mfcc_parent = ipmr_find_vif(mrt, dev);
2757
2758out:
2759 return ret;
2760}
2761
2762/* takes care of both newroute and delroute */
David Ahernc21ef3e2017-04-16 09:48:24 -07002763static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh,
2764 struct netlink_ext_ack *extack)
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +01002765{
2766 struct net *net = sock_net(skb->sk);
2767 int ret, mrtsock, parent;
2768 struct mr_table *tbl;
2769 struct mfcctl mfcc;
2770
2771 mrtsock = 0;
2772 tbl = NULL;
David Ahernc21ef3e2017-04-16 09:48:24 -07002773 ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl, extack);
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +01002774 if (ret < 0)
2775 return ret;
2776
2777 parent = ret ? mfcc.mfcc_parent : -1;
2778 if (nlh->nlmsg_type == RTM_NEWROUTE)
2779 return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent);
2780 else
2781 return ipmr_mfc_delete(tbl, &mfcc, parent);
2782}
2783
Nikolay Aleksandrov772c3442017-06-07 18:02:32 +03002784static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb)
2785{
2786 u32 queue_len = atomic_read(&mrt->cache_resolve_queue_len);
2787
2788 if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) ||
2789 nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) ||
2790 nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM,
2791 mrt->mroute_reg_vif_num) ||
2792 nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT,
2793 mrt->mroute_do_assert) ||
2794 nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM, mrt->mroute_do_pim))
2795 return false;
2796
2797 return true;
2798}
2799
2800static bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb)
2801{
2802 struct nlattr *vif_nest;
2803 struct vif_device *vif;
2804
2805 /* if the VIF doesn't exist just continue */
2806 if (!VIF_EXISTS(mrt, vifid))
2807 return true;
2808
2809 vif = &mrt->vif_table[vifid];
2810 vif_nest = nla_nest_start(skb, IPMRA_VIF);
2811 if (!vif_nest)
2812 return false;
2813 if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif->dev->ifindex) ||
2814 nla_put_u32(skb, IPMRA_VIFA_VIF_ID, vifid) ||
2815 nla_put_u16(skb, IPMRA_VIFA_FLAGS, vif->flags) ||
2816 nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, vif->bytes_in,
2817 IPMRA_VIFA_PAD) ||
2818 nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, vif->bytes_out,
2819 IPMRA_VIFA_PAD) ||
2820 nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, vif->pkt_in,
2821 IPMRA_VIFA_PAD) ||
2822 nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, vif->pkt_out,
2823 IPMRA_VIFA_PAD) ||
2824 nla_put_be32(skb, IPMRA_VIFA_LOCAL_ADDR, vif->local) ||
2825 nla_put_be32(skb, IPMRA_VIFA_REMOTE_ADDR, vif->remote)) {
2826 nla_nest_cancel(skb, vif_nest);
2827 return false;
2828 }
2829 nla_nest_end(skb, vif_nest);
2830
2831 return true;
2832}
2833
2834static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb)
2835{
2836 struct net *net = sock_net(skb->sk);
2837 struct nlmsghdr *nlh = NULL;
2838 unsigned int t = 0, s_t;
2839 unsigned int e = 0, s_e;
2840 struct mr_table *mrt;
2841
2842 s_t = cb->args[0];
2843 s_e = cb->args[1];
2844
2845 ipmr_for_each_table(mrt, net) {
2846 struct nlattr *vifs, *af;
2847 struct ifinfomsg *hdr;
2848 u32 i;
2849
2850 if (t < s_t)
2851 goto skip_table;
2852 nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
2853 cb->nlh->nlmsg_seq, RTM_NEWLINK,
2854 sizeof(*hdr), NLM_F_MULTI);
2855 if (!nlh)
2856 break;
2857
2858 hdr = nlmsg_data(nlh);
2859 memset(hdr, 0, sizeof(*hdr));
2860 hdr->ifi_family = RTNL_FAMILY_IPMR;
2861
2862 af = nla_nest_start(skb, IFLA_AF_SPEC);
2863 if (!af) {
2864 nlmsg_cancel(skb, nlh);
2865 goto out;
2866 }
2867
2868 if (!ipmr_fill_table(mrt, skb)) {
2869 nlmsg_cancel(skb, nlh);
2870 goto out;
2871 }
2872
2873 vifs = nla_nest_start(skb, IPMRA_TABLE_VIFS);
2874 if (!vifs) {
2875 nla_nest_end(skb, af);
2876 nlmsg_end(skb, nlh);
2877 goto out;
2878 }
2879 for (i = 0; i < mrt->maxvif; i++) {
2880 if (e < s_e)
2881 goto skip_entry;
2882 if (!ipmr_fill_vif(mrt, i, skb)) {
2883 nla_nest_end(skb, vifs);
2884 nla_nest_end(skb, af);
2885 nlmsg_end(skb, nlh);
2886 goto out;
2887 }
2888skip_entry:
2889 e++;
2890 }
2891 s_e = 0;
2892 e = 0;
2893 nla_nest_end(skb, vifs);
2894 nla_nest_end(skb, af);
2895 nlmsg_end(skb, nlh);
2896skip_table:
2897 t++;
2898 }
2899
2900out:
2901 cb->args[1] = e;
2902 cb->args[0] = t;
2903
2904 return skb->len;
2905}
2906
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002907#ifdef CONFIG_PROC_FS
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01002908/* The /proc interfaces to multicast routing :
2909 * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910 */
2911struct ipmr_vif_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002912 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002913 struct mr_table *mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 int ct;
2915};
2916
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002917static struct vif_device *ipmr_vif_seq_idx(struct net *net,
2918 struct ipmr_vif_iter *iter,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919 loff_t pos)
2920{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002921 struct mr_table *mrt = iter->mrt;
Patrick McHardy0c122952010-04-13 05:03:22 +00002922
2923 for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) {
2924 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002925 continue;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002926 if (pos-- == 0)
Patrick McHardy0c122952010-04-13 05:03:22 +00002927 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 }
2929 return NULL;
2930}
2931
2932static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002933 __acquires(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002934{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002935 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002936 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002937 struct mr_table *mrt;
2938
2939 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
Ian Morris51456b22015-04-03 09:17:26 +01002940 if (!mrt)
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002941 return ERR_PTR(-ENOENT);
2942
2943 iter->mrt = mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002944
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 read_lock(&mrt_lock);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002946 return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947 : SEQ_START_TOKEN;
2948}
2949
2950static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2951{
2952 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002953 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002954 struct mr_table *mrt = iter->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002955
2956 ++*pos;
2957 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002958 return ipmr_vif_seq_idx(net, iter, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002959
Patrick McHardy0c122952010-04-13 05:03:22 +00002960 while (++iter->ct < mrt->maxvif) {
2961 if (!VIF_EXISTS(mrt, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962 continue;
Patrick McHardy0c122952010-04-13 05:03:22 +00002963 return &mrt->vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964 }
2965 return NULL;
2966}
2967
2968static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08002969 __releases(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002970{
2971 read_unlock(&mrt_lock);
2972}
2973
2974static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
2975{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00002976 struct ipmr_vif_iter *iter = seq->private;
2977 struct mr_table *mrt = iter->mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002978
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002980 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981 "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n");
2982 } else {
2983 const struct vif_device *vif = v;
2984 const char *name = vif->dev ? vif->dev->name : "none";
2985
2986 seq_printf(seq,
Alexey Dobriyan5b5e0922017-02-27 14:30:02 -08002987 "%2zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n",
Patrick McHardy0c122952010-04-13 05:03:22 +00002988 vif - mrt->vif_table,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002989 name, vif->bytes_in, vif->pkt_in,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990 vif->bytes_out, vif->pkt_out,
2991 vif->flags, vif->local, vif->remote);
2992 }
2993 return 0;
2994}
2995
Stephen Hemmingerf6908082007-03-12 14:34:29 -07002996static const struct seq_operations ipmr_vif_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002997 .start = ipmr_vif_seq_start,
2998 .next = ipmr_vif_seq_next,
2999 .stop = ipmr_vif_seq_stop,
3000 .show = ipmr_vif_seq_show,
3001};
3002
3003static int ipmr_vif_open(struct inode *inode, struct file *file)
3004{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003005 return seq_open_net(inode, file, &ipmr_vif_seq_ops,
3006 sizeof(struct ipmr_vif_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003007}
3008
Arjan van de Ven9a321442007-02-12 00:55:35 -08003009static const struct file_operations ipmr_vif_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003010 .owner = THIS_MODULE,
3011 .open = ipmr_vif_open,
3012 .read = seq_read,
3013 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003014 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003015};
3016
3017struct ipmr_mfc_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003018 struct seq_net_private p;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003019 struct mr_table *mrt;
Patrick McHardy862465f2010-04-13 05:03:21 +00003020 struct list_head *cache;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021};
3022
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003023static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
3024 struct ipmr_mfc_iter *it, loff_t pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003025{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003026 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003027 struct mfc_cache *mfc;
3028
Eric Dumazeta8c94862010-10-01 16:15:08 +00003029 rcu_read_lock();
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01003030 it->cache = &mrt->mfc_cache_list;
3031 list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list)
3032 if (pos-- == 0)
3033 return mfc;
Eric Dumazeta8c94862010-10-01 16:15:08 +00003034 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003035
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy0c122952010-04-13 05:03:22 +00003037 it->cache = &mrt->mfc_unres_queue;
Patrick McHardy862465f2010-04-13 05:03:21 +00003038 list_for_each_entry(mfc, it->cache, list)
Patrick McHardye258beb2010-04-13 05:03:19 +00003039 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040 return mfc;
3041 spin_unlock_bh(&mfc_unres_lock);
3042
3043 it->cache = NULL;
3044 return NULL;
3045}
3046
3047
3048static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
3049{
3050 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003051 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003052 struct mr_table *mrt;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003053
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003054 mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
Ian Morris51456b22015-04-03 09:17:26 +01003055 if (!mrt)
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003056 return ERR_PTR(-ENOENT);
3057
3058 it->mrt = mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003059 it->cache = NULL;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003060 return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061 : SEQ_START_TOKEN;
3062}
3063
3064static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
3065{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003067 struct net *net = seq_file_net(seq);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003068 struct mr_table *mrt = it->mrt;
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01003069 struct mfc_cache *mfc = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003070
3071 ++*pos;
3072
3073 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003074 return ipmr_mfc_seq_idx(net, seq->private, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003075
Patrick McHardy862465f2010-04-13 05:03:21 +00003076 if (mfc->list.next != it->cache)
3077 return list_entry(mfc->list.next, struct mfc_cache, list);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09003078
Patrick McHardy0c122952010-04-13 05:03:22 +00003079 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080 goto end_of_list;
3081
Linus Torvalds1da177e2005-04-16 15:20:36 -07003082 /* exhausted cache_array, show unresolved */
Eric Dumazeta8c94862010-10-01 16:15:08 +00003083 rcu_read_unlock();
Patrick McHardy0c122952010-04-13 05:03:22 +00003084 it->cache = &mrt->mfc_unres_queue;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09003085
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086 spin_lock_bh(&mfc_unres_lock);
Patrick McHardy862465f2010-04-13 05:03:21 +00003087 if (!list_empty(it->cache))
3088 return list_first_entry(it->cache, struct mfc_cache, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003089
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00003090end_of_list:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 spin_unlock_bh(&mfc_unres_lock);
3092 it->cache = NULL;
3093
3094 return NULL;
3095}
3096
3097static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
3098{
3099 struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003100 struct mr_table *mrt = it->mrt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003101
Patrick McHardy0c122952010-04-13 05:03:22 +00003102 if (it->cache == &mrt->mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003103 spin_unlock_bh(&mfc_unres_lock);
Nikolay Aleksandrov8fb472c2017-01-12 15:53:33 +01003104 else if (it->cache == &mrt->mfc_cache_list)
Eric Dumazeta8c94862010-10-01 16:15:08 +00003105 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003106}
3107
3108static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
3109{
3110 int n;
3111
3112 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09003113 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114 "Group Origin Iif Pkts Bytes Wrong Oifs\n");
3115 } else {
3116 const struct mfc_cache *mfc = v;
3117 const struct ipmr_mfc_iter *it = seq->private;
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003118 const struct mr_table *mrt = it->mrt;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09003119
Eric Dumazet0eae88f2010-04-20 19:06:52 -07003120 seq_printf(seq, "%08X %08X %-3hd",
3121 (__force u32) mfc->mfc_mcastgrp,
3122 (__force u32) mfc->mfc_origin,
Benjamin Thery1ea472e2008-12-03 22:21:47 -08003123 mfc->mfc_parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124
Patrick McHardy0c122952010-04-13 05:03:22 +00003125 if (it->cache != &mrt->mfc_unres_queue) {
Benjamin Thery1ea472e2008-12-03 22:21:47 -08003126 seq_printf(seq, " %8lu %8lu %8lu",
3127 mfc->mfc_un.res.pkt,
3128 mfc->mfc_un.res.bytes,
3129 mfc->mfc_un.res.wrong_if);
Stephen Hemminger132adf52007-03-08 20:44:43 -08003130 for (n = mfc->mfc_un.res.minvif;
Eric Dumazeta8cb16d2010-10-01 16:15:29 +00003131 n < mfc->mfc_un.res.maxvif; n++) {
Patrick McHardy0c122952010-04-13 05:03:22 +00003132 if (VIF_EXISTS(mrt, n) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +00003133 mfc->mfc_un.res.ttls[n] < 255)
3134 seq_printf(seq,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09003135 " %2d:%-3d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136 n, mfc->mfc_un.res.ttls[n]);
3137 }
Benjamin Thery1ea472e2008-12-03 22:21:47 -08003138 } else {
3139 /* unresolved mfc_caches don't contain
3140 * pkt, bytes and wrong_if values
3141 */
3142 seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003143 }
3144 seq_putc(seq, '\n');
3145 }
3146 return 0;
3147}
3148
Stephen Hemmingerf6908082007-03-12 14:34:29 -07003149static const struct seq_operations ipmr_mfc_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150 .start = ipmr_mfc_seq_start,
3151 .next = ipmr_mfc_seq_next,
3152 .stop = ipmr_mfc_seq_stop,
3153 .show = ipmr_mfc_seq_show,
3154};
3155
3156static int ipmr_mfc_open(struct inode *inode, struct file *file)
3157{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003158 return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
3159 sizeof(struct ipmr_mfc_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003160}
3161
Arjan van de Ven9a321442007-02-12 00:55:35 -08003162static const struct file_operations ipmr_mfc_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003163 .owner = THIS_MODULE,
3164 .open = ipmr_mfc_open,
3165 .read = seq_read,
3166 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003167 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003168};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09003169#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170
3171#ifdef CONFIG_IP_PIMSM_V2
Alexey Dobriyan32613092009-09-14 12:21:47 +00003172static const struct net_protocol pim_protocol = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173 .handler = pim_rcv,
Tom Goff403dbb92009-06-14 03:16:13 -07003174 .netns_ok = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003175};
3176#endif
3177
Yotam Gigi4d65b942017-09-27 08:23:13 +02003178static unsigned int ipmr_seq_read(struct net *net)
3179{
3180 ASSERT_RTNL();
3181
3182 return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net);
3183}
3184
3185static int ipmr_dump(struct net *net, struct notifier_block *nb)
3186{
3187 struct mr_table *mrt;
3188 int err;
3189
3190 err = ipmr_rules_dump(net, nb);
3191 if (err)
3192 return err;
3193
3194 ipmr_for_each_table(mrt, net) {
3195 struct vif_device *v = &mrt->vif_table[0];
3196 struct mfc_cache *mfc;
3197 int vifi;
3198
3199 /* Notifiy on table VIF entries */
3200 read_lock(&mrt_lock);
3201 for (vifi = 0; vifi < mrt->maxvif; vifi++, v++) {
3202 if (!v->dev)
3203 continue;
3204
3205 call_ipmr_vif_entry_notifier(nb, net, FIB_EVENT_VIF_ADD,
3206 v, vifi, mrt->id);
3207 }
3208 read_unlock(&mrt_lock);
3209
3210 /* Notify on table MFC entries */
3211 list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list)
3212 call_ipmr_mfc_entry_notifier(nb, net,
3213 FIB_EVENT_ENTRY_ADD, mfc,
3214 mrt->id);
3215 }
3216
3217 return 0;
3218}
3219
3220static const struct fib_notifier_ops ipmr_notifier_ops_template = {
3221 .family = RTNL_FAMILY_IPMR,
3222 .fib_seq_read = ipmr_seq_read,
3223 .fib_dump = ipmr_dump,
3224 .owner = THIS_MODULE,
3225};
3226
3227int __net_init ipmr_notifier_init(struct net *net)
3228{
3229 struct fib_notifier_ops *ops;
3230
3231 net->ipv4.ipmr_seq = 0;
3232
3233 ops = fib_notifier_ops_register(&ipmr_notifier_ops_template, net);
3234 if (IS_ERR(ops))
3235 return PTR_ERR(ops);
3236 net->ipv4.ipmr_notifier_ops = ops;
3237
3238 return 0;
3239}
3240
3241static void __net_exit ipmr_notifier_exit(struct net *net)
3242{
3243 fib_notifier_ops_unregister(net->ipv4.ipmr_notifier_ops);
3244 net->ipv4.ipmr_notifier_ops = NULL;
3245}
3246
Nikolay Aleksandrov7ef8f652015-11-21 15:57:27 +01003247/* Setup for IP multicast routing */
Benjamin Therycf958ae32009-01-22 04:56:16 +00003248static int __net_init ipmr_net_init(struct net *net)
3249{
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003250 int err;
Benjamin Therycf958ae32009-01-22 04:56:16 +00003251
Yotam Gigi4d65b942017-09-27 08:23:13 +02003252 err = ipmr_notifier_init(net);
3253 if (err)
3254 goto ipmr_notifier_fail;
3255
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003256 err = ipmr_rules_init(net);
3257 if (err < 0)
Yotam Gigi4d65b942017-09-27 08:23:13 +02003258 goto ipmr_rules_fail;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003259
3260#ifdef CONFIG_PROC_FS
3261 err = -ENOMEM;
Gao fengd4beaa62013-02-18 01:34:54 +00003262 if (!proc_create("ip_mr_vif", 0, net->proc_net, &ipmr_vif_fops))
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003263 goto proc_vif_fail;
Gao fengd4beaa62013-02-18 01:34:54 +00003264 if (!proc_create("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_fops))
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003265 goto proc_cache_fail;
3266#endif
Benjamin Thery2bb8b262009-01-22 04:56:18 +00003267 return 0;
3268
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003269#ifdef CONFIG_PROC_FS
3270proc_cache_fail:
Gao fengece31ff2013-02-18 01:34:56 +00003271 remove_proc_entry("ip_mr_vif", net->proc_net);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003272proc_vif_fail:
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003273 ipmr_rules_exit(net);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003274#endif
Yotam Gigi4d65b942017-09-27 08:23:13 +02003275ipmr_rules_fail:
3276 ipmr_notifier_exit(net);
3277ipmr_notifier_fail:
Benjamin Therycf958ae32009-01-22 04:56:16 +00003278 return err;
3279}
3280
3281static void __net_exit ipmr_net_exit(struct net *net)
3282{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003283#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00003284 remove_proc_entry("ip_mr_cache", net->proc_net);
3285 remove_proc_entry("ip_mr_vif", net->proc_net);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003286#endif
Yotam Gigi4d65b942017-09-27 08:23:13 +02003287 ipmr_notifier_exit(net);
Patrick McHardyf0ad0862010-04-13 05:03:23 +00003288 ipmr_rules_exit(net);
Benjamin Therycf958ae32009-01-22 04:56:16 +00003289}
3290
3291static struct pernet_operations ipmr_net_ops = {
3292 .init = ipmr_net_init,
3293 .exit = ipmr_net_exit,
3294};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09003295
Wang Chen03d2f892008-07-03 12:13:36 +08003296int __init ip_mr_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003297{
Wang Chen03d2f892008-07-03 12:13:36 +08003298 int err;
3299
Linus Torvalds1da177e2005-04-16 15:20:36 -07003300 mrt_cachep = kmem_cache_create("ip_mrt_cache",
3301 sizeof(struct mfc_cache),
Eric Dumazeta8c94862010-10-01 16:15:08 +00003302 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
Paul Mundt20c2df82007-07-20 10:11:58 +09003303 NULL);
Wang Chen03d2f892008-07-03 12:13:36 +08003304
Benjamin Therycf958ae32009-01-22 04:56:16 +00003305 err = register_pernet_subsys(&ipmr_net_ops);
3306 if (err)
3307 goto reg_pernet_fail;
3308
Wang Chen03d2f892008-07-03 12:13:36 +08003309 err = register_netdevice_notifier(&ip_mr_notifier);
3310 if (err)
3311 goto reg_notif_fail;
Tom Goff403dbb92009-06-14 03:16:13 -07003312#ifdef CONFIG_IP_PIMSM_V2
3313 if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) {
Joe Perches058bd4d2012-03-11 18:36:11 +00003314 pr_err("%s: can't add PIM protocol\n", __func__);
Tom Goff403dbb92009-06-14 03:16:13 -07003315 err = -EAGAIN;
3316 goto add_proto_fail;
3317 }
3318#endif
Greg Rosec7ac8672011-06-10 01:27:09 +00003319 rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE,
Florian Westphalb97bac62017-08-09 20:41:48 +02003320 ipmr_rtm_getroute, ipmr_rtm_dumproute, 0);
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +01003321 rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE,
Florian Westphalb97bac62017-08-09 20:41:48 +02003322 ipmr_rtm_route, NULL, 0);
Nikolay Aleksandrovccbb0aa2015-11-26 15:23:50 +01003323 rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE,
Florian Westphalb97bac62017-08-09 20:41:48 +02003324 ipmr_rtm_route, NULL, 0);
Nikolay Aleksandrov772c3442017-06-07 18:02:32 +03003325
3326 rtnl_register(RTNL_FAMILY_IPMR, RTM_GETLINK,
Florian Westphalb97bac62017-08-09 20:41:48 +02003327 NULL, ipmr_rtm_dumplink, 0);
Wang Chen03d2f892008-07-03 12:13:36 +08003328 return 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00003329
Tom Goff403dbb92009-06-14 03:16:13 -07003330#ifdef CONFIG_IP_PIMSM_V2
3331add_proto_fail:
3332 unregister_netdevice_notifier(&ip_mr_notifier);
3333#endif
Benjamin Theryc3e38892008-11-19 14:07:41 -08003334reg_notif_fail:
Benjamin Therycf958ae32009-01-22 04:56:16 +00003335 unregister_pernet_subsys(&ipmr_net_ops);
3336reg_pernet_fail:
Benjamin Theryc3e38892008-11-19 14:07:41 -08003337 kmem_cache_destroy(mrt_cachep);
Wang Chen03d2f892008-07-03 12:13:36 +08003338 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003339}