xref: /openbmc/linux/net/ipv6/ip6mr.c (revision 060f35a317ef09101b128f399dce7ed13d019461)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27bc570c8SYOSHIFUJI Hideaki /*
37bc570c8SYOSHIFUJI Hideaki  *	Linux IPv6 multicast routing support for BSD pim6sd
47bc570c8SYOSHIFUJI Hideaki  *	Based on net/ipv4/ipmr.c.
57bc570c8SYOSHIFUJI Hideaki  *
67bc570c8SYOSHIFUJI Hideaki  *	(c) 2004 Mickael Hoerdt, <hoerdt@clarinet.u-strasbg.fr>
77bc570c8SYOSHIFUJI Hideaki  *		LSIIT Laboratory, Strasbourg, France
87bc570c8SYOSHIFUJI Hideaki  *	(c) 2004 Jean-Philippe Andriot, <jean-philippe.andriot@6WIND.com>
97bc570c8SYOSHIFUJI Hideaki  *		6WIND, Paris, France
107bc570c8SYOSHIFUJI Hideaki  *	Copyright (C)2007,2008 USAGI/WIDE Project
117bc570c8SYOSHIFUJI Hideaki  *		YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
127bc570c8SYOSHIFUJI Hideaki  */
137bc570c8SYOSHIFUJI Hideaki 
147c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
157bc570c8SYOSHIFUJI Hideaki #include <linux/types.h>
167bc570c8SYOSHIFUJI Hideaki #include <linux/sched.h>
177bc570c8SYOSHIFUJI Hideaki #include <linux/errno.h>
187bc570c8SYOSHIFUJI Hideaki #include <linux/mm.h>
197bc570c8SYOSHIFUJI Hideaki #include <linux/kernel.h>
207bc570c8SYOSHIFUJI Hideaki #include <linux/fcntl.h>
217bc570c8SYOSHIFUJI Hideaki #include <linux/stat.h>
227bc570c8SYOSHIFUJI Hideaki #include <linux/socket.h>
237bc570c8SYOSHIFUJI Hideaki #include <linux/inet.h>
247bc570c8SYOSHIFUJI Hideaki #include <linux/netdevice.h>
257bc570c8SYOSHIFUJI Hideaki #include <linux/inetdevice.h>
267bc570c8SYOSHIFUJI Hideaki #include <linux/proc_fs.h>
277bc570c8SYOSHIFUJI Hideaki #include <linux/seq_file.h>
287bc570c8SYOSHIFUJI Hideaki #include <linux/init.h>
29e2d57766SDavid S. Miller #include <linux/compat.h>
300eb71a9dSNeilBrown #include <linux/rhashtable.h>
317bc570c8SYOSHIFUJI Hideaki #include <net/protocol.h>
327bc570c8SYOSHIFUJI Hideaki #include <linux/skbuff.h>
337bc570c8SYOSHIFUJI Hideaki #include <net/raw.h>
347bc570c8SYOSHIFUJI Hideaki #include <linux/notifier.h>
357bc570c8SYOSHIFUJI Hideaki #include <linux/if_arp.h>
367bc570c8SYOSHIFUJI Hideaki #include <net/checksum.h>
377bc570c8SYOSHIFUJI Hideaki #include <net/netlink.h>
38d1db275dSPatrick McHardy #include <net/fib_rules.h>
397bc570c8SYOSHIFUJI Hideaki 
407bc570c8SYOSHIFUJI Hideaki #include <net/ipv6.h>
417bc570c8SYOSHIFUJI Hideaki #include <net/ip6_route.h>
427bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
4314fb64e1SYOSHIFUJI Hideaki #include <linux/pim.h>
447bc570c8SYOSHIFUJI Hideaki #include <net/addrconf.h>
457bc570c8SYOSHIFUJI Hideaki #include <linux/netfilter_ipv6.h>
46bc3b2d7fSPaul Gortmaker #include <linux/export.h>
475d6e430dSDave Jones #include <net/ip6_checksum.h>
48d67b8c61SNicolas Dichtel #include <linux/netconf.h>
49cb9f1b78SWillem de Bruijn #include <net/ip_tunnels.h>
507bc570c8SYOSHIFUJI Hideaki 
5169d2c867SGustavo A. R. Silva #include <linux/nospec.h>
5269d2c867SGustavo A. R. Silva 
53d1db275dSPatrick McHardy struct ip6mr_rule {
54d1db275dSPatrick McHardy 	struct fib_rule		common;
55d1db275dSPatrick McHardy };
56d1db275dSPatrick McHardy 
57d1db275dSPatrick McHardy struct ip6mr_result {
58b70432f7SYuval Mintz 	struct mr_table	*mrt;
59d1db275dSPatrick McHardy };
60d1db275dSPatrick McHardy 
617bc570c8SYOSHIFUJI Hideaki /* Big lock, protecting vif table, mrt cache and mroute socket state.
627bc570c8SYOSHIFUJI Hideaki    Note that the changes are semaphored via rtnl_lock.
637bc570c8SYOSHIFUJI Hideaki  */
647bc570c8SYOSHIFUJI Hideaki 
65a96f7a6aSEric Dumazet static DEFINE_SPINLOCK(mrt_lock);
667bc570c8SYOSHIFUJI Hideaki 
vif_dev_read(const struct vif_device * vif)67ebc31979SEric Dumazet static struct net_device *vif_dev_read(const struct vif_device *vif)
68ebc31979SEric Dumazet {
69a96f7a6aSEric Dumazet 	return rcu_dereference(vif->dev);
70ebc31979SEric Dumazet }
71ebc31979SEric Dumazet 
72b70432f7SYuval Mintz /* Multicast router control variables */
737bc570c8SYOSHIFUJI Hideaki 
747bc570c8SYOSHIFUJI Hideaki /* Special spinlock for queue of unresolved entries */
757bc570c8SYOSHIFUJI Hideaki static DEFINE_SPINLOCK(mfc_unres_lock);
767bc570c8SYOSHIFUJI Hideaki 
777bc570c8SYOSHIFUJI Hideaki /* We return to original Alan's scheme. Hash table of resolved
787bc570c8SYOSHIFUJI Hideaki    entries is changed only in process context and protected
797bc570c8SYOSHIFUJI Hideaki    with weak lock mrt_lock. Queue of unresolved entries is protected
807bc570c8SYOSHIFUJI Hideaki    with strong spinlock mfc_unres_lock.
817bc570c8SYOSHIFUJI Hideaki 
827bc570c8SYOSHIFUJI Hideaki    In this case data path is free of exclusive locks at all.
837bc570c8SYOSHIFUJI Hideaki  */
847bc570c8SYOSHIFUJI Hideaki 
857bc570c8SYOSHIFUJI Hideaki static struct kmem_cache *mrt_cachep __read_mostly;
867bc570c8SYOSHIFUJI Hideaki 
87b70432f7SYuval Mintz static struct mr_table *ip6mr_new_table(struct net *net, u32 id);
88b70432f7SYuval Mintz static void ip6mr_free_table(struct mr_table *mrt);
89d1db275dSPatrick McHardy 
90b70432f7SYuval Mintz static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
91e4a38c0cSPatrick Ruddy 			   struct net_device *dev, struct sk_buff *skb,
92e4a38c0cSPatrick Ruddy 			   struct mfc6_cache *cache);
933493a5b7SEric Dumazet static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,
948229efdaSBenjamin Thery 			      mifi_t mifi, int assert);
95b70432f7SYuval Mintz static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
96812e44ddSNicolas Dichtel 			      int cmd);
973493a5b7SEric Dumazet static void mrt6msg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt);
98d7c31cbdSDavid Lamparter static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
99d7c31cbdSDavid Lamparter 			      struct netlink_ext_ack *extack);
1005b285cacSPatrick McHardy static int ip6mr_rtm_dumproute(struct sk_buff *skb,
1015b285cacSPatrick McHardy 			       struct netlink_callback *cb);
102ca8d4794SCallum Sinclair static void mroute_clean_tables(struct mr_table *mrt, int flags);
103e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t);
1047bc570c8SYOSHIFUJI Hideaki 
105d1db275dSPatrick McHardy #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
1068ffb335eSEric Dumazet #define ip6mr_for_each_table(mrt, net) \
10728b380e2SAmol Grover 	list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list, \
108b6dd5acdSMadhuparna Bhowmik 				lockdep_rtnl_is_held() || \
109b6dd5acdSMadhuparna Bhowmik 				list_empty(&net->ipv6.mr6_tables))
110d1db275dSPatrick McHardy 
ip6mr_mr_table_iter(struct net * net,struct mr_table * mrt)1117b0db857SYuval Mintz static struct mr_table *ip6mr_mr_table_iter(struct net *net,
1127b0db857SYuval Mintz 					    struct mr_table *mrt)
1137b0db857SYuval Mintz {
1147b0db857SYuval Mintz 	struct mr_table *ret;
1157b0db857SYuval Mintz 
1167b0db857SYuval Mintz 	if (!mrt)
1177b0db857SYuval Mintz 		ret = list_entry_rcu(net->ipv6.mr6_tables.next,
1187b0db857SYuval Mintz 				     struct mr_table, list);
1197b0db857SYuval Mintz 	else
1207b0db857SYuval Mintz 		ret = list_entry_rcu(mrt->list.next,
1217b0db857SYuval Mintz 				     struct mr_table, list);
1227b0db857SYuval Mintz 
1237b0db857SYuval Mintz 	if (&ret->list == &net->ipv6.mr6_tables)
1247b0db857SYuval Mintz 		return NULL;
1257b0db857SYuval Mintz 	return ret;
1267b0db857SYuval Mintz }
1277b0db857SYuval Mintz 
__ip6mr_get_table(struct net * net,u32 id)128bba7909bSPaolo Abeni static struct mr_table *__ip6mr_get_table(struct net *net, u32 id)
129d1db275dSPatrick McHardy {
130b70432f7SYuval Mintz 	struct mr_table *mrt;
131d1db275dSPatrick McHardy 
132d1db275dSPatrick McHardy 	ip6mr_for_each_table(mrt, net) {
133d1db275dSPatrick McHardy 		if (mrt->id == id)
134d1db275dSPatrick McHardy 			return mrt;
135d1db275dSPatrick McHardy 	}
136d1db275dSPatrick McHardy 	return NULL;
137d1db275dSPatrick McHardy }
138d1db275dSPatrick McHardy 
ip6mr_get_table(struct net * net,u32 id)139bba7909bSPaolo Abeni static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
140bba7909bSPaolo Abeni {
141bba7909bSPaolo Abeni 	struct mr_table *mrt;
142bba7909bSPaolo Abeni 
143bba7909bSPaolo Abeni 	rcu_read_lock();
144bba7909bSPaolo Abeni 	mrt = __ip6mr_get_table(net, id);
145bba7909bSPaolo Abeni 	rcu_read_unlock();
146bba7909bSPaolo Abeni 	return mrt;
147bba7909bSPaolo Abeni }
148bba7909bSPaolo Abeni 
ip6mr_fib_lookup(struct net * net,struct flowi6 * flp6,struct mr_table ** mrt)1494c9483b2SDavid S. Miller static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
150b70432f7SYuval Mintz 			    struct mr_table **mrt)
151d1db275dSPatrick McHardy {
152d1db275dSPatrick McHardy 	int err;
15395f4a45dSHannes Frederic Sowa 	struct ip6mr_result res;
15495f4a45dSHannes Frederic Sowa 	struct fib_lookup_arg arg = {
15595f4a45dSHannes Frederic Sowa 		.result = &res,
15695f4a45dSHannes Frederic Sowa 		.flags = FIB_LOOKUP_NOREF,
15795f4a45dSHannes Frederic Sowa 	};
158d1db275dSPatrick McHardy 
159e4a38c0cSPatrick Ruddy 	/* update flow if oif or iif point to device enslaved to l3mdev */
160e4a38c0cSPatrick Ruddy 	l3mdev_update_flow(net, flowi6_to_flowi(flp6));
161e4a38c0cSPatrick Ruddy 
1624c9483b2SDavid S. Miller 	err = fib_rules_lookup(net->ipv6.mr6_rules_ops,
1634c9483b2SDavid S. Miller 			       flowi6_to_flowi(flp6), 0, &arg);
164d1db275dSPatrick McHardy 	if (err < 0)
165d1db275dSPatrick McHardy 		return err;
166d1db275dSPatrick McHardy 	*mrt = res.mrt;
167d1db275dSPatrick McHardy 	return 0;
168d1db275dSPatrick McHardy }
169d1db275dSPatrick McHardy 
ip6mr_rule_action(struct fib_rule * rule,struct flowi * flp,int flags,struct fib_lookup_arg * arg)170d1db275dSPatrick McHardy static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp,
171d1db275dSPatrick McHardy 			     int flags, struct fib_lookup_arg *arg)
172d1db275dSPatrick McHardy {
173d1db275dSPatrick McHardy 	struct ip6mr_result *res = arg->result;
174b70432f7SYuval Mintz 	struct mr_table *mrt;
175d1db275dSPatrick McHardy 
176d1db275dSPatrick McHardy 	switch (rule->action) {
177d1db275dSPatrick McHardy 	case FR_ACT_TO_TBL:
178d1db275dSPatrick McHardy 		break;
179d1db275dSPatrick McHardy 	case FR_ACT_UNREACHABLE:
180d1db275dSPatrick McHardy 		return -ENETUNREACH;
181d1db275dSPatrick McHardy 	case FR_ACT_PROHIBIT:
182d1db275dSPatrick McHardy 		return -EACCES;
183d1db275dSPatrick McHardy 	case FR_ACT_BLACKHOLE:
184d1db275dSPatrick McHardy 	default:
185d1db275dSPatrick McHardy 		return -EINVAL;
186d1db275dSPatrick McHardy 	}
187d1db275dSPatrick McHardy 
188e4a38c0cSPatrick Ruddy 	arg->table = fib_rule_get_table(rule, arg);
189e4a38c0cSPatrick Ruddy 
190bba7909bSPaolo Abeni 	mrt = __ip6mr_get_table(rule->fr_net, arg->table);
19163159f29SIan Morris 	if (!mrt)
192d1db275dSPatrick McHardy 		return -EAGAIN;
193d1db275dSPatrick McHardy 	res->mrt = mrt;
194d1db275dSPatrick McHardy 	return 0;
195d1db275dSPatrick McHardy }
196d1db275dSPatrick McHardy 
ip6mr_rule_match(struct fib_rule * rule,struct flowi * flp,int flags)197d1db275dSPatrick McHardy static int ip6mr_rule_match(struct fib_rule *rule, struct flowi *flp, int flags)
198d1db275dSPatrick McHardy {
199d1db275dSPatrick McHardy 	return 1;
200d1db275dSPatrick McHardy }
201d1db275dSPatrick McHardy 
ip6mr_rule_configure(struct fib_rule * rule,struct sk_buff * skb,struct fib_rule_hdr * frh,struct nlattr ** tb,struct netlink_ext_ack * extack)202d1db275dSPatrick McHardy static int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
203b16fb418SRoopa Prabhu 				struct fib_rule_hdr *frh, struct nlattr **tb,
204b16fb418SRoopa Prabhu 				struct netlink_ext_ack *extack)
205d1db275dSPatrick McHardy {
206d1db275dSPatrick McHardy 	return 0;
207d1db275dSPatrick McHardy }
208d1db275dSPatrick McHardy 
ip6mr_rule_compare(struct fib_rule * rule,struct fib_rule_hdr * frh,struct nlattr ** tb)209d1db275dSPatrick McHardy static int ip6mr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
210d1db275dSPatrick McHardy 			      struct nlattr **tb)
211d1db275dSPatrick McHardy {
212d1db275dSPatrick McHardy 	return 1;
213d1db275dSPatrick McHardy }
214d1db275dSPatrick McHardy 
ip6mr_rule_fill(struct fib_rule * rule,struct sk_buff * skb,struct fib_rule_hdr * frh)215d1db275dSPatrick McHardy static int ip6mr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
216d1db275dSPatrick McHardy 			   struct fib_rule_hdr *frh)
217d1db275dSPatrick McHardy {
218d1db275dSPatrick McHardy 	frh->dst_len = 0;
219d1db275dSPatrick McHardy 	frh->src_len = 0;
220d1db275dSPatrick McHardy 	frh->tos     = 0;
221d1db275dSPatrick McHardy 	return 0;
222d1db275dSPatrick McHardy }
223d1db275dSPatrick McHardy 
22404a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = {
225d1db275dSPatrick McHardy 	.family		= RTNL_FAMILY_IP6MR,
226d1db275dSPatrick McHardy 	.rule_size	= sizeof(struct ip6mr_rule),
227d1db275dSPatrick McHardy 	.addr_size	= sizeof(struct in6_addr),
228d1db275dSPatrick McHardy 	.action		= ip6mr_rule_action,
229d1db275dSPatrick McHardy 	.match		= ip6mr_rule_match,
230d1db275dSPatrick McHardy 	.configure	= ip6mr_rule_configure,
231d1db275dSPatrick McHardy 	.compare	= ip6mr_rule_compare,
232d1db275dSPatrick McHardy 	.fill		= ip6mr_rule_fill,
233d1db275dSPatrick McHardy 	.nlgroup	= RTNLGRP_IPV6_RULE,
234d1db275dSPatrick McHardy 	.owner		= THIS_MODULE,
235d1db275dSPatrick McHardy };
236d1db275dSPatrick McHardy 
ip6mr_rules_init(struct net * net)237d1db275dSPatrick McHardy static int __net_init ip6mr_rules_init(struct net *net)
238d1db275dSPatrick McHardy {
239d1db275dSPatrick McHardy 	struct fib_rules_ops *ops;
240b70432f7SYuval Mintz 	struct mr_table *mrt;
241d1db275dSPatrick McHardy 	int err;
242d1db275dSPatrick McHardy 
243d1db275dSPatrick McHardy 	ops = fib_rules_register(&ip6mr_rules_ops_template, net);
244d1db275dSPatrick McHardy 	if (IS_ERR(ops))
245d1db275dSPatrick McHardy 		return PTR_ERR(ops);
246d1db275dSPatrick McHardy 
247d1db275dSPatrick McHardy 	INIT_LIST_HEAD(&net->ipv6.mr6_tables);
248d1db275dSPatrick McHardy 
249d1db275dSPatrick McHardy 	mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
250e783bb00SSabrina Dubroca 	if (IS_ERR(mrt)) {
251e783bb00SSabrina Dubroca 		err = PTR_ERR(mrt);
252d1db275dSPatrick McHardy 		goto err1;
253d1db275dSPatrick McHardy 	}
254d1db275dSPatrick McHardy 
255d1db275dSPatrick McHardy 	err = fib_default_rule_add(ops, 0x7fff, RT6_TABLE_DFLT, 0);
256d1db275dSPatrick McHardy 	if (err < 0)
257d1db275dSPatrick McHardy 		goto err2;
258d1db275dSPatrick McHardy 
259d1db275dSPatrick McHardy 	net->ipv6.mr6_rules_ops = ops;
260d1db275dSPatrick McHardy 	return 0;
261d1db275dSPatrick McHardy 
262d1db275dSPatrick McHardy err2:
2635611a006SEric Dumazet 	rtnl_lock();
264f243e5a7SWANG Cong 	ip6mr_free_table(mrt);
2655611a006SEric Dumazet 	rtnl_unlock();
266d1db275dSPatrick McHardy err1:
267d1db275dSPatrick McHardy 	fib_rules_unregister(ops);
268d1db275dSPatrick McHardy 	return err;
269d1db275dSPatrick McHardy }
270d1db275dSPatrick McHardy 
ip6mr_rules_exit(struct net * net)271d1db275dSPatrick McHardy static void __net_exit ip6mr_rules_exit(struct net *net)
272d1db275dSPatrick McHardy {
273b70432f7SYuval Mintz 	struct mr_table *mrt, *next;
274d1db275dSPatrick McHardy 
275e2f736b7SEric Dumazet 	ASSERT_RTNL();
276035320d5SEric Dumazet 	list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) {
277035320d5SEric Dumazet 		list_del(&mrt->list);
278d1db275dSPatrick McHardy 		ip6mr_free_table(mrt);
279035320d5SEric Dumazet 	}
280d1db275dSPatrick McHardy 	fib_rules_unregister(net->ipv6.mr6_rules_ops);
281d1db275dSPatrick McHardy }
282088aa3eeSYuval Mintz 
ip6mr_rules_dump(struct net * net,struct notifier_block * nb,struct netlink_ext_ack * extack)283b7a59557SJiri Pirko static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
284b7a59557SJiri Pirko 			    struct netlink_ext_ack *extack)
285088aa3eeSYuval Mintz {
286b7a59557SJiri Pirko 	return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR, extack);
287088aa3eeSYuval Mintz }
288088aa3eeSYuval Mintz 
ip6mr_rules_seq_read(struct net * net)289088aa3eeSYuval Mintz static unsigned int ip6mr_rules_seq_read(struct net *net)
290088aa3eeSYuval Mintz {
291088aa3eeSYuval Mintz 	return fib_rules_seq_read(net, RTNL_FAMILY_IP6MR);
292088aa3eeSYuval Mintz }
293d3c07e5bSYuval Mintz 
ip6mr_rule_default(const struct fib_rule * rule)294d3c07e5bSYuval Mintz bool ip6mr_rule_default(const struct fib_rule *rule)
295d3c07e5bSYuval Mintz {
296d3c07e5bSYuval Mintz 	return fib_rule_matchall(rule) && rule->action == FR_ACT_TO_TBL &&
297d3c07e5bSYuval Mintz 	       rule->table == RT6_TABLE_DFLT && !rule->l3mdev;
298d3c07e5bSYuval Mintz }
299d3c07e5bSYuval Mintz EXPORT_SYMBOL(ip6mr_rule_default);
300d1db275dSPatrick McHardy #else
301d1db275dSPatrick McHardy #define ip6mr_for_each_table(mrt, net) \
302d1db275dSPatrick McHardy 	for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
303d1db275dSPatrick McHardy 
ip6mr_mr_table_iter(struct net * net,struct mr_table * mrt)3047b0db857SYuval Mintz static struct mr_table *ip6mr_mr_table_iter(struct net *net,
3057b0db857SYuval Mintz 					    struct mr_table *mrt)
3067b0db857SYuval Mintz {
3077b0db857SYuval Mintz 	if (!mrt)
3087b0db857SYuval Mintz 		return net->ipv6.mrt6;
3097b0db857SYuval Mintz 	return NULL;
3107b0db857SYuval Mintz }
3117b0db857SYuval Mintz 
ip6mr_get_table(struct net * net,u32 id)312b70432f7SYuval Mintz static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
313d1db275dSPatrick McHardy {
314d1db275dSPatrick McHardy 	return net->ipv6.mrt6;
315d1db275dSPatrick McHardy }
316d1db275dSPatrick McHardy 
317bba7909bSPaolo Abeni #define __ip6mr_get_table ip6mr_get_table
318bba7909bSPaolo Abeni 
ip6mr_fib_lookup(struct net * net,struct flowi6 * flp6,struct mr_table ** mrt)3194c9483b2SDavid S. Miller static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
320b70432f7SYuval Mintz 			    struct mr_table **mrt)
321d1db275dSPatrick McHardy {
322d1db275dSPatrick McHardy 	*mrt = net->ipv6.mrt6;
323d1db275dSPatrick McHardy 	return 0;
324d1db275dSPatrick McHardy }
325d1db275dSPatrick McHardy 
ip6mr_rules_init(struct net * net)326d1db275dSPatrick McHardy static int __net_init ip6mr_rules_init(struct net *net)
327d1db275dSPatrick McHardy {
328e783bb00SSabrina Dubroca 	struct mr_table *mrt;
329e783bb00SSabrina Dubroca 
330e783bb00SSabrina Dubroca 	mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
331e783bb00SSabrina Dubroca 	if (IS_ERR(mrt))
332e783bb00SSabrina Dubroca 		return PTR_ERR(mrt);
333e783bb00SSabrina Dubroca 	net->ipv6.mrt6 = mrt;
334e783bb00SSabrina Dubroca 	return 0;
335d1db275dSPatrick McHardy }
336d1db275dSPatrick McHardy 
ip6mr_rules_exit(struct net * net)337d1db275dSPatrick McHardy static void __net_exit ip6mr_rules_exit(struct net *net)
338d1db275dSPatrick McHardy {
339e2f736b7SEric Dumazet 	ASSERT_RTNL();
340d1db275dSPatrick McHardy 	ip6mr_free_table(net->ipv6.mrt6);
341905a6f96SHannes Frederic Sowa 	net->ipv6.mrt6 = NULL;
342d1db275dSPatrick McHardy }
343088aa3eeSYuval Mintz 
ip6mr_rules_dump(struct net * net,struct notifier_block * nb,struct netlink_ext_ack * extack)344b7a59557SJiri Pirko static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
345b7a59557SJiri Pirko 			    struct netlink_ext_ack *extack)
346088aa3eeSYuval Mintz {
347088aa3eeSYuval Mintz 	return 0;
348088aa3eeSYuval Mintz }
349088aa3eeSYuval Mintz 
ip6mr_rules_seq_read(struct net * net)350088aa3eeSYuval Mintz static unsigned int ip6mr_rules_seq_read(struct net *net)
351088aa3eeSYuval Mintz {
352088aa3eeSYuval Mintz 	return 0;
353088aa3eeSYuval Mintz }
354d1db275dSPatrick McHardy #endif
355d1db275dSPatrick McHardy 
ip6mr_hash_cmp(struct rhashtable_compare_arg * arg,const void * ptr)35687c418bfSYuval Mintz static int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg,
35787c418bfSYuval Mintz 			  const void *ptr)
35887c418bfSYuval Mintz {
35987c418bfSYuval Mintz 	const struct mfc6_cache_cmp_arg *cmparg = arg->key;
36087c418bfSYuval Mintz 	struct mfc6_cache *c = (struct mfc6_cache *)ptr;
36187c418bfSYuval Mintz 
36287c418bfSYuval Mintz 	return !ipv6_addr_equal(&c->mf6c_mcastgrp, &cmparg->mf6c_mcastgrp) ||
36387c418bfSYuval Mintz 	       !ipv6_addr_equal(&c->mf6c_origin, &cmparg->mf6c_origin);
36487c418bfSYuval Mintz }
36587c418bfSYuval Mintz 
36687c418bfSYuval Mintz static const struct rhashtable_params ip6mr_rht_params = {
367494fff56SYuval Mintz 	.head_offset = offsetof(struct mr_mfc, mnode),
36887c418bfSYuval Mintz 	.key_offset = offsetof(struct mfc6_cache, cmparg),
36987c418bfSYuval Mintz 	.key_len = sizeof(struct mfc6_cache_cmp_arg),
37087c418bfSYuval Mintz 	.nelem_hint = 3,
37187c418bfSYuval Mintz 	.obj_cmpfn = ip6mr_hash_cmp,
37287c418bfSYuval Mintz 	.automatic_shrinking = true,
37387c418bfSYuval Mintz };
37487c418bfSYuval Mintz 
ip6mr_new_table_set(struct mr_table * mrt,struct net * net)3750bbbf0e7SYuval Mintz static void ip6mr_new_table_set(struct mr_table *mrt,
3760bbbf0e7SYuval Mintz 				struct net *net)
3770bbbf0e7SYuval Mintz {
3780bbbf0e7SYuval Mintz #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
3790bbbf0e7SYuval Mintz 	list_add_tail_rcu(&mrt->list, &net->ipv6.mr6_tables);
3800bbbf0e7SYuval Mintz #endif
3810bbbf0e7SYuval Mintz }
3820bbbf0e7SYuval Mintz 
383845c9a7aSYuval Mintz static struct mfc6_cache_cmp_arg ip6mr_mr_table_ops_cmparg_any = {
384845c9a7aSYuval Mintz 	.mf6c_origin = IN6ADDR_ANY_INIT,
385845c9a7aSYuval Mintz 	.mf6c_mcastgrp = IN6ADDR_ANY_INIT,
386845c9a7aSYuval Mintz };
387845c9a7aSYuval Mintz 
388845c9a7aSYuval Mintz static struct mr_table_ops ip6mr_mr_table_ops = {
389845c9a7aSYuval Mintz 	.rht_params = &ip6mr_rht_params,
390845c9a7aSYuval Mintz 	.cmparg_any = &ip6mr_mr_table_ops_cmparg_any,
391845c9a7aSYuval Mintz };
392845c9a7aSYuval Mintz 
ip6mr_new_table(struct net * net,u32 id)393b70432f7SYuval Mintz static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
394d1db275dSPatrick McHardy {
395b70432f7SYuval Mintz 	struct mr_table *mrt;
396d1db275dSPatrick McHardy 
397bba7909bSPaolo Abeni 	mrt = __ip6mr_get_table(net, id);
39853b24b8fSIan Morris 	if (mrt)
399d1db275dSPatrick McHardy 		return mrt;
400d1db275dSPatrick McHardy 
401845c9a7aSYuval Mintz 	return mr_table_alloc(net, id, &ip6mr_mr_table_ops,
4020bbbf0e7SYuval Mintz 			      ipmr_expire_process, ip6mr_new_table_set);
403d1db275dSPatrick McHardy }
404d1db275dSPatrick McHardy 
ip6mr_free_table(struct mr_table * mrt)405b70432f7SYuval Mintz static void ip6mr_free_table(struct mr_table *mrt)
406d1db275dSPatrick McHardy {
407292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&mrt->ipmr_expire_timer);
408ca8d4794SCallum Sinclair 	mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
409ca8d4794SCallum Sinclair 				 MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC);
410b70432f7SYuval Mintz 	rhltable_destroy(&mrt->mfc_hash);
411d1db275dSPatrick McHardy 	kfree(mrt);
412d1db275dSPatrick McHardy }
4137bc570c8SYOSHIFUJI Hideaki 
4147bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_PROC_FS
415c8d61968SYuval Mintz /* The /proc interfaces to multicast routing
416c8d61968SYuval Mintz  * /proc/ip6_mr_cache /proc/ip6_mr_vif
4177bc570c8SYOSHIFUJI Hideaki  */
4187bc570c8SYOSHIFUJI Hideaki 
ip6mr_vif_seq_start(struct seq_file * seq,loff_t * pos)4197bc570c8SYOSHIFUJI Hideaki static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
420b96ef16dSEric Dumazet 	__acquires(RCU)
4217bc570c8SYOSHIFUJI Hideaki {
4223feda6b4SYuval Mintz 	struct mr_vif_iter *iter = seq->private;
4238b90fc7eSBenjamin Thery 	struct net *net = seq_file_net(seq);
424b70432f7SYuval Mintz 	struct mr_table *mrt;
425d1db275dSPatrick McHardy 
426bba7909bSPaolo Abeni 	rcu_read_lock();
427bba7909bSPaolo Abeni 	mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
428bba7909bSPaolo Abeni 	if (!mrt) {
429bba7909bSPaolo Abeni 		rcu_read_unlock();
430d1db275dSPatrick McHardy 		return ERR_PTR(-ENOENT);
431bba7909bSPaolo Abeni 	}
432d1db275dSPatrick McHardy 
433d1db275dSPatrick McHardy 	iter->mrt = mrt;
4348b90fc7eSBenjamin Thery 
4353feda6b4SYuval Mintz 	return mr_vif_seq_start(seq, pos);
4367bc570c8SYOSHIFUJI Hideaki }
4377bc570c8SYOSHIFUJI Hideaki 
ip6mr_vif_seq_stop(struct seq_file * seq,void * v)4387bc570c8SYOSHIFUJI Hideaki static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v)
439b96ef16dSEric Dumazet 	__releases(RCU)
4407bc570c8SYOSHIFUJI Hideaki {
441b96ef16dSEric Dumazet 	rcu_read_unlock();
4427bc570c8SYOSHIFUJI Hideaki }
4437bc570c8SYOSHIFUJI Hideaki 
ip6mr_vif_seq_show(struct seq_file * seq,void * v)4447bc570c8SYOSHIFUJI Hideaki static int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
4457bc570c8SYOSHIFUJI Hideaki {
4463feda6b4SYuval Mintz 	struct mr_vif_iter *iter = seq->private;
447b70432f7SYuval Mintz 	struct mr_table *mrt = iter->mrt;
4488b90fc7eSBenjamin Thery 
4497bc570c8SYOSHIFUJI Hideaki 	if (v == SEQ_START_TOKEN) {
4507bc570c8SYOSHIFUJI Hideaki 		seq_puts(seq,
4517bc570c8SYOSHIFUJI Hideaki 			 "Interface      BytesIn  PktsIn  BytesOut PktsOut Flags\n");
4527bc570c8SYOSHIFUJI Hideaki 	} else {
4536853f21fSYuval Mintz 		const struct vif_device *vif = v;
454ebc31979SEric Dumazet 		const struct net_device *vif_dev;
455ebc31979SEric Dumazet 		const char *name;
456ebc31979SEric Dumazet 
457ebc31979SEric Dumazet 		vif_dev = vif_dev_read(vif);
458ebc31979SEric Dumazet 		name = vif_dev ? vif_dev->name : "none";
4597bc570c8SYOSHIFUJI Hideaki 
4607bc570c8SYOSHIFUJI Hideaki 		seq_printf(seq,
461d430a227SAl Viro 			   "%2td %-10s %8ld %7ld  %8ld %7ld %05X\n",
462b70432f7SYuval Mintz 			   vif - mrt->vif_table,
4637bc570c8SYOSHIFUJI Hideaki 			   name, vif->bytes_in, vif->pkt_in,
4647bc570c8SYOSHIFUJI Hideaki 			   vif->bytes_out, vif->pkt_out,
4657bc570c8SYOSHIFUJI Hideaki 			   vif->flags);
4667bc570c8SYOSHIFUJI Hideaki 	}
4677bc570c8SYOSHIFUJI Hideaki 	return 0;
4687bc570c8SYOSHIFUJI Hideaki }
4697bc570c8SYOSHIFUJI Hideaki 
47098147d52SStephen Hemminger static const struct seq_operations ip6mr_vif_seq_ops = {
4717bc570c8SYOSHIFUJI Hideaki 	.start = ip6mr_vif_seq_start,
4723feda6b4SYuval Mintz 	.next  = mr_vif_seq_next,
4737bc570c8SYOSHIFUJI Hideaki 	.stop  = ip6mr_vif_seq_stop,
4747bc570c8SYOSHIFUJI Hideaki 	.show  = ip6mr_vif_seq_show,
4757bc570c8SYOSHIFUJI Hideaki };
4767bc570c8SYOSHIFUJI Hideaki 
ipmr_mfc_seq_start(struct seq_file * seq,loff_t * pos)4777bc570c8SYOSHIFUJI Hideaki static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
4787bc570c8SYOSHIFUJI Hideaki {
4798b90fc7eSBenjamin Thery 	struct net *net = seq_file_net(seq);
480b70432f7SYuval Mintz 	struct mr_table *mrt;
4818b90fc7eSBenjamin Thery 
482d1db275dSPatrick McHardy 	mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
48363159f29SIan Morris 	if (!mrt)
484d1db275dSPatrick McHardy 		return ERR_PTR(-ENOENT);
485d1db275dSPatrick McHardy 
486c8d61968SYuval Mintz 	return mr_mfc_seq_start(seq, pos, mrt, &mfc_unres_lock);
4877bc570c8SYOSHIFUJI Hideaki }
4887bc570c8SYOSHIFUJI Hideaki 
ipmr_mfc_seq_show(struct seq_file * seq,void * v)4897bc570c8SYOSHIFUJI Hideaki static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
4907bc570c8SYOSHIFUJI Hideaki {
4917bc570c8SYOSHIFUJI Hideaki 	int n;
4927bc570c8SYOSHIFUJI Hideaki 
4937bc570c8SYOSHIFUJI Hideaki 	if (v == SEQ_START_TOKEN) {
4947bc570c8SYOSHIFUJI Hideaki 		seq_puts(seq,
4957bc570c8SYOSHIFUJI Hideaki 			 "Group                            "
4967bc570c8SYOSHIFUJI Hideaki 			 "Origin                           "
4977bc570c8SYOSHIFUJI Hideaki 			 "Iif      Pkts  Bytes     Wrong  Oifs\n");
4987bc570c8SYOSHIFUJI Hideaki 	} else {
4997bc570c8SYOSHIFUJI Hideaki 		const struct mfc6_cache *mfc = v;
500c8d61968SYuval Mintz 		const struct mr_mfc_iter *it = seq->private;
501b70432f7SYuval Mintz 		struct mr_table *mrt = it->mrt;
5027bc570c8SYOSHIFUJI Hideaki 
503999890b2SBenjamin Thery 		seq_printf(seq, "%pI6 %pI6 %-3hd",
5040c6ce78aSHarvey Harrison 			   &mfc->mf6c_mcastgrp, &mfc->mf6c_origin,
505494fff56SYuval Mintz 			   mfc->_c.mfc_parent);
5061ea472e2SBenjamin Thery 
507b70432f7SYuval Mintz 		if (it->cache != &mrt->mfc_unres_queue) {
5081ea472e2SBenjamin Thery 			seq_printf(seq, " %8lu %8lu %8lu",
509*5960f4d8SEric Dumazet 				   atomic_long_read(&mfc->_c.mfc_un.res.pkt),
510*5960f4d8SEric Dumazet 				   atomic_long_read(&mfc->_c.mfc_un.res.bytes),
511*5960f4d8SEric Dumazet 				   atomic_long_read(&mfc->_c.mfc_un.res.wrong_if));
512494fff56SYuval Mintz 			for (n = mfc->_c.mfc_un.res.minvif;
513494fff56SYuval Mintz 			     n < mfc->_c.mfc_un.res.maxvif; n++) {
514b70432f7SYuval Mintz 				if (VIF_EXISTS(mrt, n) &&
515494fff56SYuval Mintz 				    mfc->_c.mfc_un.res.ttls[n] < 255)
5167bc570c8SYOSHIFUJI Hideaki 					seq_printf(seq,
517494fff56SYuval Mintz 						   " %2d:%-3d", n,
518494fff56SYuval Mintz 						   mfc->_c.mfc_un.res.ttls[n]);
5197bc570c8SYOSHIFUJI Hideaki 			}
5201ea472e2SBenjamin Thery 		} else {
5211ea472e2SBenjamin Thery 			/* unresolved mfc_caches don't contain
5221ea472e2SBenjamin Thery 			 * pkt, bytes and wrong_if values
5231ea472e2SBenjamin Thery 			 */
5241ea472e2SBenjamin Thery 			seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
5257bc570c8SYOSHIFUJI Hideaki 		}
5267bc570c8SYOSHIFUJI Hideaki 		seq_putc(seq, '\n');
5277bc570c8SYOSHIFUJI Hideaki 	}
5287bc570c8SYOSHIFUJI Hideaki 	return 0;
5297bc570c8SYOSHIFUJI Hideaki }
5307bc570c8SYOSHIFUJI Hideaki 
53188e9d34cSJames Morris static const struct seq_operations ipmr_mfc_seq_ops = {
5327bc570c8SYOSHIFUJI Hideaki 	.start = ipmr_mfc_seq_start,
533c8d61968SYuval Mintz 	.next  = mr_mfc_seq_next,
534c8d61968SYuval Mintz 	.stop  = mr_mfc_seq_stop,
5357bc570c8SYOSHIFUJI Hideaki 	.show  = ipmr_mfc_seq_show,
5367bc570c8SYOSHIFUJI Hideaki };
5377bc570c8SYOSHIFUJI Hideaki #endif
5387bc570c8SYOSHIFUJI Hideaki 
53914fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
54014fb64e1SYOSHIFUJI Hideaki 
pim6_rcv(struct sk_buff * skb)54114fb64e1SYOSHIFUJI Hideaki static int pim6_rcv(struct sk_buff *skb)
54214fb64e1SYOSHIFUJI Hideaki {
54314fb64e1SYOSHIFUJI Hideaki 	struct pimreghdr *pim;
54414fb64e1SYOSHIFUJI Hideaki 	struct ipv6hdr   *encap;
54514fb64e1SYOSHIFUJI Hideaki 	struct net_device  *reg_dev = NULL;
5468229efdaSBenjamin Thery 	struct net *net = dev_net(skb->dev);
547b70432f7SYuval Mintz 	struct mr_table *mrt;
5484c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
5494c9483b2SDavid S. Miller 		.flowi6_iif	= skb->dev->ifindex,
5504c9483b2SDavid S. Miller 		.flowi6_mark	= skb->mark,
551d1db275dSPatrick McHardy 	};
552d1db275dSPatrick McHardy 	int reg_vif_num;
55314fb64e1SYOSHIFUJI Hideaki 
55414fb64e1SYOSHIFUJI Hideaki 	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
55514fb64e1SYOSHIFUJI Hideaki 		goto drop;
55614fb64e1SYOSHIFUJI Hideaki 
55714fb64e1SYOSHIFUJI Hideaki 	pim = (struct pimreghdr *)skb_transport_header(skb);
55856245caeSNikolay Aleksandrov 	if (pim->type != ((PIM_VERSION << 4) | PIM_TYPE_REGISTER) ||
55914fb64e1SYOSHIFUJI Hideaki 	    (pim->flags & PIM_NULL_REGISTER) ||
5601d6e55f1SThomas Goff 	    (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
5611d6e55f1SThomas Goff 			     sizeof(*pim), IPPROTO_PIM,
5621d6e55f1SThomas Goff 			     csum_partial((void *)pim, sizeof(*pim), 0)) &&
563ec6b486fSAl Viro 	     csum_fold(skb_checksum(skb, 0, skb->len, 0))))
56414fb64e1SYOSHIFUJI Hideaki 		goto drop;
56514fb64e1SYOSHIFUJI Hideaki 
56614fb64e1SYOSHIFUJI Hideaki 	/* check if the inner packet is destined to mcast group */
56714fb64e1SYOSHIFUJI Hideaki 	encap = (struct ipv6hdr *)(skb_transport_header(skb) +
56814fb64e1SYOSHIFUJI Hideaki 				   sizeof(*pim));
56914fb64e1SYOSHIFUJI Hideaki 
57014fb64e1SYOSHIFUJI Hideaki 	if (!ipv6_addr_is_multicast(&encap->daddr) ||
57114fb64e1SYOSHIFUJI Hideaki 	    encap->payload_len == 0 ||
57214fb64e1SYOSHIFUJI Hideaki 	    ntohs(encap->payload_len) + sizeof(*pim) > skb->len)
57314fb64e1SYOSHIFUJI Hideaki 		goto drop;
57414fb64e1SYOSHIFUJI Hideaki 
5754c9483b2SDavid S. Miller 	if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
576d1db275dSPatrick McHardy 		goto drop;
577d1db275dSPatrick McHardy 
5786d086587SEric Dumazet 	/* Pairs with WRITE_ONCE() in mif6_add()/mif6_delete() */
5796d086587SEric Dumazet 	reg_vif_num = READ_ONCE(mrt->mroute_reg_vif_num);
58014fb64e1SYOSHIFUJI Hideaki 	if (reg_vif_num >= 0)
581ebc31979SEric Dumazet 		reg_dev = vif_dev_read(&mrt->vif_table[reg_vif_num]);
58214fb64e1SYOSHIFUJI Hideaki 
58363159f29SIan Morris 	if (!reg_dev)
58414fb64e1SYOSHIFUJI Hideaki 		goto drop;
58514fb64e1SYOSHIFUJI Hideaki 
58614fb64e1SYOSHIFUJI Hideaki 	skb->mac_header = skb->network_header;
58714fb64e1SYOSHIFUJI Hideaki 	skb_pull(skb, (u8 *)encap - skb->data);
58814fb64e1SYOSHIFUJI Hideaki 	skb_reset_network_header(skb);
5891d6e55f1SThomas Goff 	skb->protocol = htons(ETH_P_IPV6);
5903e49e6d5SCesar Eduardo Barros 	skb->ip_summed = CHECKSUM_NONE;
591d19d56ddSEric Dumazet 
592ea23192eSNicolas Dichtel 	skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
593d19d56ddSEric Dumazet 
594caf586e5SEric Dumazet 	netif_rx(skb);
5958990f468SEric Dumazet 
59614fb64e1SYOSHIFUJI Hideaki 	return 0;
59714fb64e1SYOSHIFUJI Hideaki  drop:
59814fb64e1SYOSHIFUJI Hideaki 	kfree_skb(skb);
59914fb64e1SYOSHIFUJI Hideaki 	return 0;
60014fb64e1SYOSHIFUJI Hideaki }
60114fb64e1SYOSHIFUJI Hideaki 
60241135cc8SAlexey Dobriyan static const struct inet6_protocol pim6_protocol = {
60314fb64e1SYOSHIFUJI Hideaki 	.handler	=	pim6_rcv,
60414fb64e1SYOSHIFUJI Hideaki };
60514fb64e1SYOSHIFUJI Hideaki 
60614fb64e1SYOSHIFUJI Hideaki /* Service routines creating virtual interfaces: PIMREG */
60714fb64e1SYOSHIFUJI Hideaki 
reg_vif_xmit(struct sk_buff * skb,struct net_device * dev)6086fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb,
6096fef4c0cSStephen Hemminger 				      struct net_device *dev)
61014fb64e1SYOSHIFUJI Hideaki {
6118229efdaSBenjamin Thery 	struct net *net = dev_net(dev);
612b70432f7SYuval Mintz 	struct mr_table *mrt;
6134c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
6144c9483b2SDavid S. Miller 		.flowi6_oif	= dev->ifindex,
6156a662719SCong Wang 		.flowi6_iif	= skb->skb_iif ? : LOOPBACK_IFINDEX,
6164c9483b2SDavid S. Miller 		.flowi6_mark	= skb->mark,
617d1db275dSPatrick McHardy 	};
618d1db275dSPatrick McHardy 
619cb9f1b78SWillem de Bruijn 	if (!pskb_inet_may_pull(skb))
620cb9f1b78SWillem de Bruijn 		goto tx_err;
621cb9f1b78SWillem de Bruijn 
622cb9f1b78SWillem de Bruijn 	if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
623cb9f1b78SWillem de Bruijn 		goto tx_err;
6248229efdaSBenjamin Thery 
6252fad1ba3SEric Dumazet 	DEV_STATS_ADD(dev, tx_bytes, skb->len);
6262fad1ba3SEric Dumazet 	DEV_STATS_INC(dev, tx_packets);
6273493a5b7SEric Dumazet 	rcu_read_lock();
6283493a5b7SEric Dumazet 	ip6mr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num),
6293493a5b7SEric Dumazet 			   MRT6MSG_WHOLEPKT);
6303493a5b7SEric Dumazet 	rcu_read_unlock();
63114fb64e1SYOSHIFUJI Hideaki 	kfree_skb(skb);
6326ed10654SPatrick McHardy 	return NETDEV_TX_OK;
633cb9f1b78SWillem de Bruijn 
634cb9f1b78SWillem de Bruijn tx_err:
6352fad1ba3SEric Dumazet 	DEV_STATS_INC(dev, tx_errors);
636cb9f1b78SWillem de Bruijn 	kfree_skb(skb);
637cb9f1b78SWillem de Bruijn 	return NETDEV_TX_OK;
63814fb64e1SYOSHIFUJI Hideaki }
63914fb64e1SYOSHIFUJI Hideaki 
reg_vif_get_iflink(const struct net_device * dev)640ee9b9596SNicolas Dichtel static int reg_vif_get_iflink(const struct net_device *dev)
641ee9b9596SNicolas Dichtel {
642ee9b9596SNicolas Dichtel 	return 0;
643ee9b9596SNicolas Dichtel }
644ee9b9596SNicolas Dichtel 
645007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = {
646007c3838SStephen Hemminger 	.ndo_start_xmit	= reg_vif_xmit,
647ee9b9596SNicolas Dichtel 	.ndo_get_iflink = reg_vif_get_iflink,
648007c3838SStephen Hemminger };
649007c3838SStephen Hemminger 
reg_vif_setup(struct net_device * dev)65014fb64e1SYOSHIFUJI Hideaki static void reg_vif_setup(struct net_device *dev)
65114fb64e1SYOSHIFUJI Hideaki {
65214fb64e1SYOSHIFUJI Hideaki 	dev->type		= ARPHRD_PIMREG;
65314fb64e1SYOSHIFUJI Hideaki 	dev->mtu		= 1500 - sizeof(struct ipv6hdr) - 8;
65414fb64e1SYOSHIFUJI Hideaki 	dev->flags		= IFF_NOARP;
655007c3838SStephen Hemminger 	dev->netdev_ops		= &reg_vif_netdev_ops;
656cf124db5SDavid S. Miller 	dev->needs_free_netdev	= true;
657403dbb97STom Goff 	dev->features		|= NETIF_F_NETNS_LOCAL;
65814fb64e1SYOSHIFUJI Hideaki }
65914fb64e1SYOSHIFUJI Hideaki 
ip6mr_reg_vif(struct net * net,struct mr_table * mrt)660b70432f7SYuval Mintz static struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
66114fb64e1SYOSHIFUJI Hideaki {
66214fb64e1SYOSHIFUJI Hideaki 	struct net_device *dev;
663d1db275dSPatrick McHardy 	char name[IFNAMSIZ];
66414fb64e1SYOSHIFUJI Hideaki 
665d1db275dSPatrick McHardy 	if (mrt->id == RT6_TABLE_DFLT)
666d1db275dSPatrick McHardy 		sprintf(name, "pim6reg");
667d1db275dSPatrick McHardy 	else
668d1db275dSPatrick McHardy 		sprintf(name, "pim6reg%u", mrt->id);
669d1db275dSPatrick McHardy 
670c835a677STom Gundersen 	dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup);
67163159f29SIan Morris 	if (!dev)
67214fb64e1SYOSHIFUJI Hideaki 		return NULL;
67314fb64e1SYOSHIFUJI Hideaki 
6748229efdaSBenjamin Thery 	dev_net_set(dev, net);
6758229efdaSBenjamin Thery 
67614fb64e1SYOSHIFUJI Hideaki 	if (register_netdevice(dev)) {
67714fb64e1SYOSHIFUJI Hideaki 		free_netdev(dev);
67814fb64e1SYOSHIFUJI Hideaki 		return NULL;
67914fb64e1SYOSHIFUJI Hideaki 	}
68014fb64e1SYOSHIFUJI Hideaki 
68100f54e68SPetr Machata 	if (dev_open(dev, NULL))
68214fb64e1SYOSHIFUJI Hideaki 		goto failure;
68314fb64e1SYOSHIFUJI Hideaki 
6847af3db78SWang Chen 	dev_hold(dev);
68514fb64e1SYOSHIFUJI Hideaki 	return dev;
68614fb64e1SYOSHIFUJI Hideaki 
68714fb64e1SYOSHIFUJI Hideaki failure:
68814fb64e1SYOSHIFUJI Hideaki 	unregister_netdevice(dev);
68914fb64e1SYOSHIFUJI Hideaki 	return NULL;
69014fb64e1SYOSHIFUJI Hideaki }
69114fb64e1SYOSHIFUJI Hideaki #endif
69214fb64e1SYOSHIFUJI Hideaki 
call_ip6mr_vif_entry_notifiers(struct net * net,enum fib_event_type event_type,struct vif_device * vif,struct net_device * vif_dev,mifi_t vif_index,u32 tb_id)693088aa3eeSYuval Mintz static int call_ip6mr_vif_entry_notifiers(struct net *net,
694088aa3eeSYuval Mintz 					  enum fib_event_type event_type,
695088aa3eeSYuval Mintz 					  struct vif_device *vif,
696ebc31979SEric Dumazet 					  struct net_device *vif_dev,
697088aa3eeSYuval Mintz 					  mifi_t vif_index, u32 tb_id)
698088aa3eeSYuval Mintz {
699088aa3eeSYuval Mintz 	return mr_call_vif_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
700ebc31979SEric Dumazet 				     vif, vif_dev, vif_index, tb_id,
701088aa3eeSYuval Mintz 				     &net->ipv6.ipmr_seq);
702088aa3eeSYuval Mintz }
7037bc570c8SYOSHIFUJI Hideaki 
call_ip6mr_mfc_entry_notifiers(struct net * net,enum fib_event_type event_type,struct mfc6_cache * mfc,u32 tb_id)704088aa3eeSYuval Mintz static int call_ip6mr_mfc_entry_notifiers(struct net *net,
705088aa3eeSYuval Mintz 					  enum fib_event_type event_type,
706088aa3eeSYuval Mintz 					  struct mfc6_cache *mfc, u32 tb_id)
707088aa3eeSYuval Mintz {
708088aa3eeSYuval Mintz 	return mr_call_mfc_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
709088aa3eeSYuval Mintz 				     &mfc->_c, tb_id, &net->ipv6.ipmr_seq);
710088aa3eeSYuval Mintz }
711088aa3eeSYuval Mintz 
712088aa3eeSYuval Mintz /* Delete a VIF entry */
mif6_delete(struct mr_table * mrt,int vifi,int notify,struct list_head * head)713b70432f7SYuval Mintz static int mif6_delete(struct mr_table *mrt, int vifi, int notify,
714723b929cSNikolay Aleksandrov 		       struct list_head *head)
7157bc570c8SYOSHIFUJI Hideaki {
7166853f21fSYuval Mintz 	struct vif_device *v;
7177bc570c8SYOSHIFUJI Hideaki 	struct net_device *dev;
7181d6e55f1SThomas Goff 	struct inet6_dev *in6_dev;
7196bd52143SPatrick McHardy 
7206bd52143SPatrick McHardy 	if (vifi < 0 || vifi >= mrt->maxvif)
7217bc570c8SYOSHIFUJI Hideaki 		return -EADDRNOTAVAIL;
7227bc570c8SYOSHIFUJI Hideaki 
723b70432f7SYuval Mintz 	v = &mrt->vif_table[vifi];
7247bc570c8SYOSHIFUJI Hideaki 
725ebc31979SEric Dumazet 	dev = rtnl_dereference(v->dev);
726ebc31979SEric Dumazet 	if (!dev)
7277bc570c8SYOSHIFUJI Hideaki 		return -EADDRNOTAVAIL;
728ebc31979SEric Dumazet 
729ebc31979SEric Dumazet 	call_ip6mr_vif_entry_notifiers(read_pnet(&mrt->net),
730ebc31979SEric Dumazet 				       FIB_EVENT_VIF_DEL, v, dev,
731ebc31979SEric Dumazet 				       vifi, mrt->id);
732a96f7a6aSEric Dumazet 	spin_lock(&mrt_lock);
733ebc31979SEric Dumazet 	RCU_INIT_POINTER(v->dev, NULL);
7347bc570c8SYOSHIFUJI Hideaki 
73514fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
7363493a5b7SEric Dumazet 	if (vifi == mrt->mroute_reg_vif_num) {
7373493a5b7SEric Dumazet 		/* Pairs with READ_ONCE() in ip6mr_cache_report() and reg_vif_xmit() */
7383493a5b7SEric Dumazet 		WRITE_ONCE(mrt->mroute_reg_vif_num, -1);
7393493a5b7SEric Dumazet 	}
74014fb64e1SYOSHIFUJI Hideaki #endif
74114fb64e1SYOSHIFUJI Hideaki 
7426bd52143SPatrick McHardy 	if (vifi + 1 == mrt->maxvif) {
7437bc570c8SYOSHIFUJI Hideaki 		int tmp;
7447bc570c8SYOSHIFUJI Hideaki 		for (tmp = vifi - 1; tmp >= 0; tmp--) {
745b70432f7SYuval Mintz 			if (VIF_EXISTS(mrt, tmp))
7467bc570c8SYOSHIFUJI Hideaki 				break;
7477bc570c8SYOSHIFUJI Hideaki 		}
748db9eb7c8SEric Dumazet 		WRITE_ONCE(mrt->maxvif, tmp + 1);
7497bc570c8SYOSHIFUJI Hideaki 	}
7507bc570c8SYOSHIFUJI Hideaki 
751a96f7a6aSEric Dumazet 	spin_unlock(&mrt_lock);
7527bc570c8SYOSHIFUJI Hideaki 
7537bc570c8SYOSHIFUJI Hideaki 	dev_set_allmulti(dev, -1);
7547bc570c8SYOSHIFUJI Hideaki 
7551d6e55f1SThomas Goff 	in6_dev = __in6_dev_get(dev);
756d67b8c61SNicolas Dichtel 	if (in6_dev) {
757145c7a79SEric Dumazet 		atomic_dec(&in6_dev->cnf.mc_forwarding);
75885b3daadSDavid Ahern 		inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
759d67b8c61SNicolas Dichtel 					     NETCONFA_MC_FORWARDING,
760d67b8c61SNicolas Dichtel 					     dev->ifindex, &in6_dev->cnf);
761d67b8c61SNicolas Dichtel 	}
7621d6e55f1SThomas Goff 
763723b929cSNikolay Aleksandrov 	if ((v->flags & MIFF_REGISTER) && !notify)
764c871e664SEric Dumazet 		unregister_netdevice_queue(dev, head);
7657bc570c8SYOSHIFUJI Hideaki 
766d62607c3SJakub Kicinski 	netdev_put(dev, &v->dev_tracker);
7677bc570c8SYOSHIFUJI Hideaki 	return 0;
7687bc570c8SYOSHIFUJI Hideaki }
7697bc570c8SYOSHIFUJI Hideaki 
ip6mr_cache_free_rcu(struct rcu_head * head)77087c418bfSYuval Mintz static inline void ip6mr_cache_free_rcu(struct rcu_head *head)
77187c418bfSYuval Mintz {
772494fff56SYuval Mintz 	struct mr_mfc *c = container_of(head, struct mr_mfc, rcu);
77387c418bfSYuval Mintz 
774494fff56SYuval Mintz 	kmem_cache_free(mrt_cachep, (struct mfc6_cache *)c);
77587c418bfSYuval Mintz }
77687c418bfSYuval Mintz 
ip6mr_cache_free(struct mfc6_cache * c)77758701ad4SBenjamin Thery static inline void ip6mr_cache_free(struct mfc6_cache *c)
77858701ad4SBenjamin Thery {
779494fff56SYuval Mintz 	call_rcu(&c->_c.rcu, ip6mr_cache_free_rcu);
78058701ad4SBenjamin Thery }
78158701ad4SBenjamin Thery 
7827bc570c8SYOSHIFUJI Hideaki /* Destroy an unresolved cache entry, killing queued skbs
7837bc570c8SYOSHIFUJI Hideaki    and reporting error to netlink readers.
7847bc570c8SYOSHIFUJI Hideaki  */
7857bc570c8SYOSHIFUJI Hideaki 
ip6mr_destroy_unres(struct mr_table * mrt,struct mfc6_cache * c)786b70432f7SYuval Mintz static void ip6mr_destroy_unres(struct mr_table *mrt, struct mfc6_cache *c)
7877bc570c8SYOSHIFUJI Hideaki {
7886bd52143SPatrick McHardy 	struct net *net = read_pnet(&mrt->net);
7897bc570c8SYOSHIFUJI Hideaki 	struct sk_buff *skb;
7907bc570c8SYOSHIFUJI Hideaki 
7916bd52143SPatrick McHardy 	atomic_dec(&mrt->cache_resolve_queue_len);
7927bc570c8SYOSHIFUJI Hideaki 
793494fff56SYuval Mintz 	while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved)) != NULL) {
7947bc570c8SYOSHIFUJI Hideaki 		if (ipv6_hdr(skb)->version == 0) {
795af72868bSJohannes Berg 			struct nlmsghdr *nlh = skb_pull(skb,
796af72868bSJohannes Berg 							sizeof(struct ipv6hdr));
7977bc570c8SYOSHIFUJI Hideaki 			nlh->nlmsg_type = NLMSG_ERROR;
798573ce260SHong zhi guo 			nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
7997bc570c8SYOSHIFUJI Hideaki 			skb_trim(skb, nlh->nlmsg_len);
800573ce260SHong zhi guo 			((struct nlmsgerr *)nlmsg_data(nlh))->error = -ETIMEDOUT;
80115e47304SEric W. Biederman 			rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
8027bc570c8SYOSHIFUJI Hideaki 		} else
8037bc570c8SYOSHIFUJI Hideaki 			kfree_skb(skb);
8047bc570c8SYOSHIFUJI Hideaki 	}
8057bc570c8SYOSHIFUJI Hideaki 
80658701ad4SBenjamin Thery 	ip6mr_cache_free(c);
8077bc570c8SYOSHIFUJI Hideaki }
8087bc570c8SYOSHIFUJI Hideaki 
8097bc570c8SYOSHIFUJI Hideaki 
810c476efbcSPatrick McHardy /* Timer process for all the unresolved queue. */
8117bc570c8SYOSHIFUJI Hideaki 
ipmr_do_expire_process(struct mr_table * mrt)812b70432f7SYuval Mintz static void ipmr_do_expire_process(struct mr_table *mrt)
8137bc570c8SYOSHIFUJI Hideaki {
8147bc570c8SYOSHIFUJI Hideaki 	unsigned long now = jiffies;
8157bc570c8SYOSHIFUJI Hideaki 	unsigned long expires = 10 * HZ;
816494fff56SYuval Mintz 	struct mr_mfc *c, *next;
8177bc570c8SYOSHIFUJI Hideaki 
818b70432f7SYuval Mintz 	list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
8197bc570c8SYOSHIFUJI Hideaki 		if (time_after(c->mfc_un.unres.expires, now)) {
8207bc570c8SYOSHIFUJI Hideaki 			/* not yet... */
8217bc570c8SYOSHIFUJI Hideaki 			unsigned long interval = c->mfc_un.unres.expires - now;
8227bc570c8SYOSHIFUJI Hideaki 			if (interval < expires)
8237bc570c8SYOSHIFUJI Hideaki 				expires = interval;
8247bc570c8SYOSHIFUJI Hideaki 			continue;
8257bc570c8SYOSHIFUJI Hideaki 		}
8267bc570c8SYOSHIFUJI Hideaki 
827f30a7784SPatrick McHardy 		list_del(&c->list);
828494fff56SYuval Mintz 		mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
829494fff56SYuval Mintz 		ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
8307bc570c8SYOSHIFUJI Hideaki 	}
8317bc570c8SYOSHIFUJI Hideaki 
832b70432f7SYuval Mintz 	if (!list_empty(&mrt->mfc_unres_queue))
8336bd52143SPatrick McHardy 		mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
8347bc570c8SYOSHIFUJI Hideaki }
8357bc570c8SYOSHIFUJI Hideaki 
ipmr_expire_process(struct timer_list * t)836e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t)
8377bc570c8SYOSHIFUJI Hideaki {
838b70432f7SYuval Mintz 	struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer);
839c476efbcSPatrick McHardy 
8407bc570c8SYOSHIFUJI Hideaki 	if (!spin_trylock(&mfc_unres_lock)) {
8416bd52143SPatrick McHardy 		mod_timer(&mrt->ipmr_expire_timer, jiffies + 1);
8427bc570c8SYOSHIFUJI Hideaki 		return;
8437bc570c8SYOSHIFUJI Hideaki 	}
8447bc570c8SYOSHIFUJI Hideaki 
845b70432f7SYuval Mintz 	if (!list_empty(&mrt->mfc_unres_queue))
8466bd52143SPatrick McHardy 		ipmr_do_expire_process(mrt);
8477bc570c8SYOSHIFUJI Hideaki 
8487bc570c8SYOSHIFUJI Hideaki 	spin_unlock(&mfc_unres_lock);
8497bc570c8SYOSHIFUJI Hideaki }
8507bc570c8SYOSHIFUJI Hideaki 
851a96f7a6aSEric Dumazet /* Fill oifs list. It is called under locked mrt_lock. */
8527bc570c8SYOSHIFUJI Hideaki 
ip6mr_update_thresholds(struct mr_table * mrt,struct mr_mfc * cache,unsigned char * ttls)853b70432f7SYuval Mintz static void ip6mr_update_thresholds(struct mr_table *mrt,
854494fff56SYuval Mintz 				    struct mr_mfc *cache,
855b5aa30b1SPatrick McHardy 				    unsigned char *ttls)
8567bc570c8SYOSHIFUJI Hideaki {
8577bc570c8SYOSHIFUJI Hideaki 	int vifi;
8587bc570c8SYOSHIFUJI Hideaki 
8596ac7eb08SRami Rosen 	cache->mfc_un.res.minvif = MAXMIFS;
8607bc570c8SYOSHIFUJI Hideaki 	cache->mfc_un.res.maxvif = 0;
8616ac7eb08SRami Rosen 	memset(cache->mfc_un.res.ttls, 255, MAXMIFS);
8627bc570c8SYOSHIFUJI Hideaki 
8636bd52143SPatrick McHardy 	for (vifi = 0; vifi < mrt->maxvif; vifi++) {
864b70432f7SYuval Mintz 		if (VIF_EXISTS(mrt, vifi) &&
8654e16880cSBenjamin Thery 		    ttls[vifi] && ttls[vifi] < 255) {
8667bc570c8SYOSHIFUJI Hideaki 			cache->mfc_un.res.ttls[vifi] = ttls[vifi];
8677bc570c8SYOSHIFUJI Hideaki 			if (cache->mfc_un.res.minvif > vifi)
8687bc570c8SYOSHIFUJI Hideaki 				cache->mfc_un.res.minvif = vifi;
8697bc570c8SYOSHIFUJI Hideaki 			if (cache->mfc_un.res.maxvif <= vifi)
8707bc570c8SYOSHIFUJI Hideaki 				cache->mfc_un.res.maxvif = vifi + 1;
8717bc570c8SYOSHIFUJI Hideaki 		}
8727bc570c8SYOSHIFUJI Hideaki 	}
873*5960f4d8SEric Dumazet 	WRITE_ONCE(cache->mfc_un.res.lastuse, jiffies);
8747bc570c8SYOSHIFUJI Hideaki }
8757bc570c8SYOSHIFUJI Hideaki 
mif6_add(struct net * net,struct mr_table * mrt,struct mif6ctl * vifc,int mrtsock)876b70432f7SYuval Mintz static int mif6_add(struct net *net, struct mr_table *mrt,
8776bd52143SPatrick McHardy 		    struct mif6ctl *vifc, int mrtsock)
8787bc570c8SYOSHIFUJI Hideaki {
8797bc570c8SYOSHIFUJI Hideaki 	int vifi = vifc->mif6c_mifi;
880b70432f7SYuval Mintz 	struct vif_device *v = &mrt->vif_table[vifi];
8817bc570c8SYOSHIFUJI Hideaki 	struct net_device *dev;
8821d6e55f1SThomas Goff 	struct inet6_dev *in6_dev;
8835ae7b444SWang Chen 	int err;
8847bc570c8SYOSHIFUJI Hideaki 
8857bc570c8SYOSHIFUJI Hideaki 	/* Is vif busy ? */
886b70432f7SYuval Mintz 	if (VIF_EXISTS(mrt, vifi))
8877bc570c8SYOSHIFUJI Hideaki 		return -EADDRINUSE;
8887bc570c8SYOSHIFUJI Hideaki 
8897bc570c8SYOSHIFUJI Hideaki 	switch (vifc->mif6c_flags) {
89014fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
89114fb64e1SYOSHIFUJI Hideaki 	case MIFF_REGISTER:
89214fb64e1SYOSHIFUJI Hideaki 		/*
89314fb64e1SYOSHIFUJI Hideaki 		 * Special Purpose VIF in PIM
89414fb64e1SYOSHIFUJI Hideaki 		 * All the packets will be sent to the daemon
89514fb64e1SYOSHIFUJI Hideaki 		 */
8966bd52143SPatrick McHardy 		if (mrt->mroute_reg_vif_num >= 0)
89714fb64e1SYOSHIFUJI Hideaki 			return -EADDRINUSE;
898d1db275dSPatrick McHardy 		dev = ip6mr_reg_vif(net, mrt);
89914fb64e1SYOSHIFUJI Hideaki 		if (!dev)
90014fb64e1SYOSHIFUJI Hideaki 			return -ENOBUFS;
9015ae7b444SWang Chen 		err = dev_set_allmulti(dev, 1);
9025ae7b444SWang Chen 		if (err) {
9035ae7b444SWang Chen 			unregister_netdevice(dev);
9047af3db78SWang Chen 			dev_put(dev);
9055ae7b444SWang Chen 			return err;
9065ae7b444SWang Chen 		}
90714fb64e1SYOSHIFUJI Hideaki 		break;
90814fb64e1SYOSHIFUJI Hideaki #endif
9097bc570c8SYOSHIFUJI Hideaki 	case 0:
9108229efdaSBenjamin Thery 		dev = dev_get_by_index(net, vifc->mif6c_pifi);
9117bc570c8SYOSHIFUJI Hideaki 		if (!dev)
9127bc570c8SYOSHIFUJI Hideaki 			return -EADDRNOTAVAIL;
9135ae7b444SWang Chen 		err = dev_set_allmulti(dev, 1);
9147af3db78SWang Chen 		if (err) {
9157af3db78SWang Chen 			dev_put(dev);
9165ae7b444SWang Chen 			return err;
9177af3db78SWang Chen 		}
9187bc570c8SYOSHIFUJI Hideaki 		break;
9197bc570c8SYOSHIFUJI Hideaki 	default:
9207bc570c8SYOSHIFUJI Hideaki 		return -EINVAL;
9217bc570c8SYOSHIFUJI Hideaki 	}
9227bc570c8SYOSHIFUJI Hideaki 
9231d6e55f1SThomas Goff 	in6_dev = __in6_dev_get(dev);
924d67b8c61SNicolas Dichtel 	if (in6_dev) {
925145c7a79SEric Dumazet 		atomic_inc(&in6_dev->cnf.mc_forwarding);
92685b3daadSDavid Ahern 		inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
927d67b8c61SNicolas Dichtel 					     NETCONFA_MC_FORWARDING,
928d67b8c61SNicolas Dichtel 					     dev->ifindex, &in6_dev->cnf);
929d67b8c61SNicolas Dichtel 	}
9301d6e55f1SThomas Goff 
9316853f21fSYuval Mintz 	/* Fill in the VIF structures */
9326853f21fSYuval Mintz 	vif_device_init(v, dev, vifc->vifc_rate_limit, vifc->vifc_threshold,
9336853f21fSYuval Mintz 			vifc->mif6c_flags | (!mrtsock ? VIFF_STATIC : 0),
9346853f21fSYuval Mintz 			MIFF_REGISTER);
9357bc570c8SYOSHIFUJI Hideaki 
9367bc570c8SYOSHIFUJI Hideaki 	/* And finish update writing critical data */
937a96f7a6aSEric Dumazet 	spin_lock(&mrt_lock);
938ebc31979SEric Dumazet 	rcu_assign_pointer(v->dev, dev);
93942120a86SEric Dumazet 	netdev_tracker_alloc(dev, &v->dev_tracker, GFP_ATOMIC);
94014fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
94114fb64e1SYOSHIFUJI Hideaki 	if (v->flags & MIFF_REGISTER)
9423493a5b7SEric Dumazet 		WRITE_ONCE(mrt->mroute_reg_vif_num, vifi);
94314fb64e1SYOSHIFUJI Hideaki #endif
9446bd52143SPatrick McHardy 	if (vifi + 1 > mrt->maxvif)
945db9eb7c8SEric Dumazet 		WRITE_ONCE(mrt->maxvif, vifi + 1);
946a96f7a6aSEric Dumazet 	spin_unlock(&mrt_lock);
947088aa3eeSYuval Mintz 	call_ip6mr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD,
948ebc31979SEric Dumazet 				       v, dev, vifi, mrt->id);
9497bc570c8SYOSHIFUJI Hideaki 	return 0;
9507bc570c8SYOSHIFUJI Hideaki }
9517bc570c8SYOSHIFUJI Hideaki 
ip6mr_cache_find(struct mr_table * mrt,const struct in6_addr * origin,const struct in6_addr * mcastgrp)952b70432f7SYuval Mintz static struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt,
953b71d1d42SEric Dumazet 					   const struct in6_addr *origin,
954b71d1d42SEric Dumazet 					   const struct in6_addr *mcastgrp)
9557bc570c8SYOSHIFUJI Hideaki {
95687c418bfSYuval Mintz 	struct mfc6_cache_cmp_arg arg = {
95787c418bfSYuval Mintz 		.mf6c_origin = *origin,
95887c418bfSYuval Mintz 		.mf6c_mcastgrp = *mcastgrp,
95987c418bfSYuval Mintz 	};
9607bc570c8SYOSHIFUJI Hideaki 
961845c9a7aSYuval Mintz 	return mr_mfc_find(mrt, &arg);
962660b26dcSNicolas Dichtel }
963660b26dcSNicolas Dichtel 
964660b26dcSNicolas Dichtel /* Look for a (*,G) entry */
ip6mr_cache_find_any(struct mr_table * mrt,struct in6_addr * mcastgrp,mifi_t mifi)965b70432f7SYuval Mintz static struct mfc6_cache *ip6mr_cache_find_any(struct mr_table *mrt,
966660b26dcSNicolas Dichtel 					       struct in6_addr *mcastgrp,
967660b26dcSNicolas Dichtel 					       mifi_t mifi)
968660b26dcSNicolas Dichtel {
96987c418bfSYuval Mintz 	struct mfc6_cache_cmp_arg arg = {
97087c418bfSYuval Mintz 		.mf6c_origin = in6addr_any,
97187c418bfSYuval Mintz 		.mf6c_mcastgrp = *mcastgrp,
97287c418bfSYuval Mintz 	};
973660b26dcSNicolas Dichtel 
974660b26dcSNicolas Dichtel 	if (ipv6_addr_any(mcastgrp))
975845c9a7aSYuval Mintz 		return mr_mfc_find_any_parent(mrt, mifi);
976845c9a7aSYuval Mintz 	return mr_mfc_find_any(mrt, mifi, &arg);
977660b26dcSNicolas Dichtel }
978660b26dcSNicolas Dichtel 
97987c418bfSYuval Mintz /* Look for a (S,G,iif) entry if parent != -1 */
98087c418bfSYuval Mintz static struct mfc6_cache *
ip6mr_cache_find_parent(struct mr_table * mrt,const struct in6_addr * origin,const struct in6_addr * mcastgrp,int parent)981b70432f7SYuval Mintz ip6mr_cache_find_parent(struct mr_table *mrt,
98287c418bfSYuval Mintz 			const struct in6_addr *origin,
98387c418bfSYuval Mintz 			const struct in6_addr *mcastgrp,
98487c418bfSYuval Mintz 			int parent)
98587c418bfSYuval Mintz {
98687c418bfSYuval Mintz 	struct mfc6_cache_cmp_arg arg = {
98787c418bfSYuval Mintz 		.mf6c_origin = *origin,
98887c418bfSYuval Mintz 		.mf6c_mcastgrp = *mcastgrp,
98987c418bfSYuval Mintz 	};
99087c418bfSYuval Mintz 
991845c9a7aSYuval Mintz 	return mr_mfc_find_parent(mrt, &arg, parent);
99287c418bfSYuval Mintz }
99387c418bfSYuval Mintz 
994845c9a7aSYuval Mintz /* Allocate a multicast cache entry */
ip6mr_cache_alloc(void)995b5aa30b1SPatrick McHardy static struct mfc6_cache *ip6mr_cache_alloc(void)
9967bc570c8SYOSHIFUJI Hideaki {
99736cbac59SJoe Perches 	struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
99863159f29SIan Morris 	if (!c)
9997bc570c8SYOSHIFUJI Hideaki 		return NULL;
1000494fff56SYuval Mintz 	c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
1001494fff56SYuval Mintz 	c->_c.mfc_un.res.minvif = MAXMIFS;
10028c13af2aSYuval Mintz 	c->_c.free = ip6mr_cache_free_rcu;
10038c13af2aSYuval Mintz 	refcount_set(&c->_c.mfc_un.res.refcount, 1);
10047bc570c8SYOSHIFUJI Hideaki 	return c;
10057bc570c8SYOSHIFUJI Hideaki }
10067bc570c8SYOSHIFUJI Hideaki 
ip6mr_cache_alloc_unres(void)1007b5aa30b1SPatrick McHardy static struct mfc6_cache *ip6mr_cache_alloc_unres(void)
10087bc570c8SYOSHIFUJI Hideaki {
100936cbac59SJoe Perches 	struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
101063159f29SIan Morris 	if (!c)
10117bc570c8SYOSHIFUJI Hideaki 		return NULL;
1012494fff56SYuval Mintz 	skb_queue_head_init(&c->_c.mfc_un.unres.unresolved);
1013494fff56SYuval Mintz 	c->_c.mfc_un.unres.expires = jiffies + 10 * HZ;
10147bc570c8SYOSHIFUJI Hideaki 	return c;
10157bc570c8SYOSHIFUJI Hideaki }
10167bc570c8SYOSHIFUJI Hideaki 
10177bc570c8SYOSHIFUJI Hideaki /*
10187bc570c8SYOSHIFUJI Hideaki  *	A cache entry has gone into a resolved state from queued
10197bc570c8SYOSHIFUJI Hideaki  */
10207bc570c8SYOSHIFUJI Hideaki 
ip6mr_cache_resolve(struct net * net,struct mr_table * mrt,struct mfc6_cache * uc,struct mfc6_cache * c)1021b70432f7SYuval Mintz static void ip6mr_cache_resolve(struct net *net, struct mr_table *mrt,
10226bd52143SPatrick McHardy 				struct mfc6_cache *uc, struct mfc6_cache *c)
10237bc570c8SYOSHIFUJI Hideaki {
10247bc570c8SYOSHIFUJI Hideaki 	struct sk_buff *skb;
10257bc570c8SYOSHIFUJI Hideaki 
10267bc570c8SYOSHIFUJI Hideaki 	/*
10277bc570c8SYOSHIFUJI Hideaki 	 *	Play the pending entries through our router
10287bc570c8SYOSHIFUJI Hideaki 	 */
10297bc570c8SYOSHIFUJI Hideaki 
1030494fff56SYuval Mintz 	while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) {
10317bc570c8SYOSHIFUJI Hideaki 		if (ipv6_hdr(skb)->version == 0) {
1032af72868bSJohannes Berg 			struct nlmsghdr *nlh = skb_pull(skb,
1033af72868bSJohannes Berg 							sizeof(struct ipv6hdr));
10347bc570c8SYOSHIFUJI Hideaki 
10357b0db857SYuval Mintz 			if (mr_fill_mroute(mrt, skb, &c->_c,
10367b0db857SYuval Mintz 					   nlmsg_data(nlh)) > 0) {
1037549e028dSYOSHIFUJI Hideaki 				nlh->nlmsg_len = skb_tail_pointer(skb) - (u8 *)nlh;
10387bc570c8SYOSHIFUJI Hideaki 			} else {
10397bc570c8SYOSHIFUJI Hideaki 				nlh->nlmsg_type = NLMSG_ERROR;
1040573ce260SHong zhi guo 				nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
10417bc570c8SYOSHIFUJI Hideaki 				skb_trim(skb, nlh->nlmsg_len);
1042573ce260SHong zhi guo 				((struct nlmsgerr *)nlmsg_data(nlh))->error = -EMSGSIZE;
10437bc570c8SYOSHIFUJI Hideaki 			}
104415e47304SEric W. Biederman 			rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
1045b07a9b26SIdo Schimmel 		} else {
1046b07a9b26SIdo Schimmel 			rcu_read_lock();
1047e4a38c0cSPatrick Ruddy 			ip6_mr_forward(net, mrt, skb->dev, skb, c);
1048b07a9b26SIdo Schimmel 			rcu_read_unlock();
1049b07a9b26SIdo Schimmel 		}
10507bc570c8SYOSHIFUJI Hideaki 	}
10517bc570c8SYOSHIFUJI Hideaki }
10527bc570c8SYOSHIFUJI Hideaki 
10537bc570c8SYOSHIFUJI Hideaki /*
1054dd12d15cSJulien Gomes  *	Bounce a cache query up to pim6sd and netlink.
10557bc570c8SYOSHIFUJI Hideaki  *
10563493a5b7SEric Dumazet  *	Called under rcu_read_lock()
10577bc570c8SYOSHIFUJI Hideaki  */
10587bc570c8SYOSHIFUJI Hideaki 
ip6mr_cache_report(const struct mr_table * mrt,struct sk_buff * pkt,mifi_t mifi,int assert)10593493a5b7SEric Dumazet static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,
10606bd52143SPatrick McHardy 			      mifi_t mifi, int assert)
10617bc570c8SYOSHIFUJI Hideaki {
10628571ab47SYuval Mintz 	struct sock *mroute6_sk;
10637bc570c8SYOSHIFUJI Hideaki 	struct sk_buff *skb;
10647bc570c8SYOSHIFUJI Hideaki 	struct mrt6msg *msg;
10657bc570c8SYOSHIFUJI Hideaki 	int ret;
10667bc570c8SYOSHIFUJI Hideaki 
106714fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
10684b340a5aSMobashshera Rasool 	if (assert == MRT6MSG_WHOLEPKT || assert == MRT6MSG_WRMIFWHOLE)
106914fb64e1SYOSHIFUJI Hideaki 		skb = skb_realloc_headroom(pkt, -skb_network_offset(pkt)
107014fb64e1SYOSHIFUJI Hideaki 						+sizeof(*msg));
107114fb64e1SYOSHIFUJI Hideaki 	else
107214fb64e1SYOSHIFUJI Hideaki #endif
10737bc570c8SYOSHIFUJI Hideaki 		skb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(*msg), GFP_ATOMIC);
10747bc570c8SYOSHIFUJI Hideaki 
10757bc570c8SYOSHIFUJI Hideaki 	if (!skb)
10767bc570c8SYOSHIFUJI Hideaki 		return -ENOBUFS;
10777bc570c8SYOSHIFUJI Hideaki 
10787bc570c8SYOSHIFUJI Hideaki 	/* I suppose that internal messages
10797bc570c8SYOSHIFUJI Hideaki 	 * do not require checksums */
10807bc570c8SYOSHIFUJI Hideaki 
10817bc570c8SYOSHIFUJI Hideaki 	skb->ip_summed = CHECKSUM_UNNECESSARY;
10827bc570c8SYOSHIFUJI Hideaki 
108314fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
10844b340a5aSMobashshera Rasool 	if (assert == MRT6MSG_WHOLEPKT || assert == MRT6MSG_WRMIFWHOLE) {
108514fb64e1SYOSHIFUJI Hideaki 		/* Ugly, but we have no choice with this interface.
108614fb64e1SYOSHIFUJI Hideaki 		   Duplicate old header, fix length etc.
108714fb64e1SYOSHIFUJI Hideaki 		   And all this only to mangle msg->im6_msgtype and
108814fb64e1SYOSHIFUJI Hideaki 		   to set msg->im6_mbz to "mbz" :-)
108914fb64e1SYOSHIFUJI Hideaki 		 */
109030e0191bSYue Haibing 		__skb_pull(skb, skb_network_offset(pkt));
109114fb64e1SYOSHIFUJI Hideaki 
109214fb64e1SYOSHIFUJI Hideaki 		skb_push(skb, sizeof(*msg));
109314fb64e1SYOSHIFUJI Hideaki 		skb_reset_transport_header(skb);
109414fb64e1SYOSHIFUJI Hideaki 		msg = (struct mrt6msg *)skb_transport_header(skb);
109514fb64e1SYOSHIFUJI Hideaki 		msg->im6_mbz = 0;
10964b340a5aSMobashshera Rasool 		msg->im6_msgtype = assert;
10974b340a5aSMobashshera Rasool 		if (assert == MRT6MSG_WRMIFWHOLE)
10984b340a5aSMobashshera Rasool 			msg->im6_mif = mifi;
10994b340a5aSMobashshera Rasool 		else
11003493a5b7SEric Dumazet 			msg->im6_mif = READ_ONCE(mrt->mroute_reg_vif_num);
110114fb64e1SYOSHIFUJI Hideaki 		msg->im6_pad = 0;
11024e3fd7a0SAlexey Dobriyan 		msg->im6_src = ipv6_hdr(pkt)->saddr;
11034e3fd7a0SAlexey Dobriyan 		msg->im6_dst = ipv6_hdr(pkt)->daddr;
110414fb64e1SYOSHIFUJI Hideaki 
110514fb64e1SYOSHIFUJI Hideaki 		skb->ip_summed = CHECKSUM_UNNECESSARY;
110614fb64e1SYOSHIFUJI Hideaki 	} else
110714fb64e1SYOSHIFUJI Hideaki #endif
110814fb64e1SYOSHIFUJI Hideaki 	{
11097bc570c8SYOSHIFUJI Hideaki 	/*
11107bc570c8SYOSHIFUJI Hideaki 	 *	Copy the IP header
11117bc570c8SYOSHIFUJI Hideaki 	 */
11127bc570c8SYOSHIFUJI Hideaki 
11137bc570c8SYOSHIFUJI Hideaki 	skb_put(skb, sizeof(struct ipv6hdr));
11147bc570c8SYOSHIFUJI Hideaki 	skb_reset_network_header(skb);
11157bc570c8SYOSHIFUJI Hideaki 	skb_copy_to_linear_data(skb, ipv6_hdr(pkt), sizeof(struct ipv6hdr));
11167bc570c8SYOSHIFUJI Hideaki 
11177bc570c8SYOSHIFUJI Hideaki 	/*
11187bc570c8SYOSHIFUJI Hideaki 	 *	Add our header
11197bc570c8SYOSHIFUJI Hideaki 	 */
11207bc570c8SYOSHIFUJI Hideaki 	skb_put(skb, sizeof(*msg));
11217bc570c8SYOSHIFUJI Hideaki 	skb_reset_transport_header(skb);
11227bc570c8SYOSHIFUJI Hideaki 	msg = (struct mrt6msg *)skb_transport_header(skb);
11237bc570c8SYOSHIFUJI Hideaki 
11247bc570c8SYOSHIFUJI Hideaki 	msg->im6_mbz = 0;
11257bc570c8SYOSHIFUJI Hideaki 	msg->im6_msgtype = assert;
11266ac7eb08SRami Rosen 	msg->im6_mif = mifi;
11277bc570c8SYOSHIFUJI Hideaki 	msg->im6_pad = 0;
11284e3fd7a0SAlexey Dobriyan 	msg->im6_src = ipv6_hdr(pkt)->saddr;
11294e3fd7a0SAlexey Dobriyan 	msg->im6_dst = ipv6_hdr(pkt)->daddr;
11307bc570c8SYOSHIFUJI Hideaki 
1131adf30907SEric Dumazet 	skb_dst_set(skb, dst_clone(skb_dst(pkt)));
11327bc570c8SYOSHIFUJI Hideaki 	skb->ip_summed = CHECKSUM_UNNECESSARY;
113314fb64e1SYOSHIFUJI Hideaki 	}
11347bc570c8SYOSHIFUJI Hideaki 
1135b70432f7SYuval Mintz 	mroute6_sk = rcu_dereference(mrt->mroute_sk);
11368571ab47SYuval Mintz 	if (!mroute6_sk) {
11377bc570c8SYOSHIFUJI Hideaki 		kfree_skb(skb);
11387bc570c8SYOSHIFUJI Hideaki 		return -EINVAL;
11397bc570c8SYOSHIFUJI Hideaki 	}
11407bc570c8SYOSHIFUJI Hideaki 
1141dd12d15cSJulien Gomes 	mrt6msg_netlink_event(mrt, skb);
1142dd12d15cSJulien Gomes 
11438571ab47SYuval Mintz 	/* Deliver to user space multicast routing algorithms */
11448571ab47SYuval Mintz 	ret = sock_queue_rcv_skb(mroute6_sk, skb);
11453493a5b7SEric Dumazet 
1146bd91b8bfSBenjamin Thery 	if (ret < 0) {
1147e87cc472SJoe Perches 		net_warn_ratelimited("mroute6: pending queue full, dropping entries\n");
11487bc570c8SYOSHIFUJI Hideaki 		kfree_skb(skb);
11497bc570c8SYOSHIFUJI Hideaki 	}
11507bc570c8SYOSHIFUJI Hideaki 
11517bc570c8SYOSHIFUJI Hideaki 	return ret;
11527bc570c8SYOSHIFUJI Hideaki }
11537bc570c8SYOSHIFUJI Hideaki 
1154494fff56SYuval Mintz /* Queue a packet for resolution. It gets locked cache entry! */
ip6mr_cache_unresolved(struct mr_table * mrt,mifi_t mifi,struct sk_buff * skb,struct net_device * dev)1155494fff56SYuval Mintz static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
1156e4a38c0cSPatrick Ruddy 				  struct sk_buff *skb, struct net_device *dev)
11577bc570c8SYOSHIFUJI Hideaki {
1158494fff56SYuval Mintz 	struct mfc6_cache *c;
1159f30a7784SPatrick McHardy 	bool found = false;
11607bc570c8SYOSHIFUJI Hideaki 	int err;
11617bc570c8SYOSHIFUJI Hideaki 
11627bc570c8SYOSHIFUJI Hideaki 	spin_lock_bh(&mfc_unres_lock);
1163494fff56SYuval Mintz 	list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) {
1164c476efbcSPatrick McHardy 		if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) &&
1165f30a7784SPatrick McHardy 		    ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) {
1166f30a7784SPatrick McHardy 			found = true;
11677bc570c8SYOSHIFUJI Hideaki 			break;
11687bc570c8SYOSHIFUJI Hideaki 		}
1169f30a7784SPatrick McHardy 	}
11707bc570c8SYOSHIFUJI Hideaki 
1171f30a7784SPatrick McHardy 	if (!found) {
11727bc570c8SYOSHIFUJI Hideaki 		/*
11737bc570c8SYOSHIFUJI Hideaki 		 *	Create a new entry if allowable
11747bc570c8SYOSHIFUJI Hideaki 		 */
11757bc570c8SYOSHIFUJI Hideaki 
11760079ad8eSHangbin Liu 		c = ip6mr_cache_alloc_unres();
11770079ad8eSHangbin Liu 		if (!c) {
11787bc570c8SYOSHIFUJI Hideaki 			spin_unlock_bh(&mfc_unres_lock);
11797bc570c8SYOSHIFUJI Hideaki 
11807bc570c8SYOSHIFUJI Hideaki 			kfree_skb(skb);
11817bc570c8SYOSHIFUJI Hideaki 			return -ENOBUFS;
11827bc570c8SYOSHIFUJI Hideaki 		}
11837bc570c8SYOSHIFUJI Hideaki 
1184494fff56SYuval Mintz 		/* Fill in the new cache entry */
1185494fff56SYuval Mintz 		c->_c.mfc_parent = -1;
11867bc570c8SYOSHIFUJI Hideaki 		c->mf6c_origin = ipv6_hdr(skb)->saddr;
11877bc570c8SYOSHIFUJI Hideaki 		c->mf6c_mcastgrp = ipv6_hdr(skb)->daddr;
11887bc570c8SYOSHIFUJI Hideaki 
11897bc570c8SYOSHIFUJI Hideaki 		/*
11907bc570c8SYOSHIFUJI Hideaki 		 *	Reflect first query at pim6sd
11917bc570c8SYOSHIFUJI Hideaki 		 */
11926bd52143SPatrick McHardy 		err = ip6mr_cache_report(mrt, skb, mifi, MRT6MSG_NOCACHE);
11938229efdaSBenjamin Thery 		if (err < 0) {
11947bc570c8SYOSHIFUJI Hideaki 			/* If the report failed throw the cache entry
11957bc570c8SYOSHIFUJI Hideaki 			   out - Brad Parker
11967bc570c8SYOSHIFUJI Hideaki 			 */
11977bc570c8SYOSHIFUJI Hideaki 			spin_unlock_bh(&mfc_unres_lock);
11987bc570c8SYOSHIFUJI Hideaki 
119958701ad4SBenjamin Thery 			ip6mr_cache_free(c);
12007bc570c8SYOSHIFUJI Hideaki 			kfree_skb(skb);
12017bc570c8SYOSHIFUJI Hideaki 			return err;
12027bc570c8SYOSHIFUJI Hideaki 		}
12037bc570c8SYOSHIFUJI Hideaki 
12046bd52143SPatrick McHardy 		atomic_inc(&mrt->cache_resolve_queue_len);
1205494fff56SYuval Mintz 		list_add(&c->_c.list, &mrt->mfc_unres_queue);
1206812e44ddSNicolas Dichtel 		mr6_netlink_event(mrt, c, RTM_NEWROUTE);
12077bc570c8SYOSHIFUJI Hideaki 
12086bd52143SPatrick McHardy 		ipmr_do_expire_process(mrt);
12097bc570c8SYOSHIFUJI Hideaki 	}
12107bc570c8SYOSHIFUJI Hideaki 
1211494fff56SYuval Mintz 	/* See if we can append the packet */
1212494fff56SYuval Mintz 	if (c->_c.mfc_un.unres.unresolved.qlen > 3) {
12137bc570c8SYOSHIFUJI Hideaki 		kfree_skb(skb);
12147bc570c8SYOSHIFUJI Hideaki 		err = -ENOBUFS;
12157bc570c8SYOSHIFUJI Hideaki 	} else {
1216e4a38c0cSPatrick Ruddy 		if (dev) {
1217e4a38c0cSPatrick Ruddy 			skb->dev = dev;
1218e4a38c0cSPatrick Ruddy 			skb->skb_iif = dev->ifindex;
1219e4a38c0cSPatrick Ruddy 		}
1220494fff56SYuval Mintz 		skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb);
12217bc570c8SYOSHIFUJI Hideaki 		err = 0;
12227bc570c8SYOSHIFUJI Hideaki 	}
12237bc570c8SYOSHIFUJI Hideaki 
12247bc570c8SYOSHIFUJI Hideaki 	spin_unlock_bh(&mfc_unres_lock);
12257bc570c8SYOSHIFUJI Hideaki 	return err;
12267bc570c8SYOSHIFUJI Hideaki }
12277bc570c8SYOSHIFUJI Hideaki 
12287bc570c8SYOSHIFUJI Hideaki /*
12297bc570c8SYOSHIFUJI Hideaki  *	MFC6 cache manipulation by user space
12307bc570c8SYOSHIFUJI Hideaki  */
12317bc570c8SYOSHIFUJI Hideaki 
ip6mr_mfc_delete(struct mr_table * mrt,struct mf6cctl * mfc,int parent)1232b70432f7SYuval Mintz static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
1233660b26dcSNicolas Dichtel 			    int parent)
12347bc570c8SYOSHIFUJI Hideaki {
123587c418bfSYuval Mintz 	struct mfc6_cache *c;
12367bc570c8SYOSHIFUJI Hideaki 
123787c418bfSYuval Mintz 	/* The entries are added/deleted only under RTNL */
123887c418bfSYuval Mintz 	rcu_read_lock();
123987c418bfSYuval Mintz 	c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
124087c418bfSYuval Mintz 				    &mfc->mf6cc_mcastgrp.sin6_addr, parent);
124187c418bfSYuval Mintz 	rcu_read_unlock();
124287c418bfSYuval Mintz 	if (!c)
124387c418bfSYuval Mintz 		return -ENOENT;
1244494fff56SYuval Mintz 	rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params);
1245494fff56SYuval Mintz 	list_del_rcu(&c->_c.list);
12467bc570c8SYOSHIFUJI Hideaki 
1247088aa3eeSYuval Mintz 	call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
1248088aa3eeSYuval Mintz 				       FIB_EVENT_ENTRY_DEL, c, mrt->id);
1249812e44ddSNicolas Dichtel 	mr6_netlink_event(mrt, c, RTM_DELROUTE);
12508c13af2aSYuval Mintz 	mr_cache_put(&c->_c);
12517bc570c8SYOSHIFUJI Hideaki 	return 0;
12527bc570c8SYOSHIFUJI Hideaki }
12537bc570c8SYOSHIFUJI Hideaki 
ip6mr_device_event(struct notifier_block * this,unsigned long event,void * ptr)12547bc570c8SYOSHIFUJI Hideaki static int ip6mr_device_event(struct notifier_block *this,
12557bc570c8SYOSHIFUJI Hideaki 			      unsigned long event, void *ptr)
12567bc570c8SYOSHIFUJI Hideaki {
1257351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
12588229efdaSBenjamin Thery 	struct net *net = dev_net(dev);
1259b70432f7SYuval Mintz 	struct mr_table *mrt;
12606853f21fSYuval Mintz 	struct vif_device *v;
12617bc570c8SYOSHIFUJI Hideaki 	int ct;
12627bc570c8SYOSHIFUJI Hideaki 
12637bc570c8SYOSHIFUJI Hideaki 	if (event != NETDEV_UNREGISTER)
12647bc570c8SYOSHIFUJI Hideaki 		return NOTIFY_DONE;
12657bc570c8SYOSHIFUJI Hideaki 
1266d1db275dSPatrick McHardy 	ip6mr_for_each_table(mrt, net) {
1267b70432f7SYuval Mintz 		v = &mrt->vif_table[0];
12686bd52143SPatrick McHardy 		for (ct = 0; ct < mrt->maxvif; ct++, v++) {
1269ebc31979SEric Dumazet 			if (rcu_access_pointer(v->dev) == dev)
1270723b929cSNikolay Aleksandrov 				mif6_delete(mrt, ct, 1, NULL);
12717bc570c8SYOSHIFUJI Hideaki 		}
1272d1db275dSPatrick McHardy 	}
1273c871e664SEric Dumazet 
12747bc570c8SYOSHIFUJI Hideaki 	return NOTIFY_DONE;
12757bc570c8SYOSHIFUJI Hideaki }
12767bc570c8SYOSHIFUJI Hideaki 
ip6mr_seq_read(struct net * net)1277088aa3eeSYuval Mintz static unsigned int ip6mr_seq_read(struct net *net)
1278088aa3eeSYuval Mintz {
1279088aa3eeSYuval Mintz 	ASSERT_RTNL();
1280088aa3eeSYuval Mintz 
1281088aa3eeSYuval Mintz 	return net->ipv6.ipmr_seq + ip6mr_rules_seq_read(net);
1282088aa3eeSYuval Mintz }
1283088aa3eeSYuval Mintz 
ip6mr_dump(struct net * net,struct notifier_block * nb,struct netlink_ext_ack * extack)1284b7a59557SJiri Pirko static int ip6mr_dump(struct net *net, struct notifier_block *nb,
1285b7a59557SJiri Pirko 		      struct netlink_ext_ack *extack)
1286088aa3eeSYuval Mintz {
1287088aa3eeSYuval Mintz 	return mr_dump(net, nb, RTNL_FAMILY_IP6MR, ip6mr_rules_dump,
1288194366b2SEric Dumazet 		       ip6mr_mr_table_iter, extack);
1289088aa3eeSYuval Mintz }
1290088aa3eeSYuval Mintz 
12917bc570c8SYOSHIFUJI Hideaki static struct notifier_block ip6_mr_notifier = {
12927bc570c8SYOSHIFUJI Hideaki 	.notifier_call = ip6mr_device_event
12937bc570c8SYOSHIFUJI Hideaki };
12947bc570c8SYOSHIFUJI Hideaki 
1295088aa3eeSYuval Mintz static const struct fib_notifier_ops ip6mr_notifier_ops_template = {
1296088aa3eeSYuval Mintz 	.family		= RTNL_FAMILY_IP6MR,
1297088aa3eeSYuval Mintz 	.fib_seq_read	= ip6mr_seq_read,
1298088aa3eeSYuval Mintz 	.fib_dump	= ip6mr_dump,
1299088aa3eeSYuval Mintz 	.owner		= THIS_MODULE,
1300088aa3eeSYuval Mintz };
13017bc570c8SYOSHIFUJI Hideaki 
ip6mr_notifier_init(struct net * net)1302088aa3eeSYuval Mintz static int __net_init ip6mr_notifier_init(struct net *net)
1303088aa3eeSYuval Mintz {
1304088aa3eeSYuval Mintz 	struct fib_notifier_ops *ops;
1305088aa3eeSYuval Mintz 
1306088aa3eeSYuval Mintz 	net->ipv6.ipmr_seq = 0;
1307088aa3eeSYuval Mintz 
1308088aa3eeSYuval Mintz 	ops = fib_notifier_ops_register(&ip6mr_notifier_ops_template, net);
1309088aa3eeSYuval Mintz 	if (IS_ERR(ops))
1310088aa3eeSYuval Mintz 		return PTR_ERR(ops);
1311088aa3eeSYuval Mintz 
1312088aa3eeSYuval Mintz 	net->ipv6.ip6mr_notifier_ops = ops;
1313088aa3eeSYuval Mintz 
1314088aa3eeSYuval Mintz 	return 0;
1315088aa3eeSYuval Mintz }
1316088aa3eeSYuval Mintz 
ip6mr_notifier_exit(struct net * net)1317088aa3eeSYuval Mintz static void __net_exit ip6mr_notifier_exit(struct net *net)
1318088aa3eeSYuval Mintz {
1319088aa3eeSYuval Mintz 	fib_notifier_ops_unregister(net->ipv6.ip6mr_notifier_ops);
1320088aa3eeSYuval Mintz 	net->ipv6.ip6mr_notifier_ops = NULL;
1321088aa3eeSYuval Mintz }
1322088aa3eeSYuval Mintz 
1323088aa3eeSYuval Mintz /* Setup for IP multicast routing */
ip6mr_net_init(struct net * net)13244e16880cSBenjamin Thery static int __net_init ip6mr_net_init(struct net *net)
13254e16880cSBenjamin Thery {
1326d1db275dSPatrick McHardy 	int err;
1327f30a7784SPatrick McHardy 
1328088aa3eeSYuval Mintz 	err = ip6mr_notifier_init(net);
1329088aa3eeSYuval Mintz 	if (err)
1330088aa3eeSYuval Mintz 		return err;
1331088aa3eeSYuval Mintz 
1332d1db275dSPatrick McHardy 	err = ip6mr_rules_init(net);
1333d1db275dSPatrick McHardy 	if (err < 0)
1334088aa3eeSYuval Mintz 		goto ip6mr_rules_fail;
13358b90fc7eSBenjamin Thery 
13368b90fc7eSBenjamin Thery #ifdef CONFIG_PROC_FS
13378b90fc7eSBenjamin Thery 	err = -ENOMEM;
1338c3506372SChristoph Hellwig 	if (!proc_create_net("ip6_mr_vif", 0, net->proc_net, &ip6mr_vif_seq_ops,
1339c3506372SChristoph Hellwig 			sizeof(struct mr_vif_iter)))
13408b90fc7eSBenjamin Thery 		goto proc_vif_fail;
1341c3506372SChristoph Hellwig 	if (!proc_create_net("ip6_mr_cache", 0, net->proc_net, &ipmr_mfc_seq_ops,
1342c3506372SChristoph Hellwig 			sizeof(struct mr_mfc_iter)))
13438b90fc7eSBenjamin Thery 		goto proc_cache_fail;
13448b90fc7eSBenjamin Thery #endif
13456bd52143SPatrick McHardy 
13464a6258a0SBenjamin Thery 	return 0;
13474a6258a0SBenjamin Thery 
13488b90fc7eSBenjamin Thery #ifdef CONFIG_PROC_FS
13498b90fc7eSBenjamin Thery proc_cache_fail:
1350ece31ffdSGao feng 	remove_proc_entry("ip6_mr_vif", net->proc_net);
13518b90fc7eSBenjamin Thery proc_vif_fail:
1352e2f736b7SEric Dumazet 	rtnl_lock();
1353d1db275dSPatrick McHardy 	ip6mr_rules_exit(net);
1354e2f736b7SEric Dumazet 	rtnl_unlock();
13558b90fc7eSBenjamin Thery #endif
1356088aa3eeSYuval Mintz ip6mr_rules_fail:
1357088aa3eeSYuval Mintz 	ip6mr_notifier_exit(net);
13584e16880cSBenjamin Thery 	return err;
13594e16880cSBenjamin Thery }
13604e16880cSBenjamin Thery 
ip6mr_net_exit(struct net * net)13614e16880cSBenjamin Thery static void __net_exit ip6mr_net_exit(struct net *net)
13624e16880cSBenjamin Thery {
13638b90fc7eSBenjamin Thery #ifdef CONFIG_PROC_FS
1364ece31ffdSGao feng 	remove_proc_entry("ip6_mr_cache", net->proc_net);
1365ece31ffdSGao feng 	remove_proc_entry("ip6_mr_vif", net->proc_net);
13668b90fc7eSBenjamin Thery #endif
1367088aa3eeSYuval Mintz 	ip6mr_notifier_exit(net);
13684e16880cSBenjamin Thery }
13694e16880cSBenjamin Thery 
ip6mr_net_exit_batch(struct list_head * net_list)1370e2f736b7SEric Dumazet static void __net_exit ip6mr_net_exit_batch(struct list_head *net_list)
1371e2f736b7SEric Dumazet {
1372e2f736b7SEric Dumazet 	struct net *net;
1373e2f736b7SEric Dumazet 
1374e2f736b7SEric Dumazet 	rtnl_lock();
1375e2f736b7SEric Dumazet 	list_for_each_entry(net, net_list, exit_list)
1376e2f736b7SEric Dumazet 		ip6mr_rules_exit(net);
1377e2f736b7SEric Dumazet 	rtnl_unlock();
1378e2f736b7SEric Dumazet }
1379e2f736b7SEric Dumazet 
13804e16880cSBenjamin Thery static struct pernet_operations ip6mr_net_ops = {
13814e16880cSBenjamin Thery 	.init = ip6mr_net_init,
13824e16880cSBenjamin Thery 	.exit = ip6mr_net_exit,
1383e2f736b7SEric Dumazet 	.exit_batch = ip6mr_net_exit_batch,
13844e16880cSBenjamin Thery };
13854e16880cSBenjamin Thery 
ip6_mr_init(void)1386623d1a1aSWang Chen int __init ip6_mr_init(void)
13877bc570c8SYOSHIFUJI Hideaki {
1388623d1a1aSWang Chen 	int err;
1389623d1a1aSWang Chen 
13907bc570c8SYOSHIFUJI Hideaki 	mrt_cachep = kmem_cache_create("ip6_mrt_cache",
13917bc570c8SYOSHIFUJI Hideaki 				       sizeof(struct mfc6_cache),
13927bc570c8SYOSHIFUJI Hideaki 				       0, SLAB_HWCACHE_ALIGN,
13937bc570c8SYOSHIFUJI Hideaki 				       NULL);
13947bc570c8SYOSHIFUJI Hideaki 	if (!mrt_cachep)
1395623d1a1aSWang Chen 		return -ENOMEM;
13967bc570c8SYOSHIFUJI Hideaki 
13974e16880cSBenjamin Thery 	err = register_pernet_subsys(&ip6mr_net_ops);
13984e16880cSBenjamin Thery 	if (err)
13994e16880cSBenjamin Thery 		goto reg_pernet_fail;
14004e16880cSBenjamin Thery 
1401623d1a1aSWang Chen 	err = register_netdevice_notifier(&ip6_mr_notifier);
1402623d1a1aSWang Chen 	if (err)
1403623d1a1aSWang Chen 		goto reg_notif_fail;
1404403dbb97STom Goff #ifdef CONFIG_IPV6_PIMSM_V2
1405403dbb97STom Goff 	if (inet6_add_protocol(&pim6_protocol, IPPROTO_PIM) < 0) {
1406f3213831SJoe Perches 		pr_err("%s: can't add PIM protocol\n", __func__);
1407403dbb97STom Goff 		err = -EAGAIN;
1408403dbb97STom Goff 		goto add_proto_fail;
1409403dbb97STom Goff 	}
1410403dbb97STom Goff #endif
1411a3fde2adSFlorian Westphal 	err = rtnl_register_module(THIS_MODULE, RTNL_FAMILY_IP6MR, RTM_GETROUTE,
1412d7c31cbdSDavid Lamparter 				   ip6mr_rtm_getroute, ip6mr_rtm_dumproute, 0);
1413a3fde2adSFlorian Westphal 	if (err == 0)
1414623d1a1aSWang Chen 		return 0;
1415a3fde2adSFlorian Westphal 
1416403dbb97STom Goff #ifdef CONFIG_IPV6_PIMSM_V2
1417a3fde2adSFlorian Westphal 	inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
1418403dbb97STom Goff add_proto_fail:
1419403dbb97STom Goff 	unregister_netdevice_notifier(&ip6_mr_notifier);
1420403dbb97STom Goff #endif
142187b30a65SBenjamin Thery reg_notif_fail:
14224e16880cSBenjamin Thery 	unregister_pernet_subsys(&ip6mr_net_ops);
14234e16880cSBenjamin Thery reg_pernet_fail:
142487b30a65SBenjamin Thery 	kmem_cache_destroy(mrt_cachep);
1425623d1a1aSWang Chen 	return err;
14267bc570c8SYOSHIFUJI Hideaki }
14277bc570c8SYOSHIFUJI Hideaki 
ip6_mr_cleanup(void)1428623d1a1aSWang Chen void ip6_mr_cleanup(void)
1429623d1a1aSWang Chen {
1430ffb1388aSDuan Jiong 	rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE);
1431ffb1388aSDuan Jiong #ifdef CONFIG_IPV6_PIMSM_V2
1432ffb1388aSDuan Jiong 	inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
1433ffb1388aSDuan Jiong #endif
1434623d1a1aSWang Chen 	unregister_netdevice_notifier(&ip6_mr_notifier);
14354e16880cSBenjamin Thery 	unregister_pernet_subsys(&ip6mr_net_ops);
1436623d1a1aSWang Chen 	kmem_cache_destroy(mrt_cachep);
1437623d1a1aSWang Chen }
14387bc570c8SYOSHIFUJI Hideaki 
ip6mr_mfc_add(struct net * net,struct mr_table * mrt,struct mf6cctl * mfc,int mrtsock,int parent)1439b70432f7SYuval Mintz static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
1440660b26dcSNicolas Dichtel 			 struct mf6cctl *mfc, int mrtsock, int parent)
14417bc570c8SYOSHIFUJI Hideaki {
14426ac7eb08SRami Rosen 	unsigned char ttls[MAXMIFS];
144387c418bfSYuval Mintz 	struct mfc6_cache *uc, *c;
1444494fff56SYuval Mintz 	struct mr_mfc *_uc;
144587c418bfSYuval Mintz 	bool found;
144687c418bfSYuval Mintz 	int i, err;
14477bc570c8SYOSHIFUJI Hideaki 
1448a50436f2SPatrick McHardy 	if (mfc->mf6cc_parent >= MAXMIFS)
1449a50436f2SPatrick McHardy 		return -ENFILE;
1450a50436f2SPatrick McHardy 
14516ac7eb08SRami Rosen 	memset(ttls, 255, MAXMIFS);
14526ac7eb08SRami Rosen 	for (i = 0; i < MAXMIFS; i++) {
14537bc570c8SYOSHIFUJI Hideaki 		if (IF_ISSET(i, &mfc->mf6cc_ifset))
14547bc570c8SYOSHIFUJI Hideaki 			ttls[i] = 1;
14557bc570c8SYOSHIFUJI Hideaki 	}
14567bc570c8SYOSHIFUJI Hideaki 
145787c418bfSYuval Mintz 	/* The entries are added/deleted only under RTNL */
145887c418bfSYuval Mintz 	rcu_read_lock();
145987c418bfSYuval Mintz 	c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
146087c418bfSYuval Mintz 				    &mfc->mf6cc_mcastgrp.sin6_addr, parent);
146187c418bfSYuval Mintz 	rcu_read_unlock();
146287c418bfSYuval Mintz 	if (c) {
1463a96f7a6aSEric Dumazet 		spin_lock(&mrt_lock);
1464494fff56SYuval Mintz 		c->_c.mfc_parent = mfc->mf6cc_parent;
1465494fff56SYuval Mintz 		ip6mr_update_thresholds(mrt, &c->_c, ttls);
14667bc570c8SYOSHIFUJI Hideaki 		if (!mrtsock)
1467494fff56SYuval Mintz 			c->_c.mfc_flags |= MFC_STATIC;
1468a96f7a6aSEric Dumazet 		spin_unlock(&mrt_lock);
1469088aa3eeSYuval Mintz 		call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE,
1470088aa3eeSYuval Mintz 					       c, mrt->id);
1471812e44ddSNicolas Dichtel 		mr6_netlink_event(mrt, c, RTM_NEWROUTE);
14727bc570c8SYOSHIFUJI Hideaki 		return 0;
14737bc570c8SYOSHIFUJI Hideaki 	}
14747bc570c8SYOSHIFUJI Hideaki 
1475660b26dcSNicolas Dichtel 	if (!ipv6_addr_any(&mfc->mf6cc_mcastgrp.sin6_addr) &&
1476660b26dcSNicolas Dichtel 	    !ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr))
14777bc570c8SYOSHIFUJI Hideaki 		return -EINVAL;
14787bc570c8SYOSHIFUJI Hideaki 
1479b5aa30b1SPatrick McHardy 	c = ip6mr_cache_alloc();
148063159f29SIan Morris 	if (!c)
14817bc570c8SYOSHIFUJI Hideaki 		return -ENOMEM;
14827bc570c8SYOSHIFUJI Hideaki 
14837bc570c8SYOSHIFUJI Hideaki 	c->mf6c_origin = mfc->mf6cc_origin.sin6_addr;
14847bc570c8SYOSHIFUJI Hideaki 	c->mf6c_mcastgrp = mfc->mf6cc_mcastgrp.sin6_addr;
1485494fff56SYuval Mintz 	c->_c.mfc_parent = mfc->mf6cc_parent;
1486494fff56SYuval Mintz 	ip6mr_update_thresholds(mrt, &c->_c, ttls);
14877bc570c8SYOSHIFUJI Hideaki 	if (!mrtsock)
1488494fff56SYuval Mintz 		c->_c.mfc_flags |= MFC_STATIC;
14897bc570c8SYOSHIFUJI Hideaki 
1490494fff56SYuval Mintz 	err = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode,
149187c418bfSYuval Mintz 				  ip6mr_rht_params);
149287c418bfSYuval Mintz 	if (err) {
149387c418bfSYuval Mintz 		pr_err("ip6mr: rhtable insert error %d\n", err);
149487c418bfSYuval Mintz 		ip6mr_cache_free(c);
149587c418bfSYuval Mintz 		return err;
149687c418bfSYuval Mintz 	}
1497494fff56SYuval Mintz 	list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list);
14987bc570c8SYOSHIFUJI Hideaki 
149987c418bfSYuval Mintz 	/* Check to see if we resolved a queued list. If so we
15007bc570c8SYOSHIFUJI Hideaki 	 * need to send on the frames and tidy up.
15017bc570c8SYOSHIFUJI Hideaki 	 */
1502f30a7784SPatrick McHardy 	found = false;
15037bc570c8SYOSHIFUJI Hideaki 	spin_lock_bh(&mfc_unres_lock);
1504494fff56SYuval Mintz 	list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) {
1505494fff56SYuval Mintz 		uc = (struct mfc6_cache *)_uc;
1506c476efbcSPatrick McHardy 		if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) &&
15077bc570c8SYOSHIFUJI Hideaki 		    ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) {
1508494fff56SYuval Mintz 			list_del(&_uc->list);
15096bd52143SPatrick McHardy 			atomic_dec(&mrt->cache_resolve_queue_len);
1510f30a7784SPatrick McHardy 			found = true;
15117bc570c8SYOSHIFUJI Hideaki 			break;
15127bc570c8SYOSHIFUJI Hideaki 		}
15137bc570c8SYOSHIFUJI Hideaki 	}
1514b70432f7SYuval Mintz 	if (list_empty(&mrt->mfc_unres_queue))
15156bd52143SPatrick McHardy 		del_timer(&mrt->ipmr_expire_timer);
15167bc570c8SYOSHIFUJI Hideaki 	spin_unlock_bh(&mfc_unres_lock);
15177bc570c8SYOSHIFUJI Hideaki 
1518f30a7784SPatrick McHardy 	if (found) {
15196bd52143SPatrick McHardy 		ip6mr_cache_resolve(net, mrt, uc, c);
152058701ad4SBenjamin Thery 		ip6mr_cache_free(uc);
15217bc570c8SYOSHIFUJI Hideaki 	}
1522088aa3eeSYuval Mintz 	call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD,
1523088aa3eeSYuval Mintz 				       c, mrt->id);
1524812e44ddSNicolas Dichtel 	mr6_netlink_event(mrt, c, RTM_NEWROUTE);
15257bc570c8SYOSHIFUJI Hideaki 	return 0;
15267bc570c8SYOSHIFUJI Hideaki }
15277bc570c8SYOSHIFUJI Hideaki 
15287bc570c8SYOSHIFUJI Hideaki /*
15297bc570c8SYOSHIFUJI Hideaki  *	Close the multicast socket, and clear the vif tables etc
15307bc570c8SYOSHIFUJI Hideaki  */
15317bc570c8SYOSHIFUJI Hideaki 
mroute_clean_tables(struct mr_table * mrt,int flags)1532ca8d4794SCallum Sinclair static void mroute_clean_tables(struct mr_table *mrt, int flags)
15337bc570c8SYOSHIFUJI Hideaki {
1534494fff56SYuval Mintz 	struct mr_mfc *c, *tmp;
1535c871e664SEric Dumazet 	LIST_HEAD(list);
153687c418bfSYuval Mintz 	int i;
15377bc570c8SYOSHIFUJI Hideaki 
153887c418bfSYuval Mintz 	/* Shut down all active vif entries */
1539ca8d4794SCallum Sinclair 	if (flags & (MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC)) {
15406bd52143SPatrick McHardy 		for (i = 0; i < mrt->maxvif; i++) {
1541ca8d4794SCallum Sinclair 			if (((mrt->vif_table[i].flags & VIFF_STATIC) &&
1542ca8d4794SCallum Sinclair 			     !(flags & MRT6_FLUSH_MIFS_STATIC)) ||
1543ca8d4794SCallum Sinclair 			    (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS)))
15444c698046SNikolay Aleksandrov 				continue;
1545723b929cSNikolay Aleksandrov 			mif6_delete(mrt, i, 0, &list);
15467bc570c8SYOSHIFUJI Hideaki 		}
1547c871e664SEric Dumazet 		unregister_netdevice_many(&list);
1548ca8d4794SCallum Sinclair 	}
15497bc570c8SYOSHIFUJI Hideaki 
155087c418bfSYuval Mintz 	/* Wipe the cache */
1551ca8d4794SCallum Sinclair 	if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) {
1552b70432f7SYuval Mintz 		list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
1553ca8d4794SCallum Sinclair 			if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) ||
1554ca8d4794SCallum Sinclair 			    (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC)))
15557bc570c8SYOSHIFUJI Hideaki 				continue;
1556b70432f7SYuval Mintz 			rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
155787c418bfSYuval Mintz 			list_del_rcu(&c->list);
1558146820ccSNir Dotan 			call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
1559146820ccSNir Dotan 						       FIB_EVENT_ENTRY_DEL,
1560146820ccSNir Dotan 						       (struct mfc6_cache *)c, mrt->id);
1561494fff56SYuval Mintz 			mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
15628c13af2aSYuval Mintz 			mr_cache_put(c);
15637bc570c8SYOSHIFUJI Hideaki 		}
1564ca8d4794SCallum Sinclair 	}
15657bc570c8SYOSHIFUJI Hideaki 
1566ca8d4794SCallum Sinclair 	if (flags & MRT6_FLUSH_MFC) {
15676bd52143SPatrick McHardy 		if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
15687bc570c8SYOSHIFUJI Hideaki 			spin_lock_bh(&mfc_unres_lock);
1569b70432f7SYuval Mintz 			list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
1570f30a7784SPatrick McHardy 				list_del(&c->list);
1571494fff56SYuval Mintz 				mr6_netlink_event(mrt, (struct mfc6_cache *)c,
1572494fff56SYuval Mintz 						  RTM_DELROUTE);
1573494fff56SYuval Mintz 				ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
15747bc570c8SYOSHIFUJI Hideaki 			}
15757bc570c8SYOSHIFUJI Hideaki 			spin_unlock_bh(&mfc_unres_lock);
15767bc570c8SYOSHIFUJI Hideaki 		}
15777bc570c8SYOSHIFUJI Hideaki 	}
1578ca8d4794SCallum Sinclair }
15797bc570c8SYOSHIFUJI Hideaki 
ip6mr_sk_init(struct mr_table * mrt,struct sock * sk)1580b70432f7SYuval Mintz static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk)
15817bc570c8SYOSHIFUJI Hideaki {
15827bc570c8SYOSHIFUJI Hideaki 	int err = 0;
15838229efdaSBenjamin Thery 	struct net *net = sock_net(sk);
15847bc570c8SYOSHIFUJI Hideaki 
15857bc570c8SYOSHIFUJI Hideaki 	rtnl_lock();
1586a96f7a6aSEric Dumazet 	spin_lock(&mrt_lock);
1587b70432f7SYuval Mintz 	if (rtnl_dereference(mrt->mroute_sk)) {
1588927265bcSEric Dumazet 		err = -EADDRINUSE;
15898571ab47SYuval Mintz 	} else {
1590b70432f7SYuval Mintz 		rcu_assign_pointer(mrt->mroute_sk, sk);
1591a366e300SEric Dumazet 		sock_set_flag(sk, SOCK_RCU_FREE);
1592145c7a79SEric Dumazet 		atomic_inc(&net->ipv6.devconf_all->mc_forwarding);
1593927265bcSEric Dumazet 	}
1594a96f7a6aSEric Dumazet 	spin_unlock(&mrt_lock);
1595927265bcSEric Dumazet 
1596927265bcSEric Dumazet 	if (!err)
159785b3daadSDavid Ahern 		inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
159885b3daadSDavid Ahern 					     NETCONFA_MC_FORWARDING,
1599d67b8c61SNicolas Dichtel 					     NETCONFA_IFINDEX_ALL,
1600d67b8c61SNicolas Dichtel 					     net->ipv6.devconf_all);
16017bc570c8SYOSHIFUJI Hideaki 	rtnl_unlock();
16027bc570c8SYOSHIFUJI Hideaki 
16037bc570c8SYOSHIFUJI Hideaki 	return err;
16047bc570c8SYOSHIFUJI Hideaki }
16057bc570c8SYOSHIFUJI Hideaki 
ip6mr_sk_done(struct sock * sk)16067bc570c8SYOSHIFUJI Hideaki int ip6mr_sk_done(struct sock *sk)
16077bc570c8SYOSHIFUJI Hideaki {
16088229efdaSBenjamin Thery 	struct net *net = sock_net(sk);
16097d9b1b57SEric Dumazet 	struct ipv6_devconf *devconf;
1610b70432f7SYuval Mintz 	struct mr_table *mrt;
16117d9b1b57SEric Dumazet 	int err = -EACCES;
16127bc570c8SYOSHIFUJI Hideaki 
1613338d182fSFrancesco Ruggeri 	if (sk->sk_type != SOCK_RAW ||
1614338d182fSFrancesco Ruggeri 	    inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
1615338d182fSFrancesco Ruggeri 		return err;
1616338d182fSFrancesco Ruggeri 
16177d9b1b57SEric Dumazet 	devconf = net->ipv6.devconf_all;
16187d9b1b57SEric Dumazet 	if (!devconf || !atomic_read(&devconf->mc_forwarding))
1619f2f2325eSEric Dumazet 		return err;
1620f2f2325eSEric Dumazet 
16217bc570c8SYOSHIFUJI Hideaki 	rtnl_lock();
1622d1db275dSPatrick McHardy 	ip6mr_for_each_table(mrt, net) {
1623b70432f7SYuval Mintz 		if (sk == rtnl_dereference(mrt->mroute_sk)) {
1624a96f7a6aSEric Dumazet 			spin_lock(&mrt_lock);
1625b70432f7SYuval Mintz 			RCU_INIT_POINTER(mrt->mroute_sk, NULL);
1626a366e300SEric Dumazet 			/* Note that mroute_sk had SOCK_RCU_FREE set,
1627a366e300SEric Dumazet 			 * so the RCU grace period before sk freeing
1628a366e300SEric Dumazet 			 * is guaranteed by sk_destruct()
1629a366e300SEric Dumazet 			 */
16307d9b1b57SEric Dumazet 			atomic_dec(&devconf->mc_forwarding);
1631a96f7a6aSEric Dumazet 			spin_unlock(&mrt_lock);
163285b3daadSDavid Ahern 			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
1633d67b8c61SNicolas Dichtel 						     NETCONFA_MC_FORWARDING,
1634d67b8c61SNicolas Dichtel 						     NETCONFA_IFINDEX_ALL,
1635d67b8c61SNicolas Dichtel 						     net->ipv6.devconf_all);
16367bc570c8SYOSHIFUJI Hideaki 
1637ca8d4794SCallum Sinclair 			mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC);
1638d1db275dSPatrick McHardy 			err = 0;
1639d1db275dSPatrick McHardy 			break;
1640d1db275dSPatrick McHardy 		}
1641d1db275dSPatrick McHardy 	}
16427bc570c8SYOSHIFUJI Hideaki 	rtnl_unlock();
16437bc570c8SYOSHIFUJI Hideaki 
16447bc570c8SYOSHIFUJI Hideaki 	return err;
16457bc570c8SYOSHIFUJI Hideaki }
16467bc570c8SYOSHIFUJI Hideaki 
mroute6_is_socket(struct net * net,struct sk_buff * skb)16478571ab47SYuval Mintz bool mroute6_is_socket(struct net *net, struct sk_buff *skb)
16486bd52143SPatrick McHardy {
1649b70432f7SYuval Mintz 	struct mr_table *mrt;
16504c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
1651e374c618SJulian Anastasov 		.flowi6_iif	= skb->skb_iif ? : LOOPBACK_IFINDEX,
16524c9483b2SDavid S. Miller 		.flowi6_oif	= skb->dev->ifindex,
16534c9483b2SDavid S. Miller 		.flowi6_mark	= skb->mark,
1654d1db275dSPatrick McHardy 	};
1655d1db275dSPatrick McHardy 
16564c9483b2SDavid S. Miller 	if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
1657d1db275dSPatrick McHardy 		return NULL;
16586bd52143SPatrick McHardy 
1659b70432f7SYuval Mintz 	return rcu_access_pointer(mrt->mroute_sk);
16606bd52143SPatrick McHardy }
16618571ab47SYuval Mintz EXPORT_SYMBOL(mroute6_is_socket);
16626bd52143SPatrick McHardy 
16637bc570c8SYOSHIFUJI Hideaki /*
16647bc570c8SYOSHIFUJI Hideaki  *	Socket options and virtual interface manipulation. The whole
16657bc570c8SYOSHIFUJI Hideaki  *	virtual interface system is a complete heap, but unfortunately
16667bc570c8SYOSHIFUJI Hideaki  *	that's how BSD mrouted happens to think. Maybe one day with a proper
16677bc570c8SYOSHIFUJI Hideaki  *	MOSPF/PIM router set up we can clean this up.
16687bc570c8SYOSHIFUJI Hideaki  */
16697bc570c8SYOSHIFUJI Hideaki 
ip6_mroute_setsockopt(struct sock * sk,int optname,sockptr_t optval,unsigned int optlen)1670b43c6153SChristoph Hellwig int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
1671b43c6153SChristoph Hellwig 			  unsigned int optlen)
16727bc570c8SYOSHIFUJI Hideaki {
1673660b26dcSNicolas Dichtel 	int ret, parent = 0;
16747bc570c8SYOSHIFUJI Hideaki 	struct mif6ctl vif;
16757bc570c8SYOSHIFUJI Hideaki 	struct mf6cctl mfc;
16767bc570c8SYOSHIFUJI Hideaki 	mifi_t mifi;
16778229efdaSBenjamin Thery 	struct net *net = sock_net(sk);
1678b70432f7SYuval Mintz 	struct mr_table *mrt;
1679d1db275dSPatrick McHardy 
168099253eb7SXin Long 	if (sk->sk_type != SOCK_RAW ||
168199253eb7SXin Long 	    inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
168299253eb7SXin Long 		return -EOPNOTSUPP;
168399253eb7SXin Long 
1684d1db275dSPatrick McHardy 	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
168563159f29SIan Morris 	if (!mrt)
1686d1db275dSPatrick McHardy 		return -ENOENT;
16877bc570c8SYOSHIFUJI Hideaki 
16887bc570c8SYOSHIFUJI Hideaki 	if (optname != MRT6_INIT) {
1689b70432f7SYuval Mintz 		if (sk != rcu_access_pointer(mrt->mroute_sk) &&
16908571ab47SYuval Mintz 		    !ns_capable(net->user_ns, CAP_NET_ADMIN))
16917bc570c8SYOSHIFUJI Hideaki 			return -EACCES;
16927bc570c8SYOSHIFUJI Hideaki 	}
16937bc570c8SYOSHIFUJI Hideaki 
16947bc570c8SYOSHIFUJI Hideaki 	switch (optname) {
16957bc570c8SYOSHIFUJI Hideaki 	case MRT6_INIT:
16967bc570c8SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
16977bc570c8SYOSHIFUJI Hideaki 			return -EINVAL;
16987bc570c8SYOSHIFUJI Hideaki 
16996bd52143SPatrick McHardy 		return ip6mr_sk_init(mrt, sk);
17007bc570c8SYOSHIFUJI Hideaki 
17017bc570c8SYOSHIFUJI Hideaki 	case MRT6_DONE:
17027bc570c8SYOSHIFUJI Hideaki 		return ip6mr_sk_done(sk);
17037bc570c8SYOSHIFUJI Hideaki 
17047bc570c8SYOSHIFUJI Hideaki 	case MRT6_ADD_MIF:
17057bc570c8SYOSHIFUJI Hideaki 		if (optlen < sizeof(vif))
17067bc570c8SYOSHIFUJI Hideaki 			return -EINVAL;
1707b43c6153SChristoph Hellwig 		if (copy_from_sockptr(&vif, optval, sizeof(vif)))
17087bc570c8SYOSHIFUJI Hideaki 			return -EFAULT;
17096ac7eb08SRami Rosen 		if (vif.mif6c_mifi >= MAXMIFS)
17107bc570c8SYOSHIFUJI Hideaki 			return -ENFILE;
17117bc570c8SYOSHIFUJI Hideaki 		rtnl_lock();
17128571ab47SYuval Mintz 		ret = mif6_add(net, mrt, &vif,
1713b70432f7SYuval Mintz 			       sk == rtnl_dereference(mrt->mroute_sk));
17147bc570c8SYOSHIFUJI Hideaki 		rtnl_unlock();
17157bc570c8SYOSHIFUJI Hideaki 		return ret;
17167bc570c8SYOSHIFUJI Hideaki 
17177bc570c8SYOSHIFUJI Hideaki 	case MRT6_DEL_MIF:
17187bc570c8SYOSHIFUJI Hideaki 		if (optlen < sizeof(mifi_t))
17197bc570c8SYOSHIFUJI Hideaki 			return -EINVAL;
1720b43c6153SChristoph Hellwig 		if (copy_from_sockptr(&mifi, optval, sizeof(mifi_t)))
17217bc570c8SYOSHIFUJI Hideaki 			return -EFAULT;
17227bc570c8SYOSHIFUJI Hideaki 		rtnl_lock();
1723723b929cSNikolay Aleksandrov 		ret = mif6_delete(mrt, mifi, 0, NULL);
17247bc570c8SYOSHIFUJI Hideaki 		rtnl_unlock();
17257bc570c8SYOSHIFUJI Hideaki 		return ret;
17267bc570c8SYOSHIFUJI Hideaki 
17277bc570c8SYOSHIFUJI Hideaki 	/*
17287bc570c8SYOSHIFUJI Hideaki 	 *	Manipulate the forwarding caches. These live
17297bc570c8SYOSHIFUJI Hideaki 	 *	in a sort of kernel/user symbiosis.
17307bc570c8SYOSHIFUJI Hideaki 	 */
17317bc570c8SYOSHIFUJI Hideaki 	case MRT6_ADD_MFC:
17327bc570c8SYOSHIFUJI Hideaki 	case MRT6_DEL_MFC:
1733660b26dcSNicolas Dichtel 		parent = -1;
1734a8eceea8SJoe Perches 		fallthrough;
1735660b26dcSNicolas Dichtel 	case MRT6_ADD_MFC_PROXY:
1736660b26dcSNicolas Dichtel 	case MRT6_DEL_MFC_PROXY:
17377bc570c8SYOSHIFUJI Hideaki 		if (optlen < sizeof(mfc))
17387bc570c8SYOSHIFUJI Hideaki 			return -EINVAL;
1739b43c6153SChristoph Hellwig 		if (copy_from_sockptr(&mfc, optval, sizeof(mfc)))
17407bc570c8SYOSHIFUJI Hideaki 			return -EFAULT;
1741660b26dcSNicolas Dichtel 		if (parent == 0)
1742660b26dcSNicolas Dichtel 			parent = mfc.mf6cc_parent;
17437bc570c8SYOSHIFUJI Hideaki 		rtnl_lock();
1744660b26dcSNicolas Dichtel 		if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
1745660b26dcSNicolas Dichtel 			ret = ip6mr_mfc_delete(mrt, &mfc, parent);
17467bc570c8SYOSHIFUJI Hideaki 		else
1747660b26dcSNicolas Dichtel 			ret = ip6mr_mfc_add(net, mrt, &mfc,
17488571ab47SYuval Mintz 					    sk ==
1749b70432f7SYuval Mintz 					    rtnl_dereference(mrt->mroute_sk),
17508571ab47SYuval Mintz 					    parent);
17517bc570c8SYOSHIFUJI Hideaki 		rtnl_unlock();
17527bc570c8SYOSHIFUJI Hideaki 		return ret;
17537bc570c8SYOSHIFUJI Hideaki 
1754ca8d4794SCallum Sinclair 	case MRT6_FLUSH:
1755ca8d4794SCallum Sinclair 	{
1756ca8d4794SCallum Sinclair 		int flags;
1757ca8d4794SCallum Sinclair 
1758ca8d4794SCallum Sinclair 		if (optlen != sizeof(flags))
1759ca8d4794SCallum Sinclair 			return -EINVAL;
1760b43c6153SChristoph Hellwig 		if (copy_from_sockptr(&flags, optval, sizeof(flags)))
1761ca8d4794SCallum Sinclair 			return -EFAULT;
1762ca8d4794SCallum Sinclair 		rtnl_lock();
1763ca8d4794SCallum Sinclair 		mroute_clean_tables(mrt, flags);
1764ca8d4794SCallum Sinclair 		rtnl_unlock();
1765ca8d4794SCallum Sinclair 		return 0;
1766ca8d4794SCallum Sinclair 	}
1767ca8d4794SCallum Sinclair 
17687bc570c8SYOSHIFUJI Hideaki 	/*
176914fb64e1SYOSHIFUJI Hideaki 	 *	Control PIM assert (to activate pim will activate assert)
177014fb64e1SYOSHIFUJI Hideaki 	 */
177114fb64e1SYOSHIFUJI Hideaki 	case MRT6_ASSERT:
177214fb64e1SYOSHIFUJI Hideaki 	{
177314fb64e1SYOSHIFUJI Hideaki 		int v;
177403f52a0aSJoe Perches 
177503f52a0aSJoe Perches 		if (optlen != sizeof(v))
177603f52a0aSJoe Perches 			return -EINVAL;
1777b43c6153SChristoph Hellwig 		if (copy_from_sockptr(&v, optval, sizeof(v)))
177814fb64e1SYOSHIFUJI Hideaki 			return -EFAULT;
177953d6841dSJoe Perches 		mrt->mroute_do_assert = v;
178014fb64e1SYOSHIFUJI Hideaki 		return 0;
178114fb64e1SYOSHIFUJI Hideaki 	}
178214fb64e1SYOSHIFUJI Hideaki 
178314fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
178414fb64e1SYOSHIFUJI Hideaki 	case MRT6_PIM:
178514fb64e1SYOSHIFUJI Hideaki 	{
1786a3ebe92aSFlorian Westphal 		bool do_wrmifwhole;
1787a9f83bf3SYOSHIFUJI Hideaki 		int v;
178803f52a0aSJoe Perches 
178903f52a0aSJoe Perches 		if (optlen != sizeof(v))
179003f52a0aSJoe Perches 			return -EINVAL;
1791b43c6153SChristoph Hellwig 		if (copy_from_sockptr(&v, optval, sizeof(v)))
179214fb64e1SYOSHIFUJI Hideaki 			return -EFAULT;
17934b340a5aSMobashshera Rasool 
17944b340a5aSMobashshera Rasool 		do_wrmifwhole = (v == MRT6MSG_WRMIFWHOLE);
179514fb64e1SYOSHIFUJI Hideaki 		v = !!v;
179614fb64e1SYOSHIFUJI Hideaki 		rtnl_lock();
179714fb64e1SYOSHIFUJI Hideaki 		ret = 0;
17986bd52143SPatrick McHardy 		if (v != mrt->mroute_do_pim) {
17996bd52143SPatrick McHardy 			mrt->mroute_do_pim = v;
18006bd52143SPatrick McHardy 			mrt->mroute_do_assert = v;
18014b340a5aSMobashshera Rasool 			mrt->mroute_do_wrvifwhole = do_wrmifwhole;
180214fb64e1SYOSHIFUJI Hideaki 		}
180314fb64e1SYOSHIFUJI Hideaki 		rtnl_unlock();
180414fb64e1SYOSHIFUJI Hideaki 		return ret;
180514fb64e1SYOSHIFUJI Hideaki 	}
180614fb64e1SYOSHIFUJI Hideaki 
180714fb64e1SYOSHIFUJI Hideaki #endif
1808d1db275dSPatrick McHardy #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
1809d1db275dSPatrick McHardy 	case MRT6_TABLE:
1810d1db275dSPatrick McHardy 	{
1811d1db275dSPatrick McHardy 		u32 v;
1812d1db275dSPatrick McHardy 
1813d1db275dSPatrick McHardy 		if (optlen != sizeof(u32))
1814d1db275dSPatrick McHardy 			return -EINVAL;
1815b43c6153SChristoph Hellwig 		if (copy_from_sockptr(&v, optval, sizeof(v)))
1816d1db275dSPatrick McHardy 			return -EFAULT;
181775356a81SDan Carpenter 		/* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */
181875356a81SDan Carpenter 		if (v != RT_TABLE_DEFAULT && v >= 100000000)
181975356a81SDan Carpenter 			return -EINVAL;
1820b70432f7SYuval Mintz 		if (sk == rcu_access_pointer(mrt->mroute_sk))
1821d1db275dSPatrick McHardy 			return -EBUSY;
1822d1db275dSPatrick McHardy 
1823d1db275dSPatrick McHardy 		rtnl_lock();
1824d1db275dSPatrick McHardy 		ret = 0;
1825e783bb00SSabrina Dubroca 		mrt = ip6mr_new_table(net, v);
1826e783bb00SSabrina Dubroca 		if (IS_ERR(mrt))
1827e783bb00SSabrina Dubroca 			ret = PTR_ERR(mrt);
1828848235edSSabrina Dubroca 		else
1829d1db275dSPatrick McHardy 			raw6_sk(sk)->ip6mr_table = v;
1830d1db275dSPatrick McHardy 		rtnl_unlock();
1831d1db275dSPatrick McHardy 		return ret;
1832d1db275dSPatrick McHardy 	}
1833d1db275dSPatrick McHardy #endif
183414fb64e1SYOSHIFUJI Hideaki 	/*
18357d120c55SRami Rosen 	 *	Spurious command, or MRT6_VERSION which you cannot
18367bc570c8SYOSHIFUJI Hideaki 	 *	set.
18377bc570c8SYOSHIFUJI Hideaki 	 */
18387bc570c8SYOSHIFUJI Hideaki 	default:
18397bc570c8SYOSHIFUJI Hideaki 		return -ENOPROTOOPT;
18407bc570c8SYOSHIFUJI Hideaki 	}
18417bc570c8SYOSHIFUJI Hideaki }
18427bc570c8SYOSHIFUJI Hideaki 
18437bc570c8SYOSHIFUJI Hideaki /*
18447bc570c8SYOSHIFUJI Hideaki  *	Getsock opt support for the multicast routing system.
18457bc570c8SYOSHIFUJI Hideaki  */
18467bc570c8SYOSHIFUJI Hideaki 
ip6_mroute_getsockopt(struct sock * sk,int optname,sockptr_t optval,sockptr_t optlen)18476dadbe4bSMartin KaFai Lau int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
18486dadbe4bSMartin KaFai Lau 			  sockptr_t optlen)
18497bc570c8SYOSHIFUJI Hideaki {
18507bc570c8SYOSHIFUJI Hideaki 	int olr;
18517bc570c8SYOSHIFUJI Hideaki 	int val;
18528229efdaSBenjamin Thery 	struct net *net = sock_net(sk);
1853b70432f7SYuval Mintz 	struct mr_table *mrt;
1854d1db275dSPatrick McHardy 
185599253eb7SXin Long 	if (sk->sk_type != SOCK_RAW ||
185699253eb7SXin Long 	    inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
185799253eb7SXin Long 		return -EOPNOTSUPP;
185899253eb7SXin Long 
1859d1db275dSPatrick McHardy 	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
186063159f29SIan Morris 	if (!mrt)
1861d1db275dSPatrick McHardy 		return -ENOENT;
18627bc570c8SYOSHIFUJI Hideaki 
18637bc570c8SYOSHIFUJI Hideaki 	switch (optname) {
18647bc570c8SYOSHIFUJI Hideaki 	case MRT6_VERSION:
18657bc570c8SYOSHIFUJI Hideaki 		val = 0x0305;
18667bc570c8SYOSHIFUJI Hideaki 		break;
186714fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
186814fb64e1SYOSHIFUJI Hideaki 	case MRT6_PIM:
18696bd52143SPatrick McHardy 		val = mrt->mroute_do_pim;
187014fb64e1SYOSHIFUJI Hideaki 		break;
187114fb64e1SYOSHIFUJI Hideaki #endif
187214fb64e1SYOSHIFUJI Hideaki 	case MRT6_ASSERT:
18736bd52143SPatrick McHardy 		val = mrt->mroute_do_assert;
187414fb64e1SYOSHIFUJI Hideaki 		break;
18757bc570c8SYOSHIFUJI Hideaki 	default:
18767bc570c8SYOSHIFUJI Hideaki 		return -ENOPROTOOPT;
18777bc570c8SYOSHIFUJI Hideaki 	}
18787bc570c8SYOSHIFUJI Hideaki 
18796dadbe4bSMartin KaFai Lau 	if (copy_from_sockptr(&olr, optlen, sizeof(int)))
18807bc570c8SYOSHIFUJI Hideaki 		return -EFAULT;
18817bc570c8SYOSHIFUJI Hideaki 
18827bc570c8SYOSHIFUJI Hideaki 	olr = min_t(int, olr, sizeof(int));
18837bc570c8SYOSHIFUJI Hideaki 	if (olr < 0)
18847bc570c8SYOSHIFUJI Hideaki 		return -EINVAL;
18857bc570c8SYOSHIFUJI Hideaki 
18866dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optlen, &olr, sizeof(int)))
18877bc570c8SYOSHIFUJI Hideaki 		return -EFAULT;
18886dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optval, &val, olr))
18897bc570c8SYOSHIFUJI Hideaki 		return -EFAULT;
18907bc570c8SYOSHIFUJI Hideaki 	return 0;
18917bc570c8SYOSHIFUJI Hideaki }
18927bc570c8SYOSHIFUJI Hideaki 
18937bc570c8SYOSHIFUJI Hideaki /*
18947bc570c8SYOSHIFUJI Hideaki  *	The IP multicast ioctl support routines.
18957bc570c8SYOSHIFUJI Hideaki  */
ip6mr_ioctl(struct sock * sk,int cmd,void * arg)1896e1d001faSBreno Leitao int ip6mr_ioctl(struct sock *sk, int cmd, void *arg)
18977bc570c8SYOSHIFUJI Hideaki {
1898e1d001faSBreno Leitao 	struct sioc_sg_req6 *sr;
1899e1d001faSBreno Leitao 	struct sioc_mif_req6 *vr;
19006853f21fSYuval Mintz 	struct vif_device *vif;
19017bc570c8SYOSHIFUJI Hideaki 	struct mfc6_cache *c;
19028229efdaSBenjamin Thery 	struct net *net = sock_net(sk);
1903b70432f7SYuval Mintz 	struct mr_table *mrt;
1904d1db275dSPatrick McHardy 
1905d1db275dSPatrick McHardy 	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
190663159f29SIan Morris 	if (!mrt)
1907d1db275dSPatrick McHardy 		return -ENOENT;
19087bc570c8SYOSHIFUJI Hideaki 
19097bc570c8SYOSHIFUJI Hideaki 	switch (cmd) {
19107bc570c8SYOSHIFUJI Hideaki 	case SIOCGETMIFCNT_IN6:
1911e1d001faSBreno Leitao 		vr = (struct sioc_mif_req6 *)arg;
1912e1d001faSBreno Leitao 		if (vr->mifi >= mrt->maxvif)
19137bc570c8SYOSHIFUJI Hideaki 			return -EINVAL;
1914e1d001faSBreno Leitao 		vr->mifi = array_index_nospec(vr->mifi, mrt->maxvif);
1915638cf4a2SEric Dumazet 		rcu_read_lock();
1916e1d001faSBreno Leitao 		vif = &mrt->vif_table[vr->mifi];
1917e1d001faSBreno Leitao 		if (VIF_EXISTS(mrt, vr->mifi)) {
1918e1d001faSBreno Leitao 			vr->icount = READ_ONCE(vif->pkt_in);
1919e1d001faSBreno Leitao 			vr->ocount = READ_ONCE(vif->pkt_out);
1920e1d001faSBreno Leitao 			vr->ibytes = READ_ONCE(vif->bytes_in);
1921e1d001faSBreno Leitao 			vr->obytes = READ_ONCE(vif->bytes_out);
1922638cf4a2SEric Dumazet 			rcu_read_unlock();
19237bc570c8SYOSHIFUJI Hideaki 			return 0;
19247bc570c8SYOSHIFUJI Hideaki 		}
1925638cf4a2SEric Dumazet 		rcu_read_unlock();
19267bc570c8SYOSHIFUJI Hideaki 		return -EADDRNOTAVAIL;
19277bc570c8SYOSHIFUJI Hideaki 	case SIOCGETSGCNT_IN6:
1928e1d001faSBreno Leitao 		sr = (struct sioc_sg_req6 *)arg;
19297bc570c8SYOSHIFUJI Hideaki 
193087c418bfSYuval Mintz 		rcu_read_lock();
1931e1d001faSBreno Leitao 		c = ip6mr_cache_find(mrt, &sr->src.sin6_addr,
1932e1d001faSBreno Leitao 				     &sr->grp.sin6_addr);
19337bc570c8SYOSHIFUJI Hideaki 		if (c) {
1934*5960f4d8SEric Dumazet 			sr->pktcnt = atomic_long_read(&c->_c.mfc_un.res.pkt);
1935*5960f4d8SEric Dumazet 			sr->bytecnt = atomic_long_read(&c->_c.mfc_un.res.bytes);
1936*5960f4d8SEric Dumazet 			sr->wrong_if = atomic_long_read(&c->_c.mfc_un.res.wrong_if);
193787c418bfSYuval Mintz 			rcu_read_unlock();
19387bc570c8SYOSHIFUJI Hideaki 			return 0;
19397bc570c8SYOSHIFUJI Hideaki 		}
194087c418bfSYuval Mintz 		rcu_read_unlock();
19417bc570c8SYOSHIFUJI Hideaki 		return -EADDRNOTAVAIL;
19427bc570c8SYOSHIFUJI Hideaki 	default:
19437bc570c8SYOSHIFUJI Hideaki 		return -ENOIOCTLCMD;
19447bc570c8SYOSHIFUJI Hideaki 	}
19457bc570c8SYOSHIFUJI Hideaki }
19467bc570c8SYOSHIFUJI Hideaki 
1947e2d57766SDavid S. Miller #ifdef CONFIG_COMPAT
1948e2d57766SDavid S. Miller struct compat_sioc_sg_req6 {
1949e2d57766SDavid S. Miller 	struct sockaddr_in6 src;
1950e2d57766SDavid S. Miller 	struct sockaddr_in6 grp;
1951e2d57766SDavid S. Miller 	compat_ulong_t pktcnt;
1952e2d57766SDavid S. Miller 	compat_ulong_t bytecnt;
1953e2d57766SDavid S. Miller 	compat_ulong_t wrong_if;
1954e2d57766SDavid S. Miller };
1955e2d57766SDavid S. Miller 
1956e2d57766SDavid S. Miller struct compat_sioc_mif_req6 {
1957e2d57766SDavid S. Miller 	mifi_t	mifi;
1958e2d57766SDavid S. Miller 	compat_ulong_t icount;
1959e2d57766SDavid S. Miller 	compat_ulong_t ocount;
1960e2d57766SDavid S. Miller 	compat_ulong_t ibytes;
1961e2d57766SDavid S. Miller 	compat_ulong_t obytes;
1962e2d57766SDavid S. Miller };
1963e2d57766SDavid S. Miller 
ip6mr_compat_ioctl(struct sock * sk,unsigned int cmd,void __user * arg)1964e2d57766SDavid S. Miller int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
1965e2d57766SDavid S. Miller {
1966e2d57766SDavid S. Miller 	struct compat_sioc_sg_req6 sr;
1967e2d57766SDavid S. Miller 	struct compat_sioc_mif_req6 vr;
19686853f21fSYuval Mintz 	struct vif_device *vif;
1969e2d57766SDavid S. Miller 	struct mfc6_cache *c;
1970e2d57766SDavid S. Miller 	struct net *net = sock_net(sk);
1971b70432f7SYuval Mintz 	struct mr_table *mrt;
1972e2d57766SDavid S. Miller 
1973e2d57766SDavid S. Miller 	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
197463159f29SIan Morris 	if (!mrt)
1975e2d57766SDavid S. Miller 		return -ENOENT;
1976e2d57766SDavid S. Miller 
1977e2d57766SDavid S. Miller 	switch (cmd) {
1978e2d57766SDavid S. Miller 	case SIOCGETMIFCNT_IN6:
1979e2d57766SDavid S. Miller 		if (copy_from_user(&vr, arg, sizeof(vr)))
1980e2d57766SDavid S. Miller 			return -EFAULT;
1981e2d57766SDavid S. Miller 		if (vr.mifi >= mrt->maxvif)
1982e2d57766SDavid S. Miller 			return -EINVAL;
198369d2c867SGustavo A. R. Silva 		vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
1984638cf4a2SEric Dumazet 		rcu_read_lock();
1985b70432f7SYuval Mintz 		vif = &mrt->vif_table[vr.mifi];
1986b70432f7SYuval Mintz 		if (VIF_EXISTS(mrt, vr.mifi)) {
1987638cf4a2SEric Dumazet 			vr.icount = READ_ONCE(vif->pkt_in);
1988638cf4a2SEric Dumazet 			vr.ocount = READ_ONCE(vif->pkt_out);
1989638cf4a2SEric Dumazet 			vr.ibytes = READ_ONCE(vif->bytes_in);
1990638cf4a2SEric Dumazet 			vr.obytes = READ_ONCE(vif->bytes_out);
1991638cf4a2SEric Dumazet 			rcu_read_unlock();
1992e2d57766SDavid S. Miller 
1993e2d57766SDavid S. Miller 			if (copy_to_user(arg, &vr, sizeof(vr)))
1994e2d57766SDavid S. Miller 				return -EFAULT;
1995e2d57766SDavid S. Miller 			return 0;
1996e2d57766SDavid S. Miller 		}
1997638cf4a2SEric Dumazet 		rcu_read_unlock();
1998e2d57766SDavid S. Miller 		return -EADDRNOTAVAIL;
1999e2d57766SDavid S. Miller 	case SIOCGETSGCNT_IN6:
2000e2d57766SDavid S. Miller 		if (copy_from_user(&sr, arg, sizeof(sr)))
2001e2d57766SDavid S. Miller 			return -EFAULT;
2002e2d57766SDavid S. Miller 
200387c418bfSYuval Mintz 		rcu_read_lock();
2004e2d57766SDavid S. Miller 		c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr);
2005e2d57766SDavid S. Miller 		if (c) {
2006*5960f4d8SEric Dumazet 			sr.pktcnt = atomic_long_read(&c->_c.mfc_un.res.pkt);
2007*5960f4d8SEric Dumazet 			sr.bytecnt = atomic_long_read(&c->_c.mfc_un.res.bytes);
2008*5960f4d8SEric Dumazet 			sr.wrong_if = atomic_long_read(&c->_c.mfc_un.res.wrong_if);
200987c418bfSYuval Mintz 			rcu_read_unlock();
2010e2d57766SDavid S. Miller 
2011e2d57766SDavid S. Miller 			if (copy_to_user(arg, &sr, sizeof(sr)))
2012e2d57766SDavid S. Miller 				return -EFAULT;
2013e2d57766SDavid S. Miller 			return 0;
2014e2d57766SDavid S. Miller 		}
201587c418bfSYuval Mintz 		rcu_read_unlock();
2016e2d57766SDavid S. Miller 		return -EADDRNOTAVAIL;
2017e2d57766SDavid S. Miller 	default:
2018e2d57766SDavid S. Miller 		return -ENOIOCTLCMD;
2019e2d57766SDavid S. Miller 	}
2020e2d57766SDavid S. Miller }
2021e2d57766SDavid S. Miller #endif
20227bc570c8SYOSHIFUJI Hideaki 
ip6mr_forward2_finish(struct net * net,struct sock * sk,struct sk_buff * skb)20230c4b51f0SEric W. Biederman static inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
20247bc570c8SYOSHIFUJI Hideaki {
202587c11f1dSIdo Schimmel 	IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
2026483a47d2SDenis V. Lunev 		      IPSTATS_MIB_OUTFORWDATAGRAMS);
202713206b6bSEric W. Biederman 	return dst_output(net, sk, skb);
20287bc570c8SYOSHIFUJI Hideaki }
20297bc570c8SYOSHIFUJI Hideaki 
20307bc570c8SYOSHIFUJI Hideaki /*
20317bc570c8SYOSHIFUJI Hideaki  *	Processing handlers for ip6mr_forward
20327bc570c8SYOSHIFUJI Hideaki  */
20337bc570c8SYOSHIFUJI Hideaki 
ip6mr_forward2(struct net * net,struct mr_table * mrt,struct sk_buff * skb,int vifi)2034b70432f7SYuval Mintz static int ip6mr_forward2(struct net *net, struct mr_table *mrt,
2035f5c6dfdeSDavid Ahern 			  struct sk_buff *skb, int vifi)
20367bc570c8SYOSHIFUJI Hideaki {
2037b70432f7SYuval Mintz 	struct vif_device *vif = &mrt->vif_table[vifi];
2038ebc31979SEric Dumazet 	struct net_device *vif_dev;
2039ebc31979SEric Dumazet 	struct ipv6hdr *ipv6h;
20407bc570c8SYOSHIFUJI Hideaki 	struct dst_entry *dst;
20414c9483b2SDavid S. Miller 	struct flowi6 fl6;
20427bc570c8SYOSHIFUJI Hideaki 
2043ebc31979SEric Dumazet 	vif_dev = vif_dev_read(vif);
2044ebc31979SEric Dumazet 	if (!vif_dev)
20457bc570c8SYOSHIFUJI Hideaki 		goto out_free;
20467bc570c8SYOSHIFUJI Hideaki 
204714fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
204814fb64e1SYOSHIFUJI Hideaki 	if (vif->flags & MIFF_REGISTER) {
2049638cf4a2SEric Dumazet 		WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1);
2050638cf4a2SEric Dumazet 		WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len);
20512fad1ba3SEric Dumazet 		DEV_STATS_ADD(vif_dev, tx_bytes, skb->len);
20522fad1ba3SEric Dumazet 		DEV_STATS_INC(vif_dev, tx_packets);
20536bd52143SPatrick McHardy 		ip6mr_cache_report(mrt, skb, vifi, MRT6MSG_WHOLEPKT);
20548da73b73SIlpo Järvinen 		goto out_free;
205514fb64e1SYOSHIFUJI Hideaki 	}
205614fb64e1SYOSHIFUJI Hideaki #endif
205714fb64e1SYOSHIFUJI Hideaki 
20587bc570c8SYOSHIFUJI Hideaki 	ipv6h = ipv6_hdr(skb);
20597bc570c8SYOSHIFUJI Hideaki 
20604c9483b2SDavid S. Miller 	fl6 = (struct flowi6) {
20614c9483b2SDavid S. Miller 		.flowi6_oif = vif->link,
20624c9483b2SDavid S. Miller 		.daddr = ipv6h->daddr,
20637bc570c8SYOSHIFUJI Hideaki 	};
20647bc570c8SYOSHIFUJI Hideaki 
20654c9483b2SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
20665095d64dSRongQing.Li 	if (dst->error) {
20675095d64dSRongQing.Li 		dst_release(dst);
20687bc570c8SYOSHIFUJI Hideaki 		goto out_free;
20695095d64dSRongQing.Li 	}
20707bc570c8SYOSHIFUJI Hideaki 
2071adf30907SEric Dumazet 	skb_dst_drop(skb);
2072adf30907SEric Dumazet 	skb_dst_set(skb, dst);
20737bc570c8SYOSHIFUJI Hideaki 
20747bc570c8SYOSHIFUJI Hideaki 	/*
20757bc570c8SYOSHIFUJI Hideaki 	 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
20767bc570c8SYOSHIFUJI Hideaki 	 * not only before forwarding, but after forwarding on all output
20777bc570c8SYOSHIFUJI Hideaki 	 * interfaces. It is clear, if mrouter runs a multicasting
20787bc570c8SYOSHIFUJI Hideaki 	 * program, it should receive packets not depending to what interface
20797bc570c8SYOSHIFUJI Hideaki 	 * program is joined.
20807bc570c8SYOSHIFUJI Hideaki 	 * If we will not make it, the program will have to join on all
20817bc570c8SYOSHIFUJI Hideaki 	 * interfaces. On the other hand, multihoming host (or router, but
20827bc570c8SYOSHIFUJI Hideaki 	 * not mrouter) cannot join to more than one interface - it will
20837bc570c8SYOSHIFUJI Hideaki 	 * result in receiving multiple packets.
20847bc570c8SYOSHIFUJI Hideaki 	 */
2085ebc31979SEric Dumazet 	skb->dev = vif_dev;
2086638cf4a2SEric Dumazet 	WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1);
2087638cf4a2SEric Dumazet 	WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len);
20887bc570c8SYOSHIFUJI Hideaki 
20897bc570c8SYOSHIFUJI Hideaki 	/* We are about to write */
20907bc570c8SYOSHIFUJI Hideaki 	/* XXX: extension headers? */
2091ebc31979SEric Dumazet 	if (skb_cow(skb, sizeof(*ipv6h) + LL_RESERVED_SPACE(vif_dev)))
20927bc570c8SYOSHIFUJI Hideaki 		goto out_free;
20937bc570c8SYOSHIFUJI Hideaki 
20947bc570c8SYOSHIFUJI Hideaki 	ipv6h = ipv6_hdr(skb);
20957bc570c8SYOSHIFUJI Hideaki 	ipv6h->hop_limit--;
20967bc570c8SYOSHIFUJI Hideaki 
20977bc570c8SYOSHIFUJI Hideaki 	IP6CB(skb)->flags |= IP6SKB_FORWARDED;
20987bc570c8SYOSHIFUJI Hideaki 
209929a26a56SEric W. Biederman 	return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD,
2100ebc31979SEric Dumazet 		       net, NULL, skb, skb->dev, vif_dev,
21017bc570c8SYOSHIFUJI Hideaki 		       ip6mr_forward2_finish);
21027bc570c8SYOSHIFUJI Hideaki 
21037bc570c8SYOSHIFUJI Hideaki out_free:
21047bc570c8SYOSHIFUJI Hideaki 	kfree_skb(skb);
21057bc570c8SYOSHIFUJI Hideaki 	return 0;
21067bc570c8SYOSHIFUJI Hideaki }
21077bc570c8SYOSHIFUJI Hideaki 
2108a96f7a6aSEric Dumazet /* Called with rcu_read_lock() */
ip6mr_find_vif(struct mr_table * mrt,struct net_device * dev)2109b70432f7SYuval Mintz static int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev)
21107bc570c8SYOSHIFUJI Hideaki {
21117bc570c8SYOSHIFUJI Hideaki 	int ct;
21126bd52143SPatrick McHardy 
2113db9eb7c8SEric Dumazet 	/* Pairs with WRITE_ONCE() in mif6_delete()/mif6_add() */
2114db9eb7c8SEric Dumazet 	for (ct = READ_ONCE(mrt->maxvif) - 1; ct >= 0; ct--) {
2115ebc31979SEric Dumazet 		if (rcu_access_pointer(mrt->vif_table[ct].dev) == dev)
21167bc570c8SYOSHIFUJI Hideaki 			break;
21177bc570c8SYOSHIFUJI Hideaki 	}
21187bc570c8SYOSHIFUJI Hideaki 	return ct;
21197bc570c8SYOSHIFUJI Hideaki }
21207bc570c8SYOSHIFUJI Hideaki 
21219b1c21d8SEric Dumazet /* Called under rcu_read_lock() */
ip6_mr_forward(struct net * net,struct mr_table * mrt,struct net_device * dev,struct sk_buff * skb,struct mfc6_cache * c)2122b70432f7SYuval Mintz static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
2123e4a38c0cSPatrick Ruddy 			   struct net_device *dev, struct sk_buff *skb,
2124e4a38c0cSPatrick Ruddy 			   struct mfc6_cache *c)
21257bc570c8SYOSHIFUJI Hideaki {
21267bc570c8SYOSHIFUJI Hideaki 	int psend = -1;
21277bc570c8SYOSHIFUJI Hideaki 	int vif, ct;
2128e4a38c0cSPatrick Ruddy 	int true_vifi = ip6mr_find_vif(mrt, dev);
21297bc570c8SYOSHIFUJI Hideaki 
2130494fff56SYuval Mintz 	vif = c->_c.mfc_parent;
2131*5960f4d8SEric Dumazet 	atomic_long_inc(&c->_c.mfc_un.res.pkt);
2132*5960f4d8SEric Dumazet 	atomic_long_add(skb->len, &c->_c.mfc_un.res.bytes);
2133*5960f4d8SEric Dumazet 	WRITE_ONCE(c->_c.mfc_un.res.lastuse, jiffies);
21347bc570c8SYOSHIFUJI Hideaki 
2135494fff56SYuval Mintz 	if (ipv6_addr_any(&c->mf6c_origin) && true_vifi >= 0) {
2136660b26dcSNicolas Dichtel 		struct mfc6_cache *cache_proxy;
2137660b26dcSNicolas Dichtel 
213840dc2ca3SFabian Frederick 		/* For an (*,G) entry, we only check that the incoming
2139660b26dcSNicolas Dichtel 		 * interface is part of the static tree.
2140660b26dcSNicolas Dichtel 		 */
2141845c9a7aSYuval Mintz 		cache_proxy = mr_mfc_find_any_parent(mrt, vif);
2142660b26dcSNicolas Dichtel 		if (cache_proxy &&
2143a7e555d4SEric Dumazet 		    cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255)
2144660b26dcSNicolas Dichtel 			goto forward;
2145660b26dcSNicolas Dichtel 	}
2146660b26dcSNicolas Dichtel 
214714fb64e1SYOSHIFUJI Hideaki 	/*
214814fb64e1SYOSHIFUJI Hideaki 	 * Wrong interface: drop packet and (maybe) send PIM assert.
214914fb64e1SYOSHIFUJI Hideaki 	 */
2150ebc31979SEric Dumazet 	if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
2151*5960f4d8SEric Dumazet 		atomic_long_inc(&c->_c.mfc_un.res.wrong_if);
215214fb64e1SYOSHIFUJI Hideaki 
21536bd52143SPatrick McHardy 		if (true_vifi >= 0 && mrt->mroute_do_assert &&
215414fb64e1SYOSHIFUJI Hideaki 		    /* pimsm uses asserts, when switching from RPT to SPT,
215514fb64e1SYOSHIFUJI Hideaki 		       so that we cannot check that packet arrived on an oif.
215614fb64e1SYOSHIFUJI Hideaki 		       It is bad, but otherwise we would need to move pretty
215714fb64e1SYOSHIFUJI Hideaki 		       large chunk of pimd to kernel. Ough... --ANK
215814fb64e1SYOSHIFUJI Hideaki 		     */
21596bd52143SPatrick McHardy 		    (mrt->mroute_do_pim ||
2160494fff56SYuval Mintz 		     c->_c.mfc_un.res.ttls[true_vifi] < 255) &&
216114fb64e1SYOSHIFUJI Hideaki 		    time_after(jiffies,
2162494fff56SYuval Mintz 			       c->_c.mfc_un.res.last_assert +
2163494fff56SYuval Mintz 			       MFC_ASSERT_THRESH)) {
2164494fff56SYuval Mintz 			c->_c.mfc_un.res.last_assert = jiffies;
21656bd52143SPatrick McHardy 			ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF);
21664b340a5aSMobashshera Rasool 			if (mrt->mroute_do_wrvifwhole)
21674b340a5aSMobashshera Rasool 				ip6mr_cache_report(mrt, skb, true_vifi,
21684b340a5aSMobashshera Rasool 						   MRT6MSG_WRMIFWHOLE);
216914fb64e1SYOSHIFUJI Hideaki 		}
217014fb64e1SYOSHIFUJI Hideaki 		goto dont_forward;
217114fb64e1SYOSHIFUJI Hideaki 	}
217214fb64e1SYOSHIFUJI Hideaki 
2173660b26dcSNicolas Dichtel forward:
2174638cf4a2SEric Dumazet 	WRITE_ONCE(mrt->vif_table[vif].pkt_in,
2175638cf4a2SEric Dumazet 		   mrt->vif_table[vif].pkt_in + 1);
2176638cf4a2SEric Dumazet 	WRITE_ONCE(mrt->vif_table[vif].bytes_in,
2177638cf4a2SEric Dumazet 		   mrt->vif_table[vif].bytes_in + skb->len);
21787bc570c8SYOSHIFUJI Hideaki 
21797bc570c8SYOSHIFUJI Hideaki 	/*
21807bc570c8SYOSHIFUJI Hideaki 	 *	Forward the frame
21817bc570c8SYOSHIFUJI Hideaki 	 */
2182494fff56SYuval Mintz 	if (ipv6_addr_any(&c->mf6c_origin) &&
2183494fff56SYuval Mintz 	    ipv6_addr_any(&c->mf6c_mcastgrp)) {
2184660b26dcSNicolas Dichtel 		if (true_vifi >= 0 &&
2185494fff56SYuval Mintz 		    true_vifi != c->_c.mfc_parent &&
2186660b26dcSNicolas Dichtel 		    ipv6_hdr(skb)->hop_limit >
2187494fff56SYuval Mintz 				c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) {
2188660b26dcSNicolas Dichtel 			/* It's an (*,*) entry and the packet is not coming from
2189660b26dcSNicolas Dichtel 			 * the upstream: forward the packet to the upstream
2190660b26dcSNicolas Dichtel 			 * only.
2191660b26dcSNicolas Dichtel 			 */
2192494fff56SYuval Mintz 			psend = c->_c.mfc_parent;
2193660b26dcSNicolas Dichtel 			goto last_forward;
2194660b26dcSNicolas Dichtel 		}
2195660b26dcSNicolas Dichtel 		goto dont_forward;
2196660b26dcSNicolas Dichtel 	}
2197494fff56SYuval Mintz 	for (ct = c->_c.mfc_un.res.maxvif - 1;
2198494fff56SYuval Mintz 	     ct >= c->_c.mfc_un.res.minvif; ct--) {
2199660b26dcSNicolas Dichtel 		/* For (*,G) entry, don't forward to the incoming interface */
2200494fff56SYuval Mintz 		if ((!ipv6_addr_any(&c->mf6c_origin) || ct != true_vifi) &&
2201494fff56SYuval Mintz 		    ipv6_hdr(skb)->hop_limit > c->_c.mfc_un.res.ttls[ct]) {
22027bc570c8SYOSHIFUJI Hideaki 			if (psend != -1) {
22037bc570c8SYOSHIFUJI Hideaki 				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
22047bc570c8SYOSHIFUJI Hideaki 				if (skb2)
2205f5c6dfdeSDavid Ahern 					ip6mr_forward2(net, mrt, skb2, psend);
22067bc570c8SYOSHIFUJI Hideaki 			}
22077bc570c8SYOSHIFUJI Hideaki 			psend = ct;
22087bc570c8SYOSHIFUJI Hideaki 		}
22097bc570c8SYOSHIFUJI Hideaki 	}
2210660b26dcSNicolas Dichtel last_forward:
22117bc570c8SYOSHIFUJI Hideaki 	if (psend != -1) {
2212f5c6dfdeSDavid Ahern 		ip6mr_forward2(net, mrt, skb, psend);
22132b52c3adSRami Rosen 		return;
22147bc570c8SYOSHIFUJI Hideaki 	}
22157bc570c8SYOSHIFUJI Hideaki 
221614fb64e1SYOSHIFUJI Hideaki dont_forward:
22177bc570c8SYOSHIFUJI Hideaki 	kfree_skb(skb);
22187bc570c8SYOSHIFUJI Hideaki }
22197bc570c8SYOSHIFUJI Hideaki 
22207bc570c8SYOSHIFUJI Hideaki 
22217bc570c8SYOSHIFUJI Hideaki /*
22227bc570c8SYOSHIFUJI Hideaki  *	Multicast packets for forwarding arrive here
22237bc570c8SYOSHIFUJI Hideaki  */
22247bc570c8SYOSHIFUJI Hideaki 
ip6_mr_input(struct sk_buff * skb)22257bc570c8SYOSHIFUJI Hideaki int ip6_mr_input(struct sk_buff *skb)
22267bc570c8SYOSHIFUJI Hideaki {
22277bc570c8SYOSHIFUJI Hideaki 	struct mfc6_cache *cache;
22288229efdaSBenjamin Thery 	struct net *net = dev_net(skb->dev);
2229b70432f7SYuval Mintz 	struct mr_table *mrt;
22304c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
22314c9483b2SDavid S. Miller 		.flowi6_iif	= skb->dev->ifindex,
22324c9483b2SDavid S. Miller 		.flowi6_mark	= skb->mark,
2233d1db275dSPatrick McHardy 	};
2234d1db275dSPatrick McHardy 	int err;
2235e4a38c0cSPatrick Ruddy 	struct net_device *dev;
2236e4a38c0cSPatrick Ruddy 
2237e4a38c0cSPatrick Ruddy 	/* skb->dev passed in is the master dev for vrfs.
2238e4a38c0cSPatrick Ruddy 	 * Get the proper interface that does have a vif associated with it.
2239e4a38c0cSPatrick Ruddy 	 */
2240e4a38c0cSPatrick Ruddy 	dev = skb->dev;
2241e4a38c0cSPatrick Ruddy 	if (netif_is_l3_master(skb->dev)) {
2242e4a38c0cSPatrick Ruddy 		dev = dev_get_by_index_rcu(net, IPCB(skb)->iif);
2243e4a38c0cSPatrick Ruddy 		if (!dev) {
2244e4a38c0cSPatrick Ruddy 			kfree_skb(skb);
2245e4a38c0cSPatrick Ruddy 			return -ENODEV;
2246e4a38c0cSPatrick Ruddy 		}
2247e4a38c0cSPatrick Ruddy 	}
2248d1db275dSPatrick McHardy 
22494c9483b2SDavid S. Miller 	err = ip6mr_fib_lookup(net, &fl6, &mrt);
22502015de5fSBen Greear 	if (err < 0) {
22512015de5fSBen Greear 		kfree_skb(skb);
2252d1db275dSPatrick McHardy 		return err;
22532015de5fSBen Greear 	}
22547bc570c8SYOSHIFUJI Hideaki 
22556bd52143SPatrick McHardy 	cache = ip6mr_cache_find(mrt,
22568229efdaSBenjamin Thery 				 &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
225763159f29SIan Morris 	if (!cache) {
2258e4a38c0cSPatrick Ruddy 		int vif = ip6mr_find_vif(mrt, dev);
2259660b26dcSNicolas Dichtel 
2260660b26dcSNicolas Dichtel 		if (vif >= 0)
2261660b26dcSNicolas Dichtel 			cache = ip6mr_cache_find_any(mrt,
2262660b26dcSNicolas Dichtel 						     &ipv6_hdr(skb)->daddr,
2263660b26dcSNicolas Dichtel 						     vif);
2264660b26dcSNicolas Dichtel 	}
22657bc570c8SYOSHIFUJI Hideaki 
22667bc570c8SYOSHIFUJI Hideaki 	/*
22677bc570c8SYOSHIFUJI Hideaki 	 *	No usable cache entry
22687bc570c8SYOSHIFUJI Hideaki 	 */
226963159f29SIan Morris 	if (!cache) {
22707bc570c8SYOSHIFUJI Hideaki 		int vif;
22717bc570c8SYOSHIFUJI Hideaki 
2272e4a38c0cSPatrick Ruddy 		vif = ip6mr_find_vif(mrt, dev);
22737bc570c8SYOSHIFUJI Hideaki 		if (vif >= 0) {
2274e4a38c0cSPatrick Ruddy 			int err = ip6mr_cache_unresolved(mrt, vif, skb, dev);
22757bc570c8SYOSHIFUJI Hideaki 
22767bc570c8SYOSHIFUJI Hideaki 			return err;
22777bc570c8SYOSHIFUJI Hideaki 		}
22787bc570c8SYOSHIFUJI Hideaki 		kfree_skb(skb);
22797bc570c8SYOSHIFUJI Hideaki 		return -ENODEV;
22807bc570c8SYOSHIFUJI Hideaki 	}
22817bc570c8SYOSHIFUJI Hideaki 
2282e4a38c0cSPatrick Ruddy 	ip6_mr_forward(net, mrt, dev, skb, cache);
22837bc570c8SYOSHIFUJI Hideaki 
22847bc570c8SYOSHIFUJI Hideaki 	return 0;
22857bc570c8SYOSHIFUJI Hideaki }
22867bc570c8SYOSHIFUJI Hideaki 
ip6mr_get_route(struct net * net,struct sk_buff * skb,struct rtmsg * rtm,u32 portid)22872cf75070SNikolay Aleksandrov int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
2288fd61c6baSDavid Ahern 		    u32 portid)
22897bc570c8SYOSHIFUJI Hideaki {
22907bc570c8SYOSHIFUJI Hideaki 	int err;
2291b70432f7SYuval Mintz 	struct mr_table *mrt;
22927bc570c8SYOSHIFUJI Hideaki 	struct mfc6_cache *cache;
2293797a4c1fSEric Dumazet 	struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
22947bc570c8SYOSHIFUJI Hideaki 
22956fa40a29SEric Dumazet 	rcu_read_lock();
2296bba7909bSPaolo Abeni 	mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
2297bba7909bSPaolo Abeni 	if (!mrt) {
2298bba7909bSPaolo Abeni 		rcu_read_unlock();
2299bba7909bSPaolo Abeni 		return -ENOENT;
2300bba7909bSPaolo Abeni 	}
2301bba7909bSPaolo Abeni 
23026bd52143SPatrick McHardy 	cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr);
2303660b26dcSNicolas Dichtel 	if (!cache && skb->dev) {
2304660b26dcSNicolas Dichtel 		int vif = ip6mr_find_vif(mrt, skb->dev);
2305660b26dcSNicolas Dichtel 
2306660b26dcSNicolas Dichtel 		if (vif >= 0)
2307660b26dcSNicolas Dichtel 			cache = ip6mr_cache_find_any(mrt, &rt->rt6i_dst.addr,
2308660b26dcSNicolas Dichtel 						     vif);
2309660b26dcSNicolas Dichtel 	}
23107bc570c8SYOSHIFUJI Hideaki 
23117bc570c8SYOSHIFUJI Hideaki 	if (!cache) {
23127bc570c8SYOSHIFUJI Hideaki 		struct sk_buff *skb2;
23137bc570c8SYOSHIFUJI Hideaki 		struct ipv6hdr *iph;
23147bc570c8SYOSHIFUJI Hideaki 		struct net_device *dev;
23157bc570c8SYOSHIFUJI Hideaki 		int vif;
23167bc570c8SYOSHIFUJI Hideaki 
23177bc570c8SYOSHIFUJI Hideaki 		dev = skb->dev;
231863159f29SIan Morris 		if (!dev || (vif = ip6mr_find_vif(mrt, dev)) < 0) {
23196fa40a29SEric Dumazet 			rcu_read_unlock();
23207bc570c8SYOSHIFUJI Hideaki 			return -ENODEV;
23217bc570c8SYOSHIFUJI Hideaki 		}
23227bc570c8SYOSHIFUJI Hideaki 
23237bc570c8SYOSHIFUJI Hideaki 		/* really correct? */
23247bc570c8SYOSHIFUJI Hideaki 		skb2 = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
23257bc570c8SYOSHIFUJI Hideaki 		if (!skb2) {
23266fa40a29SEric Dumazet 			rcu_read_unlock();
23277bc570c8SYOSHIFUJI Hideaki 			return -ENOMEM;
23287bc570c8SYOSHIFUJI Hideaki 		}
23297bc570c8SYOSHIFUJI Hideaki 
23302cf75070SNikolay Aleksandrov 		NETLINK_CB(skb2).portid = portid;
23317bc570c8SYOSHIFUJI Hideaki 		skb_reset_transport_header(skb2);
23327bc570c8SYOSHIFUJI Hideaki 
23337bc570c8SYOSHIFUJI Hideaki 		skb_put(skb2, sizeof(struct ipv6hdr));
23347bc570c8SYOSHIFUJI Hideaki 		skb_reset_network_header(skb2);
23357bc570c8SYOSHIFUJI Hideaki 
23367bc570c8SYOSHIFUJI Hideaki 		iph = ipv6_hdr(skb2);
23377bc570c8SYOSHIFUJI Hideaki 		iph->version = 0;
23387bc570c8SYOSHIFUJI Hideaki 		iph->priority = 0;
23397bc570c8SYOSHIFUJI Hideaki 		iph->flow_lbl[0] = 0;
23407bc570c8SYOSHIFUJI Hideaki 		iph->flow_lbl[1] = 0;
23417bc570c8SYOSHIFUJI Hideaki 		iph->flow_lbl[2] = 0;
23427bc570c8SYOSHIFUJI Hideaki 		iph->payload_len = 0;
23437bc570c8SYOSHIFUJI Hideaki 		iph->nexthdr = IPPROTO_NONE;
23447bc570c8SYOSHIFUJI Hideaki 		iph->hop_limit = 0;
23454e3fd7a0SAlexey Dobriyan 		iph->saddr = rt->rt6i_src.addr;
23464e3fd7a0SAlexey Dobriyan 		iph->daddr = rt->rt6i_dst.addr;
23477bc570c8SYOSHIFUJI Hideaki 
2348e4a38c0cSPatrick Ruddy 		err = ip6mr_cache_unresolved(mrt, vif, skb2, dev);
23496fa40a29SEric Dumazet 		rcu_read_unlock();
23507bc570c8SYOSHIFUJI Hideaki 
23517bc570c8SYOSHIFUJI Hideaki 		return err;
23527bc570c8SYOSHIFUJI Hideaki 	}
23537bc570c8SYOSHIFUJI Hideaki 
23547b0db857SYuval Mintz 	err = mr_fill_mroute(mrt, skb, &cache->_c, rtm);
23556fa40a29SEric Dumazet 	rcu_read_unlock();
23567bc570c8SYOSHIFUJI Hideaki 	return err;
23577bc570c8SYOSHIFUJI Hideaki }
23587bc570c8SYOSHIFUJI Hideaki 
ip6mr_fill_mroute(struct mr_table * mrt,struct sk_buff * skb,u32 portid,u32 seq,struct mfc6_cache * c,int cmd,int flags)2359b70432f7SYuval Mintz static int ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
2360f518338bSNicolas Dichtel 			     u32 portid, u32 seq, struct mfc6_cache *c, int cmd,
2361f518338bSNicolas Dichtel 			     int flags)
23625b285cacSPatrick McHardy {
23635b285cacSPatrick McHardy 	struct nlmsghdr *nlh;
23645b285cacSPatrick McHardy 	struct rtmsg *rtm;
23651eb99af5SNicolas Dichtel 	int err;
23665b285cacSPatrick McHardy 
2367f518338bSNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags);
236863159f29SIan Morris 	if (!nlh)
23695b285cacSPatrick McHardy 		return -EMSGSIZE;
23705b285cacSPatrick McHardy 
23715b285cacSPatrick McHardy 	rtm = nlmsg_data(nlh);
2372193c1e47SNicolas Dichtel 	rtm->rtm_family   = RTNL_FAMILY_IP6MR;
23735b285cacSPatrick McHardy 	rtm->rtm_dst_len  = 128;
23745b285cacSPatrick McHardy 	rtm->rtm_src_len  = 128;
23755b285cacSPatrick McHardy 	rtm->rtm_tos      = 0;
23765b285cacSPatrick McHardy 	rtm->rtm_table    = mrt->id;
2377c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, mrt->id))
2378c78679e8SDavid S. Miller 		goto nla_put_failure;
23791eb99af5SNicolas Dichtel 	rtm->rtm_type = RTN_MULTICAST;
23805b285cacSPatrick McHardy 	rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
2381494fff56SYuval Mintz 	if (c->_c.mfc_flags & MFC_STATIC)
23829a68ac72SNicolas Dichtel 		rtm->rtm_protocol = RTPROT_STATIC;
23839a68ac72SNicolas Dichtel 	else
23849a68ac72SNicolas Dichtel 		rtm->rtm_protocol = RTPROT_MROUTED;
23855b285cacSPatrick McHardy 	rtm->rtm_flags    = 0;
23865b285cacSPatrick McHardy 
2387930345eaSJiri Benc 	if (nla_put_in6_addr(skb, RTA_SRC, &c->mf6c_origin) ||
2388930345eaSJiri Benc 	    nla_put_in6_addr(skb, RTA_DST, &c->mf6c_mcastgrp))
2389c78679e8SDavid S. Miller 		goto nla_put_failure;
23907b0db857SYuval Mintz 	err = mr_fill_mroute(mrt, skb, &c->_c, rtm);
23911eb99af5SNicolas Dichtel 	/* do not break the dump if cache is unresolved */
23921eb99af5SNicolas Dichtel 	if (err < 0 && err != -ENOENT)
23935b285cacSPatrick McHardy 		goto nla_put_failure;
23945b285cacSPatrick McHardy 
2395053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2396053c095aSJohannes Berg 	return 0;
23975b285cacSPatrick McHardy 
23985b285cacSPatrick McHardy nla_put_failure:
23995b285cacSPatrick McHardy 	nlmsg_cancel(skb, nlh);
24005b285cacSPatrick McHardy 	return -EMSGSIZE;
24015b285cacSPatrick McHardy }
24025b285cacSPatrick McHardy 
_ip6mr_fill_mroute(struct mr_table * mrt,struct sk_buff * skb,u32 portid,u32 seq,struct mr_mfc * c,int cmd,int flags)24037b0db857SYuval Mintz static int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
24047b0db857SYuval Mintz 			      u32 portid, u32 seq, struct mr_mfc *c,
24057b0db857SYuval Mintz 			      int cmd, int flags)
24067b0db857SYuval Mintz {
24077b0db857SYuval Mintz 	return ip6mr_fill_mroute(mrt, skb, portid, seq, (struct mfc6_cache *)c,
24087b0db857SYuval Mintz 				 cmd, flags);
24097b0db857SYuval Mintz }
24107b0db857SYuval Mintz 
mr6_msgsize(bool unresolved,int maxvif)2411812e44ddSNicolas Dichtel static int mr6_msgsize(bool unresolved, int maxvif)
2412812e44ddSNicolas Dichtel {
2413812e44ddSNicolas Dichtel 	size_t len =
2414812e44ddSNicolas Dichtel 		NLMSG_ALIGN(sizeof(struct rtmsg))
2415812e44ddSNicolas Dichtel 		+ nla_total_size(4)	/* RTA_TABLE */
2416812e44ddSNicolas Dichtel 		+ nla_total_size(sizeof(struct in6_addr))	/* RTA_SRC */
2417812e44ddSNicolas Dichtel 		+ nla_total_size(sizeof(struct in6_addr))	/* RTA_DST */
2418812e44ddSNicolas Dichtel 		;
2419812e44ddSNicolas Dichtel 
2420812e44ddSNicolas Dichtel 	if (!unresolved)
2421812e44ddSNicolas Dichtel 		len = len
2422812e44ddSNicolas Dichtel 		      + nla_total_size(4)	/* RTA_IIF */
2423812e44ddSNicolas Dichtel 		      + nla_total_size(0)	/* RTA_MULTIPATH */
2424812e44ddSNicolas Dichtel 		      + maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
2425812e44ddSNicolas Dichtel 						/* RTA_MFC_STATS */
24263d6b66c1SNicolas Dichtel 		      + nla_total_size_64bit(sizeof(struct rta_mfc_stats))
2427812e44ddSNicolas Dichtel 		;
2428812e44ddSNicolas Dichtel 
2429812e44ddSNicolas Dichtel 	return len;
2430812e44ddSNicolas Dichtel }
2431812e44ddSNicolas Dichtel 
mr6_netlink_event(struct mr_table * mrt,struct mfc6_cache * mfc,int cmd)2432b70432f7SYuval Mintz static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
2433812e44ddSNicolas Dichtel 			      int cmd)
2434812e44ddSNicolas Dichtel {
2435812e44ddSNicolas Dichtel 	struct net *net = read_pnet(&mrt->net);
2436812e44ddSNicolas Dichtel 	struct sk_buff *skb;
2437812e44ddSNicolas Dichtel 	int err = -ENOBUFS;
2438812e44ddSNicolas Dichtel 
2439494fff56SYuval Mintz 	skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif),
2440812e44ddSNicolas Dichtel 			GFP_ATOMIC);
244163159f29SIan Morris 	if (!skb)
2442812e44ddSNicolas Dichtel 		goto errout;
2443812e44ddSNicolas Dichtel 
2444f518338bSNicolas Dichtel 	err = ip6mr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0);
2445812e44ddSNicolas Dichtel 	if (err < 0)
2446812e44ddSNicolas Dichtel 		goto errout;
2447812e44ddSNicolas Dichtel 
2448812e44ddSNicolas Dichtel 	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE, NULL, GFP_ATOMIC);
2449812e44ddSNicolas Dichtel 	return;
2450812e44ddSNicolas Dichtel 
2451812e44ddSNicolas Dichtel errout:
2452812e44ddSNicolas Dichtel 	kfree_skb(skb);
2453812e44ddSNicolas Dichtel 	if (err < 0)
2454812e44ddSNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err);
2455812e44ddSNicolas Dichtel }
2456812e44ddSNicolas Dichtel 
mrt6msg_netlink_msgsize(size_t payloadlen)2457dd12d15cSJulien Gomes static size_t mrt6msg_netlink_msgsize(size_t payloadlen)
2458dd12d15cSJulien Gomes {
2459dd12d15cSJulien Gomes 	size_t len =
2460dd12d15cSJulien Gomes 		NLMSG_ALIGN(sizeof(struct rtgenmsg))
2461dd12d15cSJulien Gomes 		+ nla_total_size(1)	/* IP6MRA_CREPORT_MSGTYPE */
2462dd12d15cSJulien Gomes 		+ nla_total_size(4)	/* IP6MRA_CREPORT_MIF_ID */
2463dd12d15cSJulien Gomes 					/* IP6MRA_CREPORT_SRC_ADDR */
2464dd12d15cSJulien Gomes 		+ nla_total_size(sizeof(struct in6_addr))
2465dd12d15cSJulien Gomes 					/* IP6MRA_CREPORT_DST_ADDR */
2466dd12d15cSJulien Gomes 		+ nla_total_size(sizeof(struct in6_addr))
2467dd12d15cSJulien Gomes 					/* IP6MRA_CREPORT_PKT */
2468dd12d15cSJulien Gomes 		+ nla_total_size(payloadlen)
2469dd12d15cSJulien Gomes 		;
2470dd12d15cSJulien Gomes 
2471dd12d15cSJulien Gomes 	return len;
2472dd12d15cSJulien Gomes }
2473dd12d15cSJulien Gomes 
mrt6msg_netlink_event(const struct mr_table * mrt,struct sk_buff * pkt)24743493a5b7SEric Dumazet static void mrt6msg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt)
2475dd12d15cSJulien Gomes {
2476dd12d15cSJulien Gomes 	struct net *net = read_pnet(&mrt->net);
2477dd12d15cSJulien Gomes 	struct nlmsghdr *nlh;
2478dd12d15cSJulien Gomes 	struct rtgenmsg *rtgenm;
2479dd12d15cSJulien Gomes 	struct mrt6msg *msg;
2480dd12d15cSJulien Gomes 	struct sk_buff *skb;
2481dd12d15cSJulien Gomes 	struct nlattr *nla;
2482dd12d15cSJulien Gomes 	int payloadlen;
2483dd12d15cSJulien Gomes 
2484dd12d15cSJulien Gomes 	payloadlen = pkt->len - sizeof(struct mrt6msg);
2485dd12d15cSJulien Gomes 	msg = (struct mrt6msg *)skb_transport_header(pkt);
2486dd12d15cSJulien Gomes 
2487dd12d15cSJulien Gomes 	skb = nlmsg_new(mrt6msg_netlink_msgsize(payloadlen), GFP_ATOMIC);
2488dd12d15cSJulien Gomes 	if (!skb)
2489dd12d15cSJulien Gomes 		goto errout;
2490dd12d15cSJulien Gomes 
2491dd12d15cSJulien Gomes 	nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT,
2492dd12d15cSJulien Gomes 			sizeof(struct rtgenmsg), 0);
2493dd12d15cSJulien Gomes 	if (!nlh)
2494dd12d15cSJulien Gomes 		goto errout;
2495dd12d15cSJulien Gomes 	rtgenm = nlmsg_data(nlh);
2496dd12d15cSJulien Gomes 	rtgenm->rtgen_family = RTNL_FAMILY_IP6MR;
2497dd12d15cSJulien Gomes 	if (nla_put_u8(skb, IP6MRA_CREPORT_MSGTYPE, msg->im6_msgtype) ||
2498dd12d15cSJulien Gomes 	    nla_put_u32(skb, IP6MRA_CREPORT_MIF_ID, msg->im6_mif) ||
2499dd12d15cSJulien Gomes 	    nla_put_in6_addr(skb, IP6MRA_CREPORT_SRC_ADDR,
2500dd12d15cSJulien Gomes 			     &msg->im6_src) ||
2501dd12d15cSJulien Gomes 	    nla_put_in6_addr(skb, IP6MRA_CREPORT_DST_ADDR,
2502dd12d15cSJulien Gomes 			     &msg->im6_dst))
2503dd12d15cSJulien Gomes 		goto nla_put_failure;
2504dd12d15cSJulien Gomes 
2505dd12d15cSJulien Gomes 	nla = nla_reserve(skb, IP6MRA_CREPORT_PKT, payloadlen);
2506dd12d15cSJulien Gomes 	if (!nla || skb_copy_bits(pkt, sizeof(struct mrt6msg),
2507dd12d15cSJulien Gomes 				  nla_data(nla), payloadlen))
2508dd12d15cSJulien Gomes 		goto nla_put_failure;
2509dd12d15cSJulien Gomes 
2510dd12d15cSJulien Gomes 	nlmsg_end(skb, nlh);
2511dd12d15cSJulien Gomes 
2512dd12d15cSJulien Gomes 	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE_R, NULL, GFP_ATOMIC);
2513dd12d15cSJulien Gomes 	return;
2514dd12d15cSJulien Gomes 
2515dd12d15cSJulien Gomes nla_put_failure:
2516dd12d15cSJulien Gomes 	nlmsg_cancel(skb, nlh);
2517dd12d15cSJulien Gomes errout:
2518dd12d15cSJulien Gomes 	kfree_skb(skb);
2519dd12d15cSJulien Gomes 	rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE_R, -ENOBUFS);
2520dd12d15cSJulien Gomes }
2521dd12d15cSJulien Gomes 
2522d7c31cbdSDavid Lamparter static const struct nla_policy ip6mr_getroute_policy[RTA_MAX + 1] = {
2523d7c31cbdSDavid Lamparter 	[RTA_SRC]		= NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
2524d7c31cbdSDavid Lamparter 	[RTA_DST]		= NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
2525d7c31cbdSDavid Lamparter 	[RTA_TABLE]		= { .type = NLA_U32 },
2526d7c31cbdSDavid Lamparter };
2527d7c31cbdSDavid Lamparter 
ip6mr_rtm_valid_getroute_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)2528d7c31cbdSDavid Lamparter static int ip6mr_rtm_valid_getroute_req(struct sk_buff *skb,
2529d7c31cbdSDavid Lamparter 					const struct nlmsghdr *nlh,
2530d7c31cbdSDavid Lamparter 					struct nlattr **tb,
2531d7c31cbdSDavid Lamparter 					struct netlink_ext_ack *extack)
2532d7c31cbdSDavid Lamparter {
2533d7c31cbdSDavid Lamparter 	struct rtmsg *rtm;
2534d7c31cbdSDavid Lamparter 	int err;
2535d7c31cbdSDavid Lamparter 
2536d7c31cbdSDavid Lamparter 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, ip6mr_getroute_policy,
2537d7c31cbdSDavid Lamparter 			  extack);
2538d7c31cbdSDavid Lamparter 	if (err)
2539d7c31cbdSDavid Lamparter 		return err;
2540d7c31cbdSDavid Lamparter 
2541d7c31cbdSDavid Lamparter 	rtm = nlmsg_data(nlh);
2542d7c31cbdSDavid Lamparter 	if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
2543d7c31cbdSDavid Lamparter 	    (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
2544d7c31cbdSDavid Lamparter 	    rtm->rtm_tos || rtm->rtm_table || rtm->rtm_protocol ||
2545d7c31cbdSDavid Lamparter 	    rtm->rtm_scope || rtm->rtm_type || rtm->rtm_flags) {
2546d7c31cbdSDavid Lamparter 		NL_SET_ERR_MSG_MOD(extack,
2547d7c31cbdSDavid Lamparter 				   "Invalid values in header for multicast route get request");
2548d7c31cbdSDavid Lamparter 		return -EINVAL;
2549d7c31cbdSDavid Lamparter 	}
2550d7c31cbdSDavid Lamparter 
2551d7c31cbdSDavid Lamparter 	if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
2552d7c31cbdSDavid Lamparter 	    (tb[RTA_DST] && !rtm->rtm_dst_len)) {
2553d7c31cbdSDavid Lamparter 		NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
2554d7c31cbdSDavid Lamparter 		return -EINVAL;
2555d7c31cbdSDavid Lamparter 	}
2556d7c31cbdSDavid Lamparter 
2557d7c31cbdSDavid Lamparter 	return 0;
2558d7c31cbdSDavid Lamparter }
2559d7c31cbdSDavid Lamparter 
ip6mr_rtm_getroute(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)2560d7c31cbdSDavid Lamparter static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
2561d7c31cbdSDavid Lamparter 			      struct netlink_ext_ack *extack)
2562d7c31cbdSDavid Lamparter {
2563d7c31cbdSDavid Lamparter 	struct net *net = sock_net(in_skb->sk);
2564d7c31cbdSDavid Lamparter 	struct in6_addr src = {}, grp = {};
2565d7c31cbdSDavid Lamparter 	struct nlattr *tb[RTA_MAX + 1];
2566d7c31cbdSDavid Lamparter 	struct mfc6_cache *cache;
2567d7c31cbdSDavid Lamparter 	struct mr_table *mrt;
2568d7c31cbdSDavid Lamparter 	struct sk_buff *skb;
2569d7c31cbdSDavid Lamparter 	u32 tableid;
2570d7c31cbdSDavid Lamparter 	int err;
2571d7c31cbdSDavid Lamparter 
2572d7c31cbdSDavid Lamparter 	err = ip6mr_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
2573d7c31cbdSDavid Lamparter 	if (err < 0)
2574d7c31cbdSDavid Lamparter 		return err;
2575d7c31cbdSDavid Lamparter 
2576d7c31cbdSDavid Lamparter 	if (tb[RTA_SRC])
2577d7c31cbdSDavid Lamparter 		src = nla_get_in6_addr(tb[RTA_SRC]);
2578d7c31cbdSDavid Lamparter 	if (tb[RTA_DST])
2579d7c31cbdSDavid Lamparter 		grp = nla_get_in6_addr(tb[RTA_DST]);
2580d7c31cbdSDavid Lamparter 	tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0;
2581d7c31cbdSDavid Lamparter 
2582bba7909bSPaolo Abeni 	mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
2583d7c31cbdSDavid Lamparter 	if (!mrt) {
2584d7c31cbdSDavid Lamparter 		NL_SET_ERR_MSG_MOD(extack, "MR table does not exist");
2585d7c31cbdSDavid Lamparter 		return -ENOENT;
2586d7c31cbdSDavid Lamparter 	}
2587d7c31cbdSDavid Lamparter 
2588d7c31cbdSDavid Lamparter 	/* entries are added/deleted only under RTNL */
2589d7c31cbdSDavid Lamparter 	rcu_read_lock();
2590d7c31cbdSDavid Lamparter 	cache = ip6mr_cache_find(mrt, &src, &grp);
2591d7c31cbdSDavid Lamparter 	rcu_read_unlock();
2592d7c31cbdSDavid Lamparter 	if (!cache) {
2593d7c31cbdSDavid Lamparter 		NL_SET_ERR_MSG_MOD(extack, "MR cache entry not found");
2594d7c31cbdSDavid Lamparter 		return -ENOENT;
2595d7c31cbdSDavid Lamparter 	}
2596d7c31cbdSDavid Lamparter 
2597d7c31cbdSDavid Lamparter 	skb = nlmsg_new(mr6_msgsize(false, mrt->maxvif), GFP_KERNEL);
2598d7c31cbdSDavid Lamparter 	if (!skb)
2599d7c31cbdSDavid Lamparter 		return -ENOBUFS;
2600d7c31cbdSDavid Lamparter 
2601d7c31cbdSDavid Lamparter 	err = ip6mr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid,
2602d7c31cbdSDavid Lamparter 				nlh->nlmsg_seq, cache, RTM_NEWROUTE, 0);
2603d7c31cbdSDavid Lamparter 	if (err < 0) {
2604d7c31cbdSDavid Lamparter 		kfree_skb(skb);
2605d7c31cbdSDavid Lamparter 		return err;
2606d7c31cbdSDavid Lamparter 	}
2607d7c31cbdSDavid Lamparter 
2608d7c31cbdSDavid Lamparter 	return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
2609d7c31cbdSDavid Lamparter }
2610d7c31cbdSDavid Lamparter 
ip6mr_rtm_dumproute(struct sk_buff * skb,struct netlink_callback * cb)26115b285cacSPatrick McHardy static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
26125b285cacSPatrick McHardy {
2613e8ba330aSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
26144724676dSDavid Ahern 	struct fib_dump_filter filter = {};
26154724676dSDavid Ahern 	int err;
2616e8ba330aSDavid Ahern 
2617cb167893SDavid Ahern 	if (cb->strict_check) {
26184724676dSDavid Ahern 		err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh,
2619effe6792SDavid Ahern 					    &filter, cb);
2620e8ba330aSDavid Ahern 		if (err < 0)
2621e8ba330aSDavid Ahern 			return err;
2622e8ba330aSDavid Ahern 	}
2623e8ba330aSDavid Ahern 
2624cb167893SDavid Ahern 	if (filter.table_id) {
2625cb167893SDavid Ahern 		struct mr_table *mrt;
2626cb167893SDavid Ahern 
2627bba7909bSPaolo Abeni 		mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id);
2628cb167893SDavid Ahern 		if (!mrt) {
262941b4bd98SSabrina Dubroca 			if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR)
2630ae677bbbSDavid Ahern 				return skb->len;
2631ae677bbbSDavid Ahern 
2632cb167893SDavid Ahern 			NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist");
2633cb167893SDavid Ahern 			return -ENOENT;
2634cb167893SDavid Ahern 		}
2635cb167893SDavid Ahern 		err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute,
2636cb167893SDavid Ahern 				    &mfc_unres_lock, &filter);
2637cb167893SDavid Ahern 		return skb->len ? : err;
2638cb167893SDavid Ahern 	}
2639cb167893SDavid Ahern 
26407b0db857SYuval Mintz 	return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
2641cb167893SDavid Ahern 				_ip6mr_fill_mroute, &mfc_unres_lock, &filter);
26425b285cacSPatrick McHardy }
2643