18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Linux IPv6 multicast routing support for BSD pim6sd
48c2ecf20Sopenharmony_ci *	Based on net/ipv4/ipmr.c.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	(c) 2004 Mickael Hoerdt, <hoerdt@clarinet.u-strasbg.fr>
78c2ecf20Sopenharmony_ci *		LSIIT Laboratory, Strasbourg, France
88c2ecf20Sopenharmony_ci *	(c) 2004 Jean-Philippe Andriot, <jean-philippe.andriot@6WIND.com>
98c2ecf20Sopenharmony_ci *		6WIND, Paris, France
108c2ecf20Sopenharmony_ci *	Copyright (C)2007,2008 USAGI/WIDE Project
118c2ecf20Sopenharmony_ci *		YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci#include <linux/types.h>
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/mm.h>
198c2ecf20Sopenharmony_ci#include <linux/kernel.h>
208c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
218c2ecf20Sopenharmony_ci#include <linux/stat.h>
228c2ecf20Sopenharmony_ci#include <linux/socket.h>
238c2ecf20Sopenharmony_ci#include <linux/inet.h>
248c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
258c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
268c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
278c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
288c2ecf20Sopenharmony_ci#include <linux/init.h>
298c2ecf20Sopenharmony_ci#include <linux/compat.h>
308c2ecf20Sopenharmony_ci#include <linux/rhashtable.h>
318c2ecf20Sopenharmony_ci#include <net/protocol.h>
328c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
338c2ecf20Sopenharmony_ci#include <net/raw.h>
348c2ecf20Sopenharmony_ci#include <linux/notifier.h>
358c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
368c2ecf20Sopenharmony_ci#include <net/checksum.h>
378c2ecf20Sopenharmony_ci#include <net/netlink.h>
388c2ecf20Sopenharmony_ci#include <net/fib_rules.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <net/ipv6.h>
418c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
428c2ecf20Sopenharmony_ci#include <linux/mroute6.h>
438c2ecf20Sopenharmony_ci#include <linux/pim.h>
448c2ecf20Sopenharmony_ci#include <net/addrconf.h>
458c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv6.h>
468c2ecf20Sopenharmony_ci#include <linux/export.h>
478c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h>
488c2ecf20Sopenharmony_ci#include <linux/netconf.h>
498c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h>
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#include <linux/nospec.h>
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct ip6mr_rule {
548c2ecf20Sopenharmony_ci	struct fib_rule		common;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct ip6mr_result {
588c2ecf20Sopenharmony_ci	struct mr_table	*mrt;
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Big lock, protecting vif table, mrt cache and mroute socket state.
628c2ecf20Sopenharmony_ci   Note that the changes are semaphored via rtnl_lock.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(mrt_lock);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* Multicast router control variables */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* Special spinlock for queue of unresolved entries */
708c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mfc_unres_lock);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* We return to original Alan's scheme. Hash table of resolved
738c2ecf20Sopenharmony_ci   entries is changed only in process context and protected
748c2ecf20Sopenharmony_ci   with weak lock mrt_lock. Queue of unresolved entries is protected
758c2ecf20Sopenharmony_ci   with strong spinlock mfc_unres_lock.
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci   In this case data path is free of exclusive locks at all.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct kmem_cache *mrt_cachep __read_mostly;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_new_table(struct net *net, u32 id);
838c2ecf20Sopenharmony_cistatic void ip6mr_free_table(struct mr_table *mrt);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void ip6_mr_forward(struct net *net, struct mr_table *mrt,
868c2ecf20Sopenharmony_ci			   struct net_device *dev, struct sk_buff *skb,
878c2ecf20Sopenharmony_ci			   struct mfc6_cache *cache);
888c2ecf20Sopenharmony_cistatic int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt,
898c2ecf20Sopenharmony_ci			      mifi_t mifi, int assert);
908c2ecf20Sopenharmony_cistatic void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
918c2ecf20Sopenharmony_ci			      int cmd);
928c2ecf20Sopenharmony_cistatic void mrt6msg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt);
938c2ecf20Sopenharmony_cistatic int ip6mr_rtm_dumproute(struct sk_buff *skb,
948c2ecf20Sopenharmony_ci			       struct netlink_callback *cb);
958c2ecf20Sopenharmony_cistatic void mroute_clean_tables(struct mr_table *mrt, int flags);
968c2ecf20Sopenharmony_cistatic void ipmr_expire_process(struct timer_list *t);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
998c2ecf20Sopenharmony_ci#define ip6mr_for_each_table(mrt, net) \
1008c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list, \
1018c2ecf20Sopenharmony_ci				lockdep_rtnl_is_held() || \
1028c2ecf20Sopenharmony_ci				list_empty(&net->ipv6.mr6_tables))
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_mr_table_iter(struct net *net,
1058c2ecf20Sopenharmony_ci					    struct mr_table *mrt)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct mr_table *ret;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (!mrt)
1108c2ecf20Sopenharmony_ci		ret = list_entry_rcu(net->ipv6.mr6_tables.next,
1118c2ecf20Sopenharmony_ci				     struct mr_table, list);
1128c2ecf20Sopenharmony_ci	else
1138c2ecf20Sopenharmony_ci		ret = list_entry_rcu(mrt->list.next,
1148c2ecf20Sopenharmony_ci				     struct mr_table, list);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (&ret->list == &net->ipv6.mr6_tables)
1178c2ecf20Sopenharmony_ci		return NULL;
1188c2ecf20Sopenharmony_ci	return ret;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_get_table(struct net *net, u32 id)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct mr_table *mrt;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	ip6mr_for_each_table(mrt, net) {
1268c2ecf20Sopenharmony_ci		if (mrt->id == id)
1278c2ecf20Sopenharmony_ci			return mrt;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	return NULL;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
1338c2ecf20Sopenharmony_ci			    struct mr_table **mrt)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	int err;
1368c2ecf20Sopenharmony_ci	struct ip6mr_result res;
1378c2ecf20Sopenharmony_ci	struct fib_lookup_arg arg = {
1388c2ecf20Sopenharmony_ci		.result = &res,
1398c2ecf20Sopenharmony_ci		.flags = FIB_LOOKUP_NOREF,
1408c2ecf20Sopenharmony_ci	};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* update flow if oif or iif point to device enslaved to l3mdev */
1438c2ecf20Sopenharmony_ci	l3mdev_update_flow(net, flowi6_to_flowi(flp6));
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	err = fib_rules_lookup(net->ipv6.mr6_rules_ops,
1468c2ecf20Sopenharmony_ci			       flowi6_to_flowi(flp6), 0, &arg);
1478c2ecf20Sopenharmony_ci	if (err < 0)
1488c2ecf20Sopenharmony_ci		return err;
1498c2ecf20Sopenharmony_ci	*mrt = res.mrt;
1508c2ecf20Sopenharmony_ci	return 0;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp,
1548c2ecf20Sopenharmony_ci			     int flags, struct fib_lookup_arg *arg)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct ip6mr_result *res = arg->result;
1578c2ecf20Sopenharmony_ci	struct mr_table *mrt;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	switch (rule->action) {
1608c2ecf20Sopenharmony_ci	case FR_ACT_TO_TBL:
1618c2ecf20Sopenharmony_ci		break;
1628c2ecf20Sopenharmony_ci	case FR_ACT_UNREACHABLE:
1638c2ecf20Sopenharmony_ci		return -ENETUNREACH;
1648c2ecf20Sopenharmony_ci	case FR_ACT_PROHIBIT:
1658c2ecf20Sopenharmony_ci		return -EACCES;
1668c2ecf20Sopenharmony_ci	case FR_ACT_BLACKHOLE:
1678c2ecf20Sopenharmony_ci	default:
1688c2ecf20Sopenharmony_ci		return -EINVAL;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	arg->table = fib_rule_get_table(rule, arg);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(rule->fr_net, arg->table);
1748c2ecf20Sopenharmony_ci	if (!mrt)
1758c2ecf20Sopenharmony_ci		return -EAGAIN;
1768c2ecf20Sopenharmony_ci	res->mrt = mrt;
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int ip6mr_rule_match(struct fib_rule *rule, struct flowi *flp, int flags)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	return 1;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic const struct nla_policy ip6mr_rule_policy[FRA_MAX + 1] = {
1868c2ecf20Sopenharmony_ci	FRA_GENERIC_POLICY,
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
1908c2ecf20Sopenharmony_ci				struct fib_rule_hdr *frh, struct nlattr **tb,
1918c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int ip6mr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
1978c2ecf20Sopenharmony_ci			      struct nlattr **tb)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	return 1;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int ip6mr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
2038c2ecf20Sopenharmony_ci			   struct fib_rule_hdr *frh)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	frh->dst_len = 0;
2068c2ecf20Sopenharmony_ci	frh->src_len = 0;
2078c2ecf20Sopenharmony_ci	frh->tos     = 0;
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = {
2128c2ecf20Sopenharmony_ci	.family		= RTNL_FAMILY_IP6MR,
2138c2ecf20Sopenharmony_ci	.rule_size	= sizeof(struct ip6mr_rule),
2148c2ecf20Sopenharmony_ci	.addr_size	= sizeof(struct in6_addr),
2158c2ecf20Sopenharmony_ci	.action		= ip6mr_rule_action,
2168c2ecf20Sopenharmony_ci	.match		= ip6mr_rule_match,
2178c2ecf20Sopenharmony_ci	.configure	= ip6mr_rule_configure,
2188c2ecf20Sopenharmony_ci	.compare	= ip6mr_rule_compare,
2198c2ecf20Sopenharmony_ci	.fill		= ip6mr_rule_fill,
2208c2ecf20Sopenharmony_ci	.nlgroup	= RTNLGRP_IPV6_RULE,
2218c2ecf20Sopenharmony_ci	.policy		= ip6mr_rule_policy,
2228c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2238c2ecf20Sopenharmony_ci};
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int __net_init ip6mr_rules_init(struct net *net)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct fib_rules_ops *ops;
2288c2ecf20Sopenharmony_ci	struct mr_table *mrt;
2298c2ecf20Sopenharmony_ci	int err;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	ops = fib_rules_register(&ip6mr_rules_ops_template, net);
2328c2ecf20Sopenharmony_ci	if (IS_ERR(ops))
2338c2ecf20Sopenharmony_ci		return PTR_ERR(ops);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.mr6_tables);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
2388c2ecf20Sopenharmony_ci	if (IS_ERR(mrt)) {
2398c2ecf20Sopenharmony_ci		err = PTR_ERR(mrt);
2408c2ecf20Sopenharmony_ci		goto err1;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	err = fib_default_rule_add(ops, 0x7fff, RT6_TABLE_DFLT, 0);
2448c2ecf20Sopenharmony_ci	if (err < 0)
2458c2ecf20Sopenharmony_ci		goto err2;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	net->ipv6.mr6_rules_ops = ops;
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cierr2:
2518c2ecf20Sopenharmony_ci	rtnl_lock();
2528c2ecf20Sopenharmony_ci	ip6mr_free_table(mrt);
2538c2ecf20Sopenharmony_ci	rtnl_unlock();
2548c2ecf20Sopenharmony_cierr1:
2558c2ecf20Sopenharmony_ci	fib_rules_unregister(ops);
2568c2ecf20Sopenharmony_ci	return err;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_rules_exit(struct net *net)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct mr_table *mrt, *next;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	rtnl_lock();
2648c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) {
2658c2ecf20Sopenharmony_ci		list_del(&mrt->list);
2668c2ecf20Sopenharmony_ci		ip6mr_free_table(mrt);
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci	fib_rules_unregister(net->ipv6.mr6_rules_ops);
2698c2ecf20Sopenharmony_ci	rtnl_unlock();
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
2738c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR, extack);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic unsigned int ip6mr_rules_seq_read(struct net *net)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	return fib_rules_seq_read(net, RTNL_FAMILY_IP6MR);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cibool ip6mr_rule_default(const struct fib_rule *rule)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	return fib_rule_matchall(rule) && rule->action == FR_ACT_TO_TBL &&
2868c2ecf20Sopenharmony_ci	       rule->table == RT6_TABLE_DFLT && !rule->l3mdev;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip6mr_rule_default);
2898c2ecf20Sopenharmony_ci#else
2908c2ecf20Sopenharmony_ci#define ip6mr_for_each_table(mrt, net) \
2918c2ecf20Sopenharmony_ci	for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_mr_table_iter(struct net *net,
2948c2ecf20Sopenharmony_ci					    struct mr_table *mrt)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	if (!mrt)
2978c2ecf20Sopenharmony_ci		return net->ipv6.mrt6;
2988c2ecf20Sopenharmony_ci	return NULL;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_get_table(struct net *net, u32 id)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	return net->ipv6.mrt6;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
3078c2ecf20Sopenharmony_ci			    struct mr_table **mrt)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	*mrt = net->ipv6.mrt6;
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int __net_init ip6mr_rules_init(struct net *net)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct mr_table *mrt;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
3188c2ecf20Sopenharmony_ci	if (IS_ERR(mrt))
3198c2ecf20Sopenharmony_ci		return PTR_ERR(mrt);
3208c2ecf20Sopenharmony_ci	net->ipv6.mrt6 = mrt;
3218c2ecf20Sopenharmony_ci	return 0;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_rules_exit(struct net *net)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	rtnl_lock();
3278c2ecf20Sopenharmony_ci	ip6mr_free_table(net->ipv6.mrt6);
3288c2ecf20Sopenharmony_ci	net->ipv6.mrt6 = NULL;
3298c2ecf20Sopenharmony_ci	rtnl_unlock();
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
3338c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	return 0;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic unsigned int ip6mr_rules_seq_read(struct net *net)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	return 0;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci#endif
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg,
3458c2ecf20Sopenharmony_ci			  const void *ptr)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	const struct mfc6_cache_cmp_arg *cmparg = arg->key;
3488c2ecf20Sopenharmony_ci	struct mfc6_cache *c = (struct mfc6_cache *)ptr;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return !ipv6_addr_equal(&c->mf6c_mcastgrp, &cmparg->mf6c_mcastgrp) ||
3518c2ecf20Sopenharmony_ci	       !ipv6_addr_equal(&c->mf6c_origin, &cmparg->mf6c_origin);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic const struct rhashtable_params ip6mr_rht_params = {
3558c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct mr_mfc, mnode),
3568c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct mfc6_cache, cmparg),
3578c2ecf20Sopenharmony_ci	.key_len = sizeof(struct mfc6_cache_cmp_arg),
3588c2ecf20Sopenharmony_ci	.nelem_hint = 3,
3598c2ecf20Sopenharmony_ci	.obj_cmpfn = ip6mr_hash_cmp,
3608c2ecf20Sopenharmony_ci	.automatic_shrinking = true,
3618c2ecf20Sopenharmony_ci};
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void ip6mr_new_table_set(struct mr_table *mrt,
3648c2ecf20Sopenharmony_ci				struct net *net)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
3678c2ecf20Sopenharmony_ci	list_add_tail_rcu(&mrt->list, &net->ipv6.mr6_tables);
3688c2ecf20Sopenharmony_ci#endif
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic struct mfc6_cache_cmp_arg ip6mr_mr_table_ops_cmparg_any = {
3728c2ecf20Sopenharmony_ci	.mf6c_origin = IN6ADDR_ANY_INIT,
3738c2ecf20Sopenharmony_ci	.mf6c_mcastgrp = IN6ADDR_ANY_INIT,
3748c2ecf20Sopenharmony_ci};
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic struct mr_table_ops ip6mr_mr_table_ops = {
3778c2ecf20Sopenharmony_ci	.rht_params = &ip6mr_rht_params,
3788c2ecf20Sopenharmony_ci	.cmparg_any = &ip6mr_mr_table_ops_cmparg_any,
3798c2ecf20Sopenharmony_ci};
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_new_table(struct net *net, u32 id)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct mr_table *mrt;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, id);
3868c2ecf20Sopenharmony_ci	if (mrt)
3878c2ecf20Sopenharmony_ci		return mrt;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return mr_table_alloc(net, id, &ip6mr_mr_table_ops,
3908c2ecf20Sopenharmony_ci			      ipmr_expire_process, ip6mr_new_table_set);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic void ip6mr_free_table(struct mr_table *mrt)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	del_timer_sync(&mrt->ipmr_expire_timer);
3968c2ecf20Sopenharmony_ci	mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
3978c2ecf20Sopenharmony_ci				 MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC);
3988c2ecf20Sopenharmony_ci	rhltable_destroy(&mrt->mfc_hash);
3998c2ecf20Sopenharmony_ci	kfree(mrt);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
4038c2ecf20Sopenharmony_ci/* The /proc interfaces to multicast routing
4048c2ecf20Sopenharmony_ci * /proc/ip6_mr_cache /proc/ip6_mr_vif
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
4088c2ecf20Sopenharmony_ci	__acquires(mrt_lock)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct mr_vif_iter *iter = seq->private;
4118c2ecf20Sopenharmony_ci	struct net *net = seq_file_net(seq);
4128c2ecf20Sopenharmony_ci	struct mr_table *mrt;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
4158c2ecf20Sopenharmony_ci	if (!mrt)
4168c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	iter->mrt = mrt;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	read_lock(&mrt_lock);
4218c2ecf20Sopenharmony_ci	return mr_vif_seq_start(seq, pos);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic void ip6mr_vif_seq_stop(struct seq_file *seq, void *v)
4258c2ecf20Sopenharmony_ci	__releases(mrt_lock)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	read_unlock(&mrt_lock);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct mr_vif_iter *iter = seq->private;
4338c2ecf20Sopenharmony_ci	struct mr_table *mrt = iter->mrt;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
4368c2ecf20Sopenharmony_ci		seq_puts(seq,
4378c2ecf20Sopenharmony_ci			 "Interface      BytesIn  PktsIn  BytesOut PktsOut Flags\n");
4388c2ecf20Sopenharmony_ci	} else {
4398c2ecf20Sopenharmony_ci		const struct vif_device *vif = v;
4408c2ecf20Sopenharmony_ci		const char *name = vif->dev ? vif->dev->name : "none";
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		seq_printf(seq,
4438c2ecf20Sopenharmony_ci			   "%2td %-10s %8ld %7ld  %8ld %7ld %05X\n",
4448c2ecf20Sopenharmony_ci			   vif - mrt->vif_table,
4458c2ecf20Sopenharmony_ci			   name, vif->bytes_in, vif->pkt_in,
4468c2ecf20Sopenharmony_ci			   vif->bytes_out, vif->pkt_out,
4478c2ecf20Sopenharmony_ci			   vif->flags);
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic const struct seq_operations ip6mr_vif_seq_ops = {
4538c2ecf20Sopenharmony_ci	.start = ip6mr_vif_seq_start,
4548c2ecf20Sopenharmony_ci	.next  = mr_vif_seq_next,
4558c2ecf20Sopenharmony_ci	.stop  = ip6mr_vif_seq_stop,
4568c2ecf20Sopenharmony_ci	.show  = ip6mr_vif_seq_show,
4578c2ecf20Sopenharmony_ci};
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct net *net = seq_file_net(seq);
4628c2ecf20Sopenharmony_ci	struct mr_table *mrt;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
4658c2ecf20Sopenharmony_ci	if (!mrt)
4668c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return mr_mfc_seq_start(seq, pos, mrt, &mfc_unres_lock);
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	int n;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
4768c2ecf20Sopenharmony_ci		seq_puts(seq,
4778c2ecf20Sopenharmony_ci			 "Group                            "
4788c2ecf20Sopenharmony_ci			 "Origin                           "
4798c2ecf20Sopenharmony_ci			 "Iif      Pkts  Bytes     Wrong  Oifs\n");
4808c2ecf20Sopenharmony_ci	} else {
4818c2ecf20Sopenharmony_ci		const struct mfc6_cache *mfc = v;
4828c2ecf20Sopenharmony_ci		const struct mr_mfc_iter *it = seq->private;
4838c2ecf20Sopenharmony_ci		struct mr_table *mrt = it->mrt;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		seq_printf(seq, "%pI6 %pI6 %-3hd",
4868c2ecf20Sopenharmony_ci			   &mfc->mf6c_mcastgrp, &mfc->mf6c_origin,
4878c2ecf20Sopenharmony_ci			   mfc->_c.mfc_parent);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci		if (it->cache != &mrt->mfc_unres_queue) {
4908c2ecf20Sopenharmony_ci			seq_printf(seq, " %8lu %8lu %8lu",
4918c2ecf20Sopenharmony_ci				   mfc->_c.mfc_un.res.pkt,
4928c2ecf20Sopenharmony_ci				   mfc->_c.mfc_un.res.bytes,
4938c2ecf20Sopenharmony_ci				   mfc->_c.mfc_un.res.wrong_if);
4948c2ecf20Sopenharmony_ci			for (n = mfc->_c.mfc_un.res.minvif;
4958c2ecf20Sopenharmony_ci			     n < mfc->_c.mfc_un.res.maxvif; n++) {
4968c2ecf20Sopenharmony_ci				if (VIF_EXISTS(mrt, n) &&
4978c2ecf20Sopenharmony_ci				    mfc->_c.mfc_un.res.ttls[n] < 255)
4988c2ecf20Sopenharmony_ci					seq_printf(seq,
4998c2ecf20Sopenharmony_ci						   " %2d:%-3d", n,
5008c2ecf20Sopenharmony_ci						   mfc->_c.mfc_un.res.ttls[n]);
5018c2ecf20Sopenharmony_ci			}
5028c2ecf20Sopenharmony_ci		} else {
5038c2ecf20Sopenharmony_ci			/* unresolved mfc_caches don't contain
5048c2ecf20Sopenharmony_ci			 * pkt, bytes and wrong_if values
5058c2ecf20Sopenharmony_ci			 */
5068c2ecf20Sopenharmony_ci			seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci		seq_putc(seq, '\n');
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci	return 0;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic const struct seq_operations ipmr_mfc_seq_ops = {
5148c2ecf20Sopenharmony_ci	.start = ipmr_mfc_seq_start,
5158c2ecf20Sopenharmony_ci	.next  = mr_mfc_seq_next,
5168c2ecf20Sopenharmony_ci	.stop  = mr_mfc_seq_stop,
5178c2ecf20Sopenharmony_ci	.show  = ipmr_mfc_seq_show,
5188c2ecf20Sopenharmony_ci};
5198c2ecf20Sopenharmony_ci#endif
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic int pim6_rcv(struct sk_buff *skb)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct pimreghdr *pim;
5268c2ecf20Sopenharmony_ci	struct ipv6hdr   *encap;
5278c2ecf20Sopenharmony_ci	struct net_device  *reg_dev = NULL;
5288c2ecf20Sopenharmony_ci	struct net *net = dev_net(skb->dev);
5298c2ecf20Sopenharmony_ci	struct mr_table *mrt;
5308c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
5318c2ecf20Sopenharmony_ci		.flowi6_iif	= skb->dev->ifindex,
5328c2ecf20Sopenharmony_ci		.flowi6_mark	= skb->mark,
5338c2ecf20Sopenharmony_ci	};
5348c2ecf20Sopenharmony_ci	int reg_vif_num;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
5378c2ecf20Sopenharmony_ci		goto drop;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	pim = (struct pimreghdr *)skb_transport_header(skb);
5408c2ecf20Sopenharmony_ci	if (pim->type != ((PIM_VERSION << 4) | PIM_TYPE_REGISTER) ||
5418c2ecf20Sopenharmony_ci	    (pim->flags & PIM_NULL_REGISTER) ||
5428c2ecf20Sopenharmony_ci	    (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
5438c2ecf20Sopenharmony_ci			     sizeof(*pim), IPPROTO_PIM,
5448c2ecf20Sopenharmony_ci			     csum_partial((void *)pim, sizeof(*pim), 0)) &&
5458c2ecf20Sopenharmony_ci	     csum_fold(skb_checksum(skb, 0, skb->len, 0))))
5468c2ecf20Sopenharmony_ci		goto drop;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* check if the inner packet is destined to mcast group */
5498c2ecf20Sopenharmony_ci	encap = (struct ipv6hdr *)(skb_transport_header(skb) +
5508c2ecf20Sopenharmony_ci				   sizeof(*pim));
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (!ipv6_addr_is_multicast(&encap->daddr) ||
5538c2ecf20Sopenharmony_ci	    encap->payload_len == 0 ||
5548c2ecf20Sopenharmony_ci	    ntohs(encap->payload_len) + sizeof(*pim) > skb->len)
5558c2ecf20Sopenharmony_ci		goto drop;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
5588c2ecf20Sopenharmony_ci		goto drop;
5598c2ecf20Sopenharmony_ci	reg_vif_num = mrt->mroute_reg_vif_num;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	read_lock(&mrt_lock);
5628c2ecf20Sopenharmony_ci	if (reg_vif_num >= 0)
5638c2ecf20Sopenharmony_ci		reg_dev = mrt->vif_table[reg_vif_num].dev;
5648c2ecf20Sopenharmony_ci	if (reg_dev)
5658c2ecf20Sopenharmony_ci		dev_hold(reg_dev);
5668c2ecf20Sopenharmony_ci	read_unlock(&mrt_lock);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!reg_dev)
5698c2ecf20Sopenharmony_ci		goto drop;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	skb->mac_header = skb->network_header;
5728c2ecf20Sopenharmony_ci	skb_pull(skb, (u8 *)encap - skb->data);
5738c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
5748c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
5758c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	netif_rx(skb);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	dev_put(reg_dev);
5828c2ecf20Sopenharmony_ci	return 0;
5838c2ecf20Sopenharmony_ci drop:
5848c2ecf20Sopenharmony_ci	kfree_skb(skb);
5858c2ecf20Sopenharmony_ci	return 0;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic const struct inet6_protocol pim6_protocol = {
5898c2ecf20Sopenharmony_ci	.handler	=	pim6_rcv,
5908c2ecf20Sopenharmony_ci};
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/* Service routines creating virtual interfaces: PIMREG */
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic netdev_tx_t reg_vif_xmit(struct sk_buff *skb,
5958c2ecf20Sopenharmony_ci				      struct net_device *dev)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
5988c2ecf20Sopenharmony_ci	struct mr_table *mrt;
5998c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
6008c2ecf20Sopenharmony_ci		.flowi6_oif	= dev->ifindex,
6018c2ecf20Sopenharmony_ci		.flowi6_iif	= skb->skb_iif ? : LOOPBACK_IFINDEX,
6028c2ecf20Sopenharmony_ci		.flowi6_mark	= skb->mark,
6038c2ecf20Sopenharmony_ci	};
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
6068c2ecf20Sopenharmony_ci		goto tx_err;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
6098c2ecf20Sopenharmony_ci		goto tx_err;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	read_lock(&mrt_lock);
6128c2ecf20Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
6138c2ecf20Sopenharmony_ci	dev->stats.tx_packets++;
6148c2ecf20Sopenharmony_ci	ip6mr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, MRT6MSG_WHOLEPKT);
6158c2ecf20Sopenharmony_ci	read_unlock(&mrt_lock);
6168c2ecf20Sopenharmony_ci	kfree_skb(skb);
6178c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_citx_err:
6208c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
6218c2ecf20Sopenharmony_ci	kfree_skb(skb);
6228c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int reg_vif_get_iflink(const struct net_device *dev)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic const struct net_device_ops reg_vif_netdev_ops = {
6318c2ecf20Sopenharmony_ci	.ndo_start_xmit	= reg_vif_xmit,
6328c2ecf20Sopenharmony_ci	.ndo_get_iflink = reg_vif_get_iflink,
6338c2ecf20Sopenharmony_ci};
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic void reg_vif_setup(struct net_device *dev)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	dev->type		= ARPHRD_PIMREG;
6388c2ecf20Sopenharmony_ci	dev->mtu		= 1500 - sizeof(struct ipv6hdr) - 8;
6398c2ecf20Sopenharmony_ci	dev->flags		= IFF_NOARP;
6408c2ecf20Sopenharmony_ci	dev->netdev_ops		= &reg_vif_netdev_ops;
6418c2ecf20Sopenharmony_ci	dev->needs_free_netdev	= true;
6428c2ecf20Sopenharmony_ci	dev->features		|= NETIF_F_NETNS_LOCAL;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	struct net_device *dev;
6488c2ecf20Sopenharmony_ci	char name[IFNAMSIZ];
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	if (mrt->id == RT6_TABLE_DFLT)
6518c2ecf20Sopenharmony_ci		sprintf(name, "pim6reg");
6528c2ecf20Sopenharmony_ci	else
6538c2ecf20Sopenharmony_ci		sprintf(name, "pim6reg%u", mrt->id);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup);
6568c2ecf20Sopenharmony_ci	if (!dev)
6578c2ecf20Sopenharmony_ci		return NULL;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	dev_net_set(dev, net);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (register_netdevice(dev)) {
6628c2ecf20Sopenharmony_ci		free_netdev(dev);
6638c2ecf20Sopenharmony_ci		return NULL;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (dev_open(dev, NULL))
6678c2ecf20Sopenharmony_ci		goto failure;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	dev_hold(dev);
6708c2ecf20Sopenharmony_ci	return dev;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cifailure:
6738c2ecf20Sopenharmony_ci	unregister_netdevice(dev);
6748c2ecf20Sopenharmony_ci	return NULL;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci#endif
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cistatic int call_ip6mr_vif_entry_notifiers(struct net *net,
6798c2ecf20Sopenharmony_ci					  enum fib_event_type event_type,
6808c2ecf20Sopenharmony_ci					  struct vif_device *vif,
6818c2ecf20Sopenharmony_ci					  mifi_t vif_index, u32 tb_id)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	return mr_call_vif_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
6848c2ecf20Sopenharmony_ci				     vif, vif_index, tb_id,
6858c2ecf20Sopenharmony_ci				     &net->ipv6.ipmr_seq);
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_cistatic int call_ip6mr_mfc_entry_notifiers(struct net *net,
6898c2ecf20Sopenharmony_ci					  enum fib_event_type event_type,
6908c2ecf20Sopenharmony_ci					  struct mfc6_cache *mfc, u32 tb_id)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	return mr_call_mfc_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
6938c2ecf20Sopenharmony_ci				     &mfc->_c, tb_id, &net->ipv6.ipmr_seq);
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci/* Delete a VIF entry */
6978c2ecf20Sopenharmony_cistatic int mif6_delete(struct mr_table *mrt, int vifi, int notify,
6988c2ecf20Sopenharmony_ci		       struct list_head *head)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct vif_device *v;
7018c2ecf20Sopenharmony_ci	struct net_device *dev;
7028c2ecf20Sopenharmony_ci	struct inet6_dev *in6_dev;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (vifi < 0 || vifi >= mrt->maxvif)
7058c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	v = &mrt->vif_table[vifi];
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (VIF_EXISTS(mrt, vifi))
7108c2ecf20Sopenharmony_ci		call_ip6mr_vif_entry_notifiers(read_pnet(&mrt->net),
7118c2ecf20Sopenharmony_ci					       FIB_EVENT_VIF_DEL, v, vifi,
7128c2ecf20Sopenharmony_ci					       mrt->id);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	write_lock_bh(&mrt_lock);
7158c2ecf20Sopenharmony_ci	dev = v->dev;
7168c2ecf20Sopenharmony_ci	v->dev = NULL;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (!dev) {
7198c2ecf20Sopenharmony_ci		write_unlock_bh(&mrt_lock);
7208c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
7248c2ecf20Sopenharmony_ci	if (vifi == mrt->mroute_reg_vif_num)
7258c2ecf20Sopenharmony_ci		mrt->mroute_reg_vif_num = -1;
7268c2ecf20Sopenharmony_ci#endif
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (vifi + 1 == mrt->maxvif) {
7298c2ecf20Sopenharmony_ci		int tmp;
7308c2ecf20Sopenharmony_ci		for (tmp = vifi - 1; tmp >= 0; tmp--) {
7318c2ecf20Sopenharmony_ci			if (VIF_EXISTS(mrt, tmp))
7328c2ecf20Sopenharmony_ci				break;
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci		mrt->maxvif = tmp + 1;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	write_unlock_bh(&mrt_lock);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	dev_set_allmulti(dev, -1);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	in6_dev = __in6_dev_get(dev);
7428c2ecf20Sopenharmony_ci	if (in6_dev) {
7438c2ecf20Sopenharmony_ci		atomic_dec(&in6_dev->cnf.mc_forwarding);
7448c2ecf20Sopenharmony_ci		inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
7458c2ecf20Sopenharmony_ci					     NETCONFA_MC_FORWARDING,
7468c2ecf20Sopenharmony_ci					     dev->ifindex, &in6_dev->cnf);
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if ((v->flags & MIFF_REGISTER) && !notify)
7508c2ecf20Sopenharmony_ci		unregister_netdevice_queue(dev, head);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	dev_put(dev);
7538c2ecf20Sopenharmony_ci	return 0;
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic inline void ip6mr_cache_free_rcu(struct rcu_head *head)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct mr_mfc *c = container_of(head, struct mr_mfc, rcu);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	kmem_cache_free(mrt_cachep, (struct mfc6_cache *)c);
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic inline void ip6mr_cache_free(struct mfc6_cache *c)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	call_rcu(&c->_c.rcu, ip6mr_cache_free_rcu);
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci/* Destroy an unresolved cache entry, killing queued skbs
7698c2ecf20Sopenharmony_ci   and reporting error to netlink readers.
7708c2ecf20Sopenharmony_ci */
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic void ip6mr_destroy_unres(struct mr_table *mrt, struct mfc6_cache *c)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct net *net = read_pnet(&mrt->net);
7758c2ecf20Sopenharmony_ci	struct sk_buff *skb;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	atomic_dec(&mrt->cache_resolve_queue_len);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved)) != NULL) {
7808c2ecf20Sopenharmony_ci		if (ipv6_hdr(skb)->version == 0) {
7818c2ecf20Sopenharmony_ci			struct nlmsghdr *nlh = skb_pull(skb,
7828c2ecf20Sopenharmony_ci							sizeof(struct ipv6hdr));
7838c2ecf20Sopenharmony_ci			nlh->nlmsg_type = NLMSG_ERROR;
7848c2ecf20Sopenharmony_ci			nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
7858c2ecf20Sopenharmony_ci			skb_trim(skb, nlh->nlmsg_len);
7868c2ecf20Sopenharmony_ci			((struct nlmsgerr *)nlmsg_data(nlh))->error = -ETIMEDOUT;
7878c2ecf20Sopenharmony_ci			rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
7888c2ecf20Sopenharmony_ci		} else
7898c2ecf20Sopenharmony_ci			kfree_skb(skb);
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	ip6mr_cache_free(c);
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci/* Timer process for all the unresolved queue. */
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic void ipmr_do_expire_process(struct mr_table *mrt)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
8018c2ecf20Sopenharmony_ci	unsigned long expires = 10 * HZ;
8028c2ecf20Sopenharmony_ci	struct mr_mfc *c, *next;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
8058c2ecf20Sopenharmony_ci		if (time_after(c->mfc_un.unres.expires, now)) {
8068c2ecf20Sopenharmony_ci			/* not yet... */
8078c2ecf20Sopenharmony_ci			unsigned long interval = c->mfc_un.unres.expires - now;
8088c2ecf20Sopenharmony_ci			if (interval < expires)
8098c2ecf20Sopenharmony_ci				expires = interval;
8108c2ecf20Sopenharmony_ci			continue;
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci		list_del(&c->list);
8148c2ecf20Sopenharmony_ci		mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
8158c2ecf20Sopenharmony_ci		ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	if (!list_empty(&mrt->mfc_unres_queue))
8198c2ecf20Sopenharmony_ci		mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_cistatic void ipmr_expire_process(struct timer_list *t)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (!spin_trylock(&mfc_unres_lock)) {
8278c2ecf20Sopenharmony_ci		mod_timer(&mrt->ipmr_expire_timer, jiffies + 1);
8288c2ecf20Sopenharmony_ci		return;
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	if (!list_empty(&mrt->mfc_unres_queue))
8328c2ecf20Sopenharmony_ci		ipmr_do_expire_process(mrt);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	spin_unlock(&mfc_unres_lock);
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci/* Fill oifs list. It is called under write locked mrt_lock. */
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_cistatic void ip6mr_update_thresholds(struct mr_table *mrt,
8408c2ecf20Sopenharmony_ci				    struct mr_mfc *cache,
8418c2ecf20Sopenharmony_ci				    unsigned char *ttls)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	int vifi;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	cache->mfc_un.res.minvif = MAXMIFS;
8468c2ecf20Sopenharmony_ci	cache->mfc_un.res.maxvif = 0;
8478c2ecf20Sopenharmony_ci	memset(cache->mfc_un.res.ttls, 255, MAXMIFS);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	for (vifi = 0; vifi < mrt->maxvif; vifi++) {
8508c2ecf20Sopenharmony_ci		if (VIF_EXISTS(mrt, vifi) &&
8518c2ecf20Sopenharmony_ci		    ttls[vifi] && ttls[vifi] < 255) {
8528c2ecf20Sopenharmony_ci			cache->mfc_un.res.ttls[vifi] = ttls[vifi];
8538c2ecf20Sopenharmony_ci			if (cache->mfc_un.res.minvif > vifi)
8548c2ecf20Sopenharmony_ci				cache->mfc_un.res.minvif = vifi;
8558c2ecf20Sopenharmony_ci			if (cache->mfc_un.res.maxvif <= vifi)
8568c2ecf20Sopenharmony_ci				cache->mfc_un.res.maxvif = vifi + 1;
8578c2ecf20Sopenharmony_ci		}
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci	cache->mfc_un.res.lastuse = jiffies;
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_cistatic int mif6_add(struct net *net, struct mr_table *mrt,
8638c2ecf20Sopenharmony_ci		    struct mif6ctl *vifc, int mrtsock)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	int vifi = vifc->mif6c_mifi;
8668c2ecf20Sopenharmony_ci	struct vif_device *v = &mrt->vif_table[vifi];
8678c2ecf20Sopenharmony_ci	struct net_device *dev;
8688c2ecf20Sopenharmony_ci	struct inet6_dev *in6_dev;
8698c2ecf20Sopenharmony_ci	int err;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* Is vif busy ? */
8728c2ecf20Sopenharmony_ci	if (VIF_EXISTS(mrt, vifi))
8738c2ecf20Sopenharmony_ci		return -EADDRINUSE;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	switch (vifc->mif6c_flags) {
8768c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
8778c2ecf20Sopenharmony_ci	case MIFF_REGISTER:
8788c2ecf20Sopenharmony_ci		/*
8798c2ecf20Sopenharmony_ci		 * Special Purpose VIF in PIM
8808c2ecf20Sopenharmony_ci		 * All the packets will be sent to the daemon
8818c2ecf20Sopenharmony_ci		 */
8828c2ecf20Sopenharmony_ci		if (mrt->mroute_reg_vif_num >= 0)
8838c2ecf20Sopenharmony_ci			return -EADDRINUSE;
8848c2ecf20Sopenharmony_ci		dev = ip6mr_reg_vif(net, mrt);
8858c2ecf20Sopenharmony_ci		if (!dev)
8868c2ecf20Sopenharmony_ci			return -ENOBUFS;
8878c2ecf20Sopenharmony_ci		err = dev_set_allmulti(dev, 1);
8888c2ecf20Sopenharmony_ci		if (err) {
8898c2ecf20Sopenharmony_ci			unregister_netdevice(dev);
8908c2ecf20Sopenharmony_ci			dev_put(dev);
8918c2ecf20Sopenharmony_ci			return err;
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci		break;
8948c2ecf20Sopenharmony_ci#endif
8958c2ecf20Sopenharmony_ci	case 0:
8968c2ecf20Sopenharmony_ci		dev = dev_get_by_index(net, vifc->mif6c_pifi);
8978c2ecf20Sopenharmony_ci		if (!dev)
8988c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
8998c2ecf20Sopenharmony_ci		err = dev_set_allmulti(dev, 1);
9008c2ecf20Sopenharmony_ci		if (err) {
9018c2ecf20Sopenharmony_ci			dev_put(dev);
9028c2ecf20Sopenharmony_ci			return err;
9038c2ecf20Sopenharmony_ci		}
9048c2ecf20Sopenharmony_ci		break;
9058c2ecf20Sopenharmony_ci	default:
9068c2ecf20Sopenharmony_ci		return -EINVAL;
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	in6_dev = __in6_dev_get(dev);
9108c2ecf20Sopenharmony_ci	if (in6_dev) {
9118c2ecf20Sopenharmony_ci		atomic_inc(&in6_dev->cnf.mc_forwarding);
9128c2ecf20Sopenharmony_ci		inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
9138c2ecf20Sopenharmony_ci					     NETCONFA_MC_FORWARDING,
9148c2ecf20Sopenharmony_ci					     dev->ifindex, &in6_dev->cnf);
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* Fill in the VIF structures */
9188c2ecf20Sopenharmony_ci	vif_device_init(v, dev, vifc->vifc_rate_limit, vifc->vifc_threshold,
9198c2ecf20Sopenharmony_ci			vifc->mif6c_flags | (!mrtsock ? VIFF_STATIC : 0),
9208c2ecf20Sopenharmony_ci			MIFF_REGISTER);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	/* And finish update writing critical data */
9238c2ecf20Sopenharmony_ci	write_lock_bh(&mrt_lock);
9248c2ecf20Sopenharmony_ci	v->dev = dev;
9258c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
9268c2ecf20Sopenharmony_ci	if (v->flags & MIFF_REGISTER)
9278c2ecf20Sopenharmony_ci		mrt->mroute_reg_vif_num = vifi;
9288c2ecf20Sopenharmony_ci#endif
9298c2ecf20Sopenharmony_ci	if (vifi + 1 > mrt->maxvif)
9308c2ecf20Sopenharmony_ci		mrt->maxvif = vifi + 1;
9318c2ecf20Sopenharmony_ci	write_unlock_bh(&mrt_lock);
9328c2ecf20Sopenharmony_ci	call_ip6mr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD,
9338c2ecf20Sopenharmony_ci				       v, vifi, mrt->id);
9348c2ecf20Sopenharmony_ci	return 0;
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt,
9388c2ecf20Sopenharmony_ci					   const struct in6_addr *origin,
9398c2ecf20Sopenharmony_ci					   const struct in6_addr *mcastgrp)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	struct mfc6_cache_cmp_arg arg = {
9428c2ecf20Sopenharmony_ci		.mf6c_origin = *origin,
9438c2ecf20Sopenharmony_ci		.mf6c_mcastgrp = *mcastgrp,
9448c2ecf20Sopenharmony_ci	};
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	return mr_mfc_find(mrt, &arg);
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci/* Look for a (*,G) entry */
9508c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_find_any(struct mr_table *mrt,
9518c2ecf20Sopenharmony_ci					       struct in6_addr *mcastgrp,
9528c2ecf20Sopenharmony_ci					       mifi_t mifi)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	struct mfc6_cache_cmp_arg arg = {
9558c2ecf20Sopenharmony_ci		.mf6c_origin = in6addr_any,
9568c2ecf20Sopenharmony_ci		.mf6c_mcastgrp = *mcastgrp,
9578c2ecf20Sopenharmony_ci	};
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (ipv6_addr_any(mcastgrp))
9608c2ecf20Sopenharmony_ci		return mr_mfc_find_any_parent(mrt, mifi);
9618c2ecf20Sopenharmony_ci	return mr_mfc_find_any(mrt, mifi, &arg);
9628c2ecf20Sopenharmony_ci}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci/* Look for a (S,G,iif) entry if parent != -1 */
9658c2ecf20Sopenharmony_cistatic struct mfc6_cache *
9668c2ecf20Sopenharmony_ciip6mr_cache_find_parent(struct mr_table *mrt,
9678c2ecf20Sopenharmony_ci			const struct in6_addr *origin,
9688c2ecf20Sopenharmony_ci			const struct in6_addr *mcastgrp,
9698c2ecf20Sopenharmony_ci			int parent)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	struct mfc6_cache_cmp_arg arg = {
9728c2ecf20Sopenharmony_ci		.mf6c_origin = *origin,
9738c2ecf20Sopenharmony_ci		.mf6c_mcastgrp = *mcastgrp,
9748c2ecf20Sopenharmony_ci	};
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	return mr_mfc_find_parent(mrt, &arg, parent);
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci/* Allocate a multicast cache entry */
9808c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_alloc(void)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
9838c2ecf20Sopenharmony_ci	if (!c)
9848c2ecf20Sopenharmony_ci		return NULL;
9858c2ecf20Sopenharmony_ci	c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
9868c2ecf20Sopenharmony_ci	c->_c.mfc_un.res.minvif = MAXMIFS;
9878c2ecf20Sopenharmony_ci	c->_c.free = ip6mr_cache_free_rcu;
9888c2ecf20Sopenharmony_ci	refcount_set(&c->_c.mfc_un.res.refcount, 1);
9898c2ecf20Sopenharmony_ci	return c;
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_alloc_unres(void)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
9958c2ecf20Sopenharmony_ci	if (!c)
9968c2ecf20Sopenharmony_ci		return NULL;
9978c2ecf20Sopenharmony_ci	skb_queue_head_init(&c->_c.mfc_un.unres.unresolved);
9988c2ecf20Sopenharmony_ci	c->_c.mfc_un.unres.expires = jiffies + 10 * HZ;
9998c2ecf20Sopenharmony_ci	return c;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci/*
10038c2ecf20Sopenharmony_ci *	A cache entry has gone into a resolved state from queued
10048c2ecf20Sopenharmony_ci */
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic void ip6mr_cache_resolve(struct net *net, struct mr_table *mrt,
10078c2ecf20Sopenharmony_ci				struct mfc6_cache *uc, struct mfc6_cache *c)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	struct sk_buff *skb;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	/*
10128c2ecf20Sopenharmony_ci	 *	Play the pending entries through our router
10138c2ecf20Sopenharmony_ci	 */
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) {
10168c2ecf20Sopenharmony_ci		if (ipv6_hdr(skb)->version == 0) {
10178c2ecf20Sopenharmony_ci			struct nlmsghdr *nlh = skb_pull(skb,
10188c2ecf20Sopenharmony_ci							sizeof(struct ipv6hdr));
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci			if (mr_fill_mroute(mrt, skb, &c->_c,
10218c2ecf20Sopenharmony_ci					   nlmsg_data(nlh)) > 0) {
10228c2ecf20Sopenharmony_ci				nlh->nlmsg_len = skb_tail_pointer(skb) - (u8 *)nlh;
10238c2ecf20Sopenharmony_ci			} else {
10248c2ecf20Sopenharmony_ci				nlh->nlmsg_type = NLMSG_ERROR;
10258c2ecf20Sopenharmony_ci				nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
10268c2ecf20Sopenharmony_ci				skb_trim(skb, nlh->nlmsg_len);
10278c2ecf20Sopenharmony_ci				((struct nlmsgerr *)nlmsg_data(nlh))->error = -EMSGSIZE;
10288c2ecf20Sopenharmony_ci			}
10298c2ecf20Sopenharmony_ci			rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
10308c2ecf20Sopenharmony_ci		} else
10318c2ecf20Sopenharmony_ci			ip6_mr_forward(net, mrt, skb->dev, skb, c);
10328c2ecf20Sopenharmony_ci	}
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci/*
10368c2ecf20Sopenharmony_ci *	Bounce a cache query up to pim6sd and netlink.
10378c2ecf20Sopenharmony_ci *
10388c2ecf20Sopenharmony_ci *	Called under mrt_lock.
10398c2ecf20Sopenharmony_ci */
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt,
10428c2ecf20Sopenharmony_ci			      mifi_t mifi, int assert)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	struct sock *mroute6_sk;
10458c2ecf20Sopenharmony_ci	struct sk_buff *skb;
10468c2ecf20Sopenharmony_ci	struct mrt6msg *msg;
10478c2ecf20Sopenharmony_ci	int ret;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
10508c2ecf20Sopenharmony_ci	if (assert == MRT6MSG_WHOLEPKT)
10518c2ecf20Sopenharmony_ci		skb = skb_realloc_headroom(pkt, -skb_network_offset(pkt)
10528c2ecf20Sopenharmony_ci						+sizeof(*msg));
10538c2ecf20Sopenharmony_ci	else
10548c2ecf20Sopenharmony_ci#endif
10558c2ecf20Sopenharmony_ci		skb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(*msg), GFP_ATOMIC);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	if (!skb)
10588c2ecf20Sopenharmony_ci		return -ENOBUFS;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	/* I suppose that internal messages
10618c2ecf20Sopenharmony_ci	 * do not require checksums */
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_UNNECESSARY;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
10668c2ecf20Sopenharmony_ci	if (assert == MRT6MSG_WHOLEPKT) {
10678c2ecf20Sopenharmony_ci		/* Ugly, but we have no choice with this interface.
10688c2ecf20Sopenharmony_ci		   Duplicate old header, fix length etc.
10698c2ecf20Sopenharmony_ci		   And all this only to mangle msg->im6_msgtype and
10708c2ecf20Sopenharmony_ci		   to set msg->im6_mbz to "mbz" :-)
10718c2ecf20Sopenharmony_ci		 */
10728c2ecf20Sopenharmony_ci		__skb_pull(skb, skb_network_offset(pkt));
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		skb_push(skb, sizeof(*msg));
10758c2ecf20Sopenharmony_ci		skb_reset_transport_header(skb);
10768c2ecf20Sopenharmony_ci		msg = (struct mrt6msg *)skb_transport_header(skb);
10778c2ecf20Sopenharmony_ci		msg->im6_mbz = 0;
10788c2ecf20Sopenharmony_ci		msg->im6_msgtype = MRT6MSG_WHOLEPKT;
10798c2ecf20Sopenharmony_ci		msg->im6_mif = mrt->mroute_reg_vif_num;
10808c2ecf20Sopenharmony_ci		msg->im6_pad = 0;
10818c2ecf20Sopenharmony_ci		msg->im6_src = ipv6_hdr(pkt)->saddr;
10828c2ecf20Sopenharmony_ci		msg->im6_dst = ipv6_hdr(pkt)->daddr;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
10858c2ecf20Sopenharmony_ci	} else
10868c2ecf20Sopenharmony_ci#endif
10878c2ecf20Sopenharmony_ci	{
10888c2ecf20Sopenharmony_ci	/*
10898c2ecf20Sopenharmony_ci	 *	Copy the IP header
10908c2ecf20Sopenharmony_ci	 */
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(struct ipv6hdr));
10938c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
10948c2ecf20Sopenharmony_ci	skb_copy_to_linear_data(skb, ipv6_hdr(pkt), sizeof(struct ipv6hdr));
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	/*
10978c2ecf20Sopenharmony_ci	 *	Add our header
10988c2ecf20Sopenharmony_ci	 */
10998c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*msg));
11008c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
11018c2ecf20Sopenharmony_ci	msg = (struct mrt6msg *)skb_transport_header(skb);
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	msg->im6_mbz = 0;
11048c2ecf20Sopenharmony_ci	msg->im6_msgtype = assert;
11058c2ecf20Sopenharmony_ci	msg->im6_mif = mifi;
11068c2ecf20Sopenharmony_ci	msg->im6_pad = 0;
11078c2ecf20Sopenharmony_ci	msg->im6_src = ipv6_hdr(pkt)->saddr;
11088c2ecf20Sopenharmony_ci	msg->im6_dst = ipv6_hdr(pkt)->daddr;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	skb_dst_set(skb, dst_clone(skb_dst(pkt)));
11118c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_UNNECESSARY;
11128c2ecf20Sopenharmony_ci	}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	rcu_read_lock();
11158c2ecf20Sopenharmony_ci	mroute6_sk = rcu_dereference(mrt->mroute_sk);
11168c2ecf20Sopenharmony_ci	if (!mroute6_sk) {
11178c2ecf20Sopenharmony_ci		rcu_read_unlock();
11188c2ecf20Sopenharmony_ci		kfree_skb(skb);
11198c2ecf20Sopenharmony_ci		return -EINVAL;
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	mrt6msg_netlink_event(mrt, skb);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	/* Deliver to user space multicast routing algorithms */
11258c2ecf20Sopenharmony_ci	ret = sock_queue_rcv_skb(mroute6_sk, skb);
11268c2ecf20Sopenharmony_ci	rcu_read_unlock();
11278c2ecf20Sopenharmony_ci	if (ret < 0) {
11288c2ecf20Sopenharmony_ci		net_warn_ratelimited("mroute6: pending queue full, dropping entries\n");
11298c2ecf20Sopenharmony_ci		kfree_skb(skb);
11308c2ecf20Sopenharmony_ci	}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	return ret;
11338c2ecf20Sopenharmony_ci}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci/* Queue a packet for resolution. It gets locked cache entry! */
11368c2ecf20Sopenharmony_cistatic int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
11378c2ecf20Sopenharmony_ci				  struct sk_buff *skb, struct net_device *dev)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	struct mfc6_cache *c;
11408c2ecf20Sopenharmony_ci	bool found = false;
11418c2ecf20Sopenharmony_ci	int err;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	spin_lock_bh(&mfc_unres_lock);
11448c2ecf20Sopenharmony_ci	list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) {
11458c2ecf20Sopenharmony_ci		if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) &&
11468c2ecf20Sopenharmony_ci		    ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) {
11478c2ecf20Sopenharmony_ci			found = true;
11488c2ecf20Sopenharmony_ci			break;
11498c2ecf20Sopenharmony_ci		}
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	if (!found) {
11538c2ecf20Sopenharmony_ci		/*
11548c2ecf20Sopenharmony_ci		 *	Create a new entry if allowable
11558c2ecf20Sopenharmony_ci		 */
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci		c = ip6mr_cache_alloc_unres();
11588c2ecf20Sopenharmony_ci		if (!c) {
11598c2ecf20Sopenharmony_ci			spin_unlock_bh(&mfc_unres_lock);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci			kfree_skb(skb);
11628c2ecf20Sopenharmony_ci			return -ENOBUFS;
11638c2ecf20Sopenharmony_ci		}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci		/* Fill in the new cache entry */
11668c2ecf20Sopenharmony_ci		c->_c.mfc_parent = -1;
11678c2ecf20Sopenharmony_ci		c->mf6c_origin = ipv6_hdr(skb)->saddr;
11688c2ecf20Sopenharmony_ci		c->mf6c_mcastgrp = ipv6_hdr(skb)->daddr;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci		/*
11718c2ecf20Sopenharmony_ci		 *	Reflect first query at pim6sd
11728c2ecf20Sopenharmony_ci		 */
11738c2ecf20Sopenharmony_ci		err = ip6mr_cache_report(mrt, skb, mifi, MRT6MSG_NOCACHE);
11748c2ecf20Sopenharmony_ci		if (err < 0) {
11758c2ecf20Sopenharmony_ci			/* If the report failed throw the cache entry
11768c2ecf20Sopenharmony_ci			   out - Brad Parker
11778c2ecf20Sopenharmony_ci			 */
11788c2ecf20Sopenharmony_ci			spin_unlock_bh(&mfc_unres_lock);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci			ip6mr_cache_free(c);
11818c2ecf20Sopenharmony_ci			kfree_skb(skb);
11828c2ecf20Sopenharmony_ci			return err;
11838c2ecf20Sopenharmony_ci		}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci		atomic_inc(&mrt->cache_resolve_queue_len);
11868c2ecf20Sopenharmony_ci		list_add(&c->_c.list, &mrt->mfc_unres_queue);
11878c2ecf20Sopenharmony_ci		mr6_netlink_event(mrt, c, RTM_NEWROUTE);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci		ipmr_do_expire_process(mrt);
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	/* See if we can append the packet */
11938c2ecf20Sopenharmony_ci	if (c->_c.mfc_un.unres.unresolved.qlen > 3) {
11948c2ecf20Sopenharmony_ci		kfree_skb(skb);
11958c2ecf20Sopenharmony_ci		err = -ENOBUFS;
11968c2ecf20Sopenharmony_ci	} else {
11978c2ecf20Sopenharmony_ci		if (dev) {
11988c2ecf20Sopenharmony_ci			skb->dev = dev;
11998c2ecf20Sopenharmony_ci			skb->skb_iif = dev->ifindex;
12008c2ecf20Sopenharmony_ci		}
12018c2ecf20Sopenharmony_ci		skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb);
12028c2ecf20Sopenharmony_ci		err = 0;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	spin_unlock_bh(&mfc_unres_lock);
12068c2ecf20Sopenharmony_ci	return err;
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci/*
12108c2ecf20Sopenharmony_ci *	MFC6 cache manipulation by user space
12118c2ecf20Sopenharmony_ci */
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_cistatic int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
12148c2ecf20Sopenharmony_ci			    int parent)
12158c2ecf20Sopenharmony_ci{
12168c2ecf20Sopenharmony_ci	struct mfc6_cache *c;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	/* The entries are added/deleted only under RTNL */
12198c2ecf20Sopenharmony_ci	rcu_read_lock();
12208c2ecf20Sopenharmony_ci	c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
12218c2ecf20Sopenharmony_ci				    &mfc->mf6cc_mcastgrp.sin6_addr, parent);
12228c2ecf20Sopenharmony_ci	rcu_read_unlock();
12238c2ecf20Sopenharmony_ci	if (!c)
12248c2ecf20Sopenharmony_ci		return -ENOENT;
12258c2ecf20Sopenharmony_ci	rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params);
12268c2ecf20Sopenharmony_ci	list_del_rcu(&c->_c.list);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
12298c2ecf20Sopenharmony_ci				       FIB_EVENT_ENTRY_DEL, c, mrt->id);
12308c2ecf20Sopenharmony_ci	mr6_netlink_event(mrt, c, RTM_DELROUTE);
12318c2ecf20Sopenharmony_ci	mr_cache_put(&c->_c);
12328c2ecf20Sopenharmony_ci	return 0;
12338c2ecf20Sopenharmony_ci}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_cistatic int ip6mr_device_event(struct notifier_block *this,
12368c2ecf20Sopenharmony_ci			      unsigned long event, void *ptr)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
12398c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
12408c2ecf20Sopenharmony_ci	struct mr_table *mrt;
12418c2ecf20Sopenharmony_ci	struct vif_device *v;
12428c2ecf20Sopenharmony_ci	int ct;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	if (event != NETDEV_UNREGISTER)
12458c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	ip6mr_for_each_table(mrt, net) {
12488c2ecf20Sopenharmony_ci		v = &mrt->vif_table[0];
12498c2ecf20Sopenharmony_ci		for (ct = 0; ct < mrt->maxvif; ct++, v++) {
12508c2ecf20Sopenharmony_ci			if (v->dev == dev)
12518c2ecf20Sopenharmony_ci				mif6_delete(mrt, ct, 1, NULL);
12528c2ecf20Sopenharmony_ci		}
12538c2ecf20Sopenharmony_ci	}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_cistatic unsigned int ip6mr_seq_read(struct net *net)
12598c2ecf20Sopenharmony_ci{
12608c2ecf20Sopenharmony_ci	ASSERT_RTNL();
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	return net->ipv6.ipmr_seq + ip6mr_rules_seq_read(net);
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic int ip6mr_dump(struct net *net, struct notifier_block *nb,
12668c2ecf20Sopenharmony_ci		      struct netlink_ext_ack *extack)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	return mr_dump(net, nb, RTNL_FAMILY_IP6MR, ip6mr_rules_dump,
12698c2ecf20Sopenharmony_ci		       ip6mr_mr_table_iter, &mrt_lock, extack);
12708c2ecf20Sopenharmony_ci}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_cistatic struct notifier_block ip6_mr_notifier = {
12738c2ecf20Sopenharmony_ci	.notifier_call = ip6mr_device_event
12748c2ecf20Sopenharmony_ci};
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic const struct fib_notifier_ops ip6mr_notifier_ops_template = {
12778c2ecf20Sopenharmony_ci	.family		= RTNL_FAMILY_IP6MR,
12788c2ecf20Sopenharmony_ci	.fib_seq_read	= ip6mr_seq_read,
12798c2ecf20Sopenharmony_ci	.fib_dump	= ip6mr_dump,
12808c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
12818c2ecf20Sopenharmony_ci};
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic int __net_init ip6mr_notifier_init(struct net *net)
12848c2ecf20Sopenharmony_ci{
12858c2ecf20Sopenharmony_ci	struct fib_notifier_ops *ops;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	net->ipv6.ipmr_seq = 0;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	ops = fib_notifier_ops_register(&ip6mr_notifier_ops_template, net);
12908c2ecf20Sopenharmony_ci	if (IS_ERR(ops))
12918c2ecf20Sopenharmony_ci		return PTR_ERR(ops);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	net->ipv6.ip6mr_notifier_ops = ops;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	return 0;
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_notifier_exit(struct net *net)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	fib_notifier_ops_unregister(net->ipv6.ip6mr_notifier_ops);
13018c2ecf20Sopenharmony_ci	net->ipv6.ip6mr_notifier_ops = NULL;
13028c2ecf20Sopenharmony_ci}
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci/* Setup for IP multicast routing */
13058c2ecf20Sopenharmony_cistatic int __net_init ip6mr_net_init(struct net *net)
13068c2ecf20Sopenharmony_ci{
13078c2ecf20Sopenharmony_ci	int err;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	err = ip6mr_notifier_init(net);
13108c2ecf20Sopenharmony_ci	if (err)
13118c2ecf20Sopenharmony_ci		return err;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	err = ip6mr_rules_init(net);
13148c2ecf20Sopenharmony_ci	if (err < 0)
13158c2ecf20Sopenharmony_ci		goto ip6mr_rules_fail;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
13188c2ecf20Sopenharmony_ci	err = -ENOMEM;
13198c2ecf20Sopenharmony_ci	if (!proc_create_net("ip6_mr_vif", 0, net->proc_net, &ip6mr_vif_seq_ops,
13208c2ecf20Sopenharmony_ci			sizeof(struct mr_vif_iter)))
13218c2ecf20Sopenharmony_ci		goto proc_vif_fail;
13228c2ecf20Sopenharmony_ci	if (!proc_create_net("ip6_mr_cache", 0, net->proc_net, &ipmr_mfc_seq_ops,
13238c2ecf20Sopenharmony_ci			sizeof(struct mr_mfc_iter)))
13248c2ecf20Sopenharmony_ci		goto proc_cache_fail;
13258c2ecf20Sopenharmony_ci#endif
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	return 0;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
13308c2ecf20Sopenharmony_ciproc_cache_fail:
13318c2ecf20Sopenharmony_ci	remove_proc_entry("ip6_mr_vif", net->proc_net);
13328c2ecf20Sopenharmony_ciproc_vif_fail:
13338c2ecf20Sopenharmony_ci	ip6mr_rules_exit(net);
13348c2ecf20Sopenharmony_ci#endif
13358c2ecf20Sopenharmony_ciip6mr_rules_fail:
13368c2ecf20Sopenharmony_ci	ip6mr_notifier_exit(net);
13378c2ecf20Sopenharmony_ci	return err;
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_net_exit(struct net *net)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
13438c2ecf20Sopenharmony_ci	remove_proc_entry("ip6_mr_cache", net->proc_net);
13448c2ecf20Sopenharmony_ci	remove_proc_entry("ip6_mr_vif", net->proc_net);
13458c2ecf20Sopenharmony_ci#endif
13468c2ecf20Sopenharmony_ci	ip6mr_rules_exit(net);
13478c2ecf20Sopenharmony_ci	ip6mr_notifier_exit(net);
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_cistatic struct pernet_operations ip6mr_net_ops = {
13518c2ecf20Sopenharmony_ci	.init = ip6mr_net_init,
13528c2ecf20Sopenharmony_ci	.exit = ip6mr_net_exit,
13538c2ecf20Sopenharmony_ci};
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ciint __init ip6_mr_init(void)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	int err;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	mrt_cachep = kmem_cache_create("ip6_mrt_cache",
13608c2ecf20Sopenharmony_ci				       sizeof(struct mfc6_cache),
13618c2ecf20Sopenharmony_ci				       0, SLAB_HWCACHE_ALIGN,
13628c2ecf20Sopenharmony_ci				       NULL);
13638c2ecf20Sopenharmony_ci	if (!mrt_cachep)
13648c2ecf20Sopenharmony_ci		return -ENOMEM;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	err = register_pernet_subsys(&ip6mr_net_ops);
13678c2ecf20Sopenharmony_ci	if (err)
13688c2ecf20Sopenharmony_ci		goto reg_pernet_fail;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	err = register_netdevice_notifier(&ip6_mr_notifier);
13718c2ecf20Sopenharmony_ci	if (err)
13728c2ecf20Sopenharmony_ci		goto reg_notif_fail;
13738c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
13748c2ecf20Sopenharmony_ci	if (inet6_add_protocol(&pim6_protocol, IPPROTO_PIM) < 0) {
13758c2ecf20Sopenharmony_ci		pr_err("%s: can't add PIM protocol\n", __func__);
13768c2ecf20Sopenharmony_ci		err = -EAGAIN;
13778c2ecf20Sopenharmony_ci		goto add_proto_fail;
13788c2ecf20Sopenharmony_ci	}
13798c2ecf20Sopenharmony_ci#endif
13808c2ecf20Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, RTNL_FAMILY_IP6MR, RTM_GETROUTE,
13818c2ecf20Sopenharmony_ci				   NULL, ip6mr_rtm_dumproute, 0);
13828c2ecf20Sopenharmony_ci	if (err == 0)
13838c2ecf20Sopenharmony_ci		return 0;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
13868c2ecf20Sopenharmony_ci	inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
13878c2ecf20Sopenharmony_ciadd_proto_fail:
13888c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&ip6_mr_notifier);
13898c2ecf20Sopenharmony_ci#endif
13908c2ecf20Sopenharmony_cireg_notif_fail:
13918c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6mr_net_ops);
13928c2ecf20Sopenharmony_cireg_pernet_fail:
13938c2ecf20Sopenharmony_ci	kmem_cache_destroy(mrt_cachep);
13948c2ecf20Sopenharmony_ci	return err;
13958c2ecf20Sopenharmony_ci}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_civoid ip6_mr_cleanup(void)
13988c2ecf20Sopenharmony_ci{
13998c2ecf20Sopenharmony_ci	rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE);
14008c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
14018c2ecf20Sopenharmony_ci	inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
14028c2ecf20Sopenharmony_ci#endif
14038c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&ip6_mr_notifier);
14048c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6mr_net_ops);
14058c2ecf20Sopenharmony_ci	kmem_cache_destroy(mrt_cachep);
14068c2ecf20Sopenharmony_ci}
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_cistatic int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
14098c2ecf20Sopenharmony_ci			 struct mf6cctl *mfc, int mrtsock, int parent)
14108c2ecf20Sopenharmony_ci{
14118c2ecf20Sopenharmony_ci	unsigned char ttls[MAXMIFS];
14128c2ecf20Sopenharmony_ci	struct mfc6_cache *uc, *c;
14138c2ecf20Sopenharmony_ci	struct mr_mfc *_uc;
14148c2ecf20Sopenharmony_ci	bool found;
14158c2ecf20Sopenharmony_ci	int i, err;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	if (mfc->mf6cc_parent >= MAXMIFS)
14188c2ecf20Sopenharmony_ci		return -ENFILE;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	memset(ttls, 255, MAXMIFS);
14218c2ecf20Sopenharmony_ci	for (i = 0; i < MAXMIFS; i++) {
14228c2ecf20Sopenharmony_ci		if (IF_ISSET(i, &mfc->mf6cc_ifset))
14238c2ecf20Sopenharmony_ci			ttls[i] = 1;
14248c2ecf20Sopenharmony_ci	}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	/* The entries are added/deleted only under RTNL */
14278c2ecf20Sopenharmony_ci	rcu_read_lock();
14288c2ecf20Sopenharmony_ci	c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
14298c2ecf20Sopenharmony_ci				    &mfc->mf6cc_mcastgrp.sin6_addr, parent);
14308c2ecf20Sopenharmony_ci	rcu_read_unlock();
14318c2ecf20Sopenharmony_ci	if (c) {
14328c2ecf20Sopenharmony_ci		write_lock_bh(&mrt_lock);
14338c2ecf20Sopenharmony_ci		c->_c.mfc_parent = mfc->mf6cc_parent;
14348c2ecf20Sopenharmony_ci		ip6mr_update_thresholds(mrt, &c->_c, ttls);
14358c2ecf20Sopenharmony_ci		if (!mrtsock)
14368c2ecf20Sopenharmony_ci			c->_c.mfc_flags |= MFC_STATIC;
14378c2ecf20Sopenharmony_ci		write_unlock_bh(&mrt_lock);
14388c2ecf20Sopenharmony_ci		call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE,
14398c2ecf20Sopenharmony_ci					       c, mrt->id);
14408c2ecf20Sopenharmony_ci		mr6_netlink_event(mrt, c, RTM_NEWROUTE);
14418c2ecf20Sopenharmony_ci		return 0;
14428c2ecf20Sopenharmony_ci	}
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(&mfc->mf6cc_mcastgrp.sin6_addr) &&
14458c2ecf20Sopenharmony_ci	    !ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr))
14468c2ecf20Sopenharmony_ci		return -EINVAL;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	c = ip6mr_cache_alloc();
14498c2ecf20Sopenharmony_ci	if (!c)
14508c2ecf20Sopenharmony_ci		return -ENOMEM;
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	c->mf6c_origin = mfc->mf6cc_origin.sin6_addr;
14538c2ecf20Sopenharmony_ci	c->mf6c_mcastgrp = mfc->mf6cc_mcastgrp.sin6_addr;
14548c2ecf20Sopenharmony_ci	c->_c.mfc_parent = mfc->mf6cc_parent;
14558c2ecf20Sopenharmony_ci	ip6mr_update_thresholds(mrt, &c->_c, ttls);
14568c2ecf20Sopenharmony_ci	if (!mrtsock)
14578c2ecf20Sopenharmony_ci		c->_c.mfc_flags |= MFC_STATIC;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	err = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode,
14608c2ecf20Sopenharmony_ci				  ip6mr_rht_params);
14618c2ecf20Sopenharmony_ci	if (err) {
14628c2ecf20Sopenharmony_ci		pr_err("ip6mr: rhtable insert error %d\n", err);
14638c2ecf20Sopenharmony_ci		ip6mr_cache_free(c);
14648c2ecf20Sopenharmony_ci		return err;
14658c2ecf20Sopenharmony_ci	}
14668c2ecf20Sopenharmony_ci	list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list);
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	/* Check to see if we resolved a queued list. If so we
14698c2ecf20Sopenharmony_ci	 * need to send on the frames and tidy up.
14708c2ecf20Sopenharmony_ci	 */
14718c2ecf20Sopenharmony_ci	found = false;
14728c2ecf20Sopenharmony_ci	spin_lock_bh(&mfc_unres_lock);
14738c2ecf20Sopenharmony_ci	list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) {
14748c2ecf20Sopenharmony_ci		uc = (struct mfc6_cache *)_uc;
14758c2ecf20Sopenharmony_ci		if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) &&
14768c2ecf20Sopenharmony_ci		    ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) {
14778c2ecf20Sopenharmony_ci			list_del(&_uc->list);
14788c2ecf20Sopenharmony_ci			atomic_dec(&mrt->cache_resolve_queue_len);
14798c2ecf20Sopenharmony_ci			found = true;
14808c2ecf20Sopenharmony_ci			break;
14818c2ecf20Sopenharmony_ci		}
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci	if (list_empty(&mrt->mfc_unres_queue))
14848c2ecf20Sopenharmony_ci		del_timer(&mrt->ipmr_expire_timer);
14858c2ecf20Sopenharmony_ci	spin_unlock_bh(&mfc_unres_lock);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	if (found) {
14888c2ecf20Sopenharmony_ci		ip6mr_cache_resolve(net, mrt, uc, c);
14898c2ecf20Sopenharmony_ci		ip6mr_cache_free(uc);
14908c2ecf20Sopenharmony_ci	}
14918c2ecf20Sopenharmony_ci	call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD,
14928c2ecf20Sopenharmony_ci				       c, mrt->id);
14938c2ecf20Sopenharmony_ci	mr6_netlink_event(mrt, c, RTM_NEWROUTE);
14948c2ecf20Sopenharmony_ci	return 0;
14958c2ecf20Sopenharmony_ci}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci/*
14988c2ecf20Sopenharmony_ci *	Close the multicast socket, and clear the vif tables etc
14998c2ecf20Sopenharmony_ci */
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_cistatic void mroute_clean_tables(struct mr_table *mrt, int flags)
15028c2ecf20Sopenharmony_ci{
15038c2ecf20Sopenharmony_ci	struct mr_mfc *c, *tmp;
15048c2ecf20Sopenharmony_ci	LIST_HEAD(list);
15058c2ecf20Sopenharmony_ci	int i;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	/* Shut down all active vif entries */
15088c2ecf20Sopenharmony_ci	if (flags & (MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC)) {
15098c2ecf20Sopenharmony_ci		for (i = 0; i < mrt->maxvif; i++) {
15108c2ecf20Sopenharmony_ci			if (((mrt->vif_table[i].flags & VIFF_STATIC) &&
15118c2ecf20Sopenharmony_ci			     !(flags & MRT6_FLUSH_MIFS_STATIC)) ||
15128c2ecf20Sopenharmony_ci			    (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS)))
15138c2ecf20Sopenharmony_ci				continue;
15148c2ecf20Sopenharmony_ci			mif6_delete(mrt, i, 0, &list);
15158c2ecf20Sopenharmony_ci		}
15168c2ecf20Sopenharmony_ci		unregister_netdevice_many(&list);
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	/* Wipe the cache */
15208c2ecf20Sopenharmony_ci	if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) {
15218c2ecf20Sopenharmony_ci		list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
15228c2ecf20Sopenharmony_ci			if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) ||
15238c2ecf20Sopenharmony_ci			    (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC)))
15248c2ecf20Sopenharmony_ci				continue;
15258c2ecf20Sopenharmony_ci			rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
15268c2ecf20Sopenharmony_ci			list_del_rcu(&c->list);
15278c2ecf20Sopenharmony_ci			call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
15288c2ecf20Sopenharmony_ci						       FIB_EVENT_ENTRY_DEL,
15298c2ecf20Sopenharmony_ci						       (struct mfc6_cache *)c, mrt->id);
15308c2ecf20Sopenharmony_ci			mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
15318c2ecf20Sopenharmony_ci			mr_cache_put(c);
15328c2ecf20Sopenharmony_ci		}
15338c2ecf20Sopenharmony_ci	}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	if (flags & MRT6_FLUSH_MFC) {
15368c2ecf20Sopenharmony_ci		if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
15378c2ecf20Sopenharmony_ci			spin_lock_bh(&mfc_unres_lock);
15388c2ecf20Sopenharmony_ci			list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
15398c2ecf20Sopenharmony_ci				list_del(&c->list);
15408c2ecf20Sopenharmony_ci				mr6_netlink_event(mrt, (struct mfc6_cache *)c,
15418c2ecf20Sopenharmony_ci						  RTM_DELROUTE);
15428c2ecf20Sopenharmony_ci				ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
15438c2ecf20Sopenharmony_ci			}
15448c2ecf20Sopenharmony_ci			spin_unlock_bh(&mfc_unres_lock);
15458c2ecf20Sopenharmony_ci		}
15468c2ecf20Sopenharmony_ci	}
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_cistatic int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk)
15508c2ecf20Sopenharmony_ci{
15518c2ecf20Sopenharmony_ci	int err = 0;
15528c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	rtnl_lock();
15558c2ecf20Sopenharmony_ci	write_lock_bh(&mrt_lock);
15568c2ecf20Sopenharmony_ci	if (rtnl_dereference(mrt->mroute_sk)) {
15578c2ecf20Sopenharmony_ci		err = -EADDRINUSE;
15588c2ecf20Sopenharmony_ci	} else {
15598c2ecf20Sopenharmony_ci		rcu_assign_pointer(mrt->mroute_sk, sk);
15608c2ecf20Sopenharmony_ci		sock_set_flag(sk, SOCK_RCU_FREE);
15618c2ecf20Sopenharmony_ci		atomic_inc(&net->ipv6.devconf_all->mc_forwarding);
15628c2ecf20Sopenharmony_ci	}
15638c2ecf20Sopenharmony_ci	write_unlock_bh(&mrt_lock);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	if (!err)
15668c2ecf20Sopenharmony_ci		inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
15678c2ecf20Sopenharmony_ci					     NETCONFA_MC_FORWARDING,
15688c2ecf20Sopenharmony_ci					     NETCONFA_IFINDEX_ALL,
15698c2ecf20Sopenharmony_ci					     net->ipv6.devconf_all);
15708c2ecf20Sopenharmony_ci	rtnl_unlock();
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	return err;
15738c2ecf20Sopenharmony_ci}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ciint ip6mr_sk_done(struct sock *sk)
15768c2ecf20Sopenharmony_ci{
15778c2ecf20Sopenharmony_ci	int err = -EACCES;
15788c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
15798c2ecf20Sopenharmony_ci	struct mr_table *mrt;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	if (sk->sk_type != SOCK_RAW ||
15828c2ecf20Sopenharmony_ci	    inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
15838c2ecf20Sopenharmony_ci		return err;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	rtnl_lock();
15868c2ecf20Sopenharmony_ci	ip6mr_for_each_table(mrt, net) {
15878c2ecf20Sopenharmony_ci		if (sk == rtnl_dereference(mrt->mroute_sk)) {
15888c2ecf20Sopenharmony_ci			write_lock_bh(&mrt_lock);
15898c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(mrt->mroute_sk, NULL);
15908c2ecf20Sopenharmony_ci			/* Note that mroute_sk had SOCK_RCU_FREE set,
15918c2ecf20Sopenharmony_ci			 * so the RCU grace period before sk freeing
15928c2ecf20Sopenharmony_ci			 * is guaranteed by sk_destruct()
15938c2ecf20Sopenharmony_ci			 */
15948c2ecf20Sopenharmony_ci			atomic_dec(&net->ipv6.devconf_all->mc_forwarding);
15958c2ecf20Sopenharmony_ci			write_unlock_bh(&mrt_lock);
15968c2ecf20Sopenharmony_ci			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
15978c2ecf20Sopenharmony_ci						     NETCONFA_MC_FORWARDING,
15988c2ecf20Sopenharmony_ci						     NETCONFA_IFINDEX_ALL,
15998c2ecf20Sopenharmony_ci						     net->ipv6.devconf_all);
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci			mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC);
16028c2ecf20Sopenharmony_ci			err = 0;
16038c2ecf20Sopenharmony_ci			break;
16048c2ecf20Sopenharmony_ci		}
16058c2ecf20Sopenharmony_ci	}
16068c2ecf20Sopenharmony_ci	rtnl_unlock();
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	return err;
16098c2ecf20Sopenharmony_ci}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cibool mroute6_is_socket(struct net *net, struct sk_buff *skb)
16128c2ecf20Sopenharmony_ci{
16138c2ecf20Sopenharmony_ci	struct mr_table *mrt;
16148c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
16158c2ecf20Sopenharmony_ci		.flowi6_iif	= skb->skb_iif ? : LOOPBACK_IFINDEX,
16168c2ecf20Sopenharmony_ci		.flowi6_oif	= skb->dev->ifindex,
16178c2ecf20Sopenharmony_ci		.flowi6_mark	= skb->mark,
16188c2ecf20Sopenharmony_ci	};
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
16218c2ecf20Sopenharmony_ci		return NULL;
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	return rcu_access_pointer(mrt->mroute_sk);
16248c2ecf20Sopenharmony_ci}
16258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mroute6_is_socket);
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci/*
16288c2ecf20Sopenharmony_ci *	Socket options and virtual interface manipulation. The whole
16298c2ecf20Sopenharmony_ci *	virtual interface system is a complete heap, but unfortunately
16308c2ecf20Sopenharmony_ci *	that's how BSD mrouted happens to think. Maybe one day with a proper
16318c2ecf20Sopenharmony_ci *	MOSPF/PIM router set up we can clean this up.
16328c2ecf20Sopenharmony_ci */
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ciint ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
16358c2ecf20Sopenharmony_ci			  unsigned int optlen)
16368c2ecf20Sopenharmony_ci{
16378c2ecf20Sopenharmony_ci	int ret, parent = 0;
16388c2ecf20Sopenharmony_ci	struct mif6ctl vif;
16398c2ecf20Sopenharmony_ci	struct mf6cctl mfc;
16408c2ecf20Sopenharmony_ci	mifi_t mifi;
16418c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
16428c2ecf20Sopenharmony_ci	struct mr_table *mrt;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	if (sk->sk_type != SOCK_RAW ||
16458c2ecf20Sopenharmony_ci	    inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
16468c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
16498c2ecf20Sopenharmony_ci	if (!mrt)
16508c2ecf20Sopenharmony_ci		return -ENOENT;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	if (optname != MRT6_INIT) {
16538c2ecf20Sopenharmony_ci		if (sk != rcu_access_pointer(mrt->mroute_sk) &&
16548c2ecf20Sopenharmony_ci		    !ns_capable(net->user_ns, CAP_NET_ADMIN))
16558c2ecf20Sopenharmony_ci			return -EACCES;
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	switch (optname) {
16598c2ecf20Sopenharmony_ci	case MRT6_INIT:
16608c2ecf20Sopenharmony_ci		if (optlen < sizeof(int))
16618c2ecf20Sopenharmony_ci			return -EINVAL;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci		return ip6mr_sk_init(mrt, sk);
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	case MRT6_DONE:
16668c2ecf20Sopenharmony_ci		return ip6mr_sk_done(sk);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	case MRT6_ADD_MIF:
16698c2ecf20Sopenharmony_ci		if (optlen < sizeof(vif))
16708c2ecf20Sopenharmony_ci			return -EINVAL;
16718c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&vif, optval, sizeof(vif)))
16728c2ecf20Sopenharmony_ci			return -EFAULT;
16738c2ecf20Sopenharmony_ci		if (vif.mif6c_mifi >= MAXMIFS)
16748c2ecf20Sopenharmony_ci			return -ENFILE;
16758c2ecf20Sopenharmony_ci		rtnl_lock();
16768c2ecf20Sopenharmony_ci		ret = mif6_add(net, mrt, &vif,
16778c2ecf20Sopenharmony_ci			       sk == rtnl_dereference(mrt->mroute_sk));
16788c2ecf20Sopenharmony_ci		rtnl_unlock();
16798c2ecf20Sopenharmony_ci		return ret;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	case MRT6_DEL_MIF:
16828c2ecf20Sopenharmony_ci		if (optlen < sizeof(mifi_t))
16838c2ecf20Sopenharmony_ci			return -EINVAL;
16848c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&mifi, optval, sizeof(mifi_t)))
16858c2ecf20Sopenharmony_ci			return -EFAULT;
16868c2ecf20Sopenharmony_ci		rtnl_lock();
16878c2ecf20Sopenharmony_ci		ret = mif6_delete(mrt, mifi, 0, NULL);
16888c2ecf20Sopenharmony_ci		rtnl_unlock();
16898c2ecf20Sopenharmony_ci		return ret;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	/*
16928c2ecf20Sopenharmony_ci	 *	Manipulate the forwarding caches. These live
16938c2ecf20Sopenharmony_ci	 *	in a sort of kernel/user symbiosis.
16948c2ecf20Sopenharmony_ci	 */
16958c2ecf20Sopenharmony_ci	case MRT6_ADD_MFC:
16968c2ecf20Sopenharmony_ci	case MRT6_DEL_MFC:
16978c2ecf20Sopenharmony_ci		parent = -1;
16988c2ecf20Sopenharmony_ci		fallthrough;
16998c2ecf20Sopenharmony_ci	case MRT6_ADD_MFC_PROXY:
17008c2ecf20Sopenharmony_ci	case MRT6_DEL_MFC_PROXY:
17018c2ecf20Sopenharmony_ci		if (optlen < sizeof(mfc))
17028c2ecf20Sopenharmony_ci			return -EINVAL;
17038c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&mfc, optval, sizeof(mfc)))
17048c2ecf20Sopenharmony_ci			return -EFAULT;
17058c2ecf20Sopenharmony_ci		if (parent == 0)
17068c2ecf20Sopenharmony_ci			parent = mfc.mf6cc_parent;
17078c2ecf20Sopenharmony_ci		rtnl_lock();
17088c2ecf20Sopenharmony_ci		if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
17098c2ecf20Sopenharmony_ci			ret = ip6mr_mfc_delete(mrt, &mfc, parent);
17108c2ecf20Sopenharmony_ci		else
17118c2ecf20Sopenharmony_ci			ret = ip6mr_mfc_add(net, mrt, &mfc,
17128c2ecf20Sopenharmony_ci					    sk ==
17138c2ecf20Sopenharmony_ci					    rtnl_dereference(mrt->mroute_sk),
17148c2ecf20Sopenharmony_ci					    parent);
17158c2ecf20Sopenharmony_ci		rtnl_unlock();
17168c2ecf20Sopenharmony_ci		return ret;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	case MRT6_FLUSH:
17198c2ecf20Sopenharmony_ci	{
17208c2ecf20Sopenharmony_ci		int flags;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci		if (optlen != sizeof(flags))
17238c2ecf20Sopenharmony_ci			return -EINVAL;
17248c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&flags, optval, sizeof(flags)))
17258c2ecf20Sopenharmony_ci			return -EFAULT;
17268c2ecf20Sopenharmony_ci		rtnl_lock();
17278c2ecf20Sopenharmony_ci		mroute_clean_tables(mrt, flags);
17288c2ecf20Sopenharmony_ci		rtnl_unlock();
17298c2ecf20Sopenharmony_ci		return 0;
17308c2ecf20Sopenharmony_ci	}
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	/*
17338c2ecf20Sopenharmony_ci	 *	Control PIM assert (to activate pim will activate assert)
17348c2ecf20Sopenharmony_ci	 */
17358c2ecf20Sopenharmony_ci	case MRT6_ASSERT:
17368c2ecf20Sopenharmony_ci	{
17378c2ecf20Sopenharmony_ci		int v;
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci		if (optlen != sizeof(v))
17408c2ecf20Sopenharmony_ci			return -EINVAL;
17418c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&v, optval, sizeof(v)))
17428c2ecf20Sopenharmony_ci			return -EFAULT;
17438c2ecf20Sopenharmony_ci		mrt->mroute_do_assert = v;
17448c2ecf20Sopenharmony_ci		return 0;
17458c2ecf20Sopenharmony_ci	}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
17488c2ecf20Sopenharmony_ci	case MRT6_PIM:
17498c2ecf20Sopenharmony_ci	{
17508c2ecf20Sopenharmony_ci		int v;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci		if (optlen != sizeof(v))
17538c2ecf20Sopenharmony_ci			return -EINVAL;
17548c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&v, optval, sizeof(v)))
17558c2ecf20Sopenharmony_ci			return -EFAULT;
17568c2ecf20Sopenharmony_ci		v = !!v;
17578c2ecf20Sopenharmony_ci		rtnl_lock();
17588c2ecf20Sopenharmony_ci		ret = 0;
17598c2ecf20Sopenharmony_ci		if (v != mrt->mroute_do_pim) {
17608c2ecf20Sopenharmony_ci			mrt->mroute_do_pim = v;
17618c2ecf20Sopenharmony_ci			mrt->mroute_do_assert = v;
17628c2ecf20Sopenharmony_ci		}
17638c2ecf20Sopenharmony_ci		rtnl_unlock();
17648c2ecf20Sopenharmony_ci		return ret;
17658c2ecf20Sopenharmony_ci	}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci#endif
17688c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
17698c2ecf20Sopenharmony_ci	case MRT6_TABLE:
17708c2ecf20Sopenharmony_ci	{
17718c2ecf20Sopenharmony_ci		u32 v;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci		if (optlen != sizeof(u32))
17748c2ecf20Sopenharmony_ci			return -EINVAL;
17758c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&v, optval, sizeof(v)))
17768c2ecf20Sopenharmony_ci			return -EFAULT;
17778c2ecf20Sopenharmony_ci		/* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */
17788c2ecf20Sopenharmony_ci		if (v != RT_TABLE_DEFAULT && v >= 100000000)
17798c2ecf20Sopenharmony_ci			return -EINVAL;
17808c2ecf20Sopenharmony_ci		if (sk == rcu_access_pointer(mrt->mroute_sk))
17818c2ecf20Sopenharmony_ci			return -EBUSY;
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci		rtnl_lock();
17848c2ecf20Sopenharmony_ci		ret = 0;
17858c2ecf20Sopenharmony_ci		mrt = ip6mr_new_table(net, v);
17868c2ecf20Sopenharmony_ci		if (IS_ERR(mrt))
17878c2ecf20Sopenharmony_ci			ret = PTR_ERR(mrt);
17888c2ecf20Sopenharmony_ci		else
17898c2ecf20Sopenharmony_ci			raw6_sk(sk)->ip6mr_table = v;
17908c2ecf20Sopenharmony_ci		rtnl_unlock();
17918c2ecf20Sopenharmony_ci		return ret;
17928c2ecf20Sopenharmony_ci	}
17938c2ecf20Sopenharmony_ci#endif
17948c2ecf20Sopenharmony_ci	/*
17958c2ecf20Sopenharmony_ci	 *	Spurious command, or MRT6_VERSION which you cannot
17968c2ecf20Sopenharmony_ci	 *	set.
17978c2ecf20Sopenharmony_ci	 */
17988c2ecf20Sopenharmony_ci	default:
17998c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ci}
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci/*
18048c2ecf20Sopenharmony_ci *	Getsock opt support for the multicast routing system.
18058c2ecf20Sopenharmony_ci */
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ciint ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval,
18088c2ecf20Sopenharmony_ci			  int __user *optlen)
18098c2ecf20Sopenharmony_ci{
18108c2ecf20Sopenharmony_ci	int olr;
18118c2ecf20Sopenharmony_ci	int val;
18128c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
18138c2ecf20Sopenharmony_ci	struct mr_table *mrt;
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	if (sk->sk_type != SOCK_RAW ||
18168c2ecf20Sopenharmony_ci	    inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
18178c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
18208c2ecf20Sopenharmony_ci	if (!mrt)
18218c2ecf20Sopenharmony_ci		return -ENOENT;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	switch (optname) {
18248c2ecf20Sopenharmony_ci	case MRT6_VERSION:
18258c2ecf20Sopenharmony_ci		val = 0x0305;
18268c2ecf20Sopenharmony_ci		break;
18278c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
18288c2ecf20Sopenharmony_ci	case MRT6_PIM:
18298c2ecf20Sopenharmony_ci		val = mrt->mroute_do_pim;
18308c2ecf20Sopenharmony_ci		break;
18318c2ecf20Sopenharmony_ci#endif
18328c2ecf20Sopenharmony_ci	case MRT6_ASSERT:
18338c2ecf20Sopenharmony_ci		val = mrt->mroute_do_assert;
18348c2ecf20Sopenharmony_ci		break;
18358c2ecf20Sopenharmony_ci	default:
18368c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
18378c2ecf20Sopenharmony_ci	}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	if (get_user(olr, optlen))
18408c2ecf20Sopenharmony_ci		return -EFAULT;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	olr = min_t(int, olr, sizeof(int));
18438c2ecf20Sopenharmony_ci	if (olr < 0)
18448c2ecf20Sopenharmony_ci		return -EINVAL;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	if (put_user(olr, optlen))
18478c2ecf20Sopenharmony_ci		return -EFAULT;
18488c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, olr))
18498c2ecf20Sopenharmony_ci		return -EFAULT;
18508c2ecf20Sopenharmony_ci	return 0;
18518c2ecf20Sopenharmony_ci}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci/*
18548c2ecf20Sopenharmony_ci *	The IP multicast ioctl support routines.
18558c2ecf20Sopenharmony_ci */
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ciint ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
18588c2ecf20Sopenharmony_ci{
18598c2ecf20Sopenharmony_ci	struct sioc_sg_req6 sr;
18608c2ecf20Sopenharmony_ci	struct sioc_mif_req6 vr;
18618c2ecf20Sopenharmony_ci	struct vif_device *vif;
18628c2ecf20Sopenharmony_ci	struct mfc6_cache *c;
18638c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
18648c2ecf20Sopenharmony_ci	struct mr_table *mrt;
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
18678c2ecf20Sopenharmony_ci	if (!mrt)
18688c2ecf20Sopenharmony_ci		return -ENOENT;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	switch (cmd) {
18718c2ecf20Sopenharmony_ci	case SIOCGETMIFCNT_IN6:
18728c2ecf20Sopenharmony_ci		if (copy_from_user(&vr, arg, sizeof(vr)))
18738c2ecf20Sopenharmony_ci			return -EFAULT;
18748c2ecf20Sopenharmony_ci		if (vr.mifi >= mrt->maxvif)
18758c2ecf20Sopenharmony_ci			return -EINVAL;
18768c2ecf20Sopenharmony_ci		vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
18778c2ecf20Sopenharmony_ci		read_lock(&mrt_lock);
18788c2ecf20Sopenharmony_ci		vif = &mrt->vif_table[vr.mifi];
18798c2ecf20Sopenharmony_ci		if (VIF_EXISTS(mrt, vr.mifi)) {
18808c2ecf20Sopenharmony_ci			vr.icount = vif->pkt_in;
18818c2ecf20Sopenharmony_ci			vr.ocount = vif->pkt_out;
18828c2ecf20Sopenharmony_ci			vr.ibytes = vif->bytes_in;
18838c2ecf20Sopenharmony_ci			vr.obytes = vif->bytes_out;
18848c2ecf20Sopenharmony_ci			read_unlock(&mrt_lock);
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci			if (copy_to_user(arg, &vr, sizeof(vr)))
18878c2ecf20Sopenharmony_ci				return -EFAULT;
18888c2ecf20Sopenharmony_ci			return 0;
18898c2ecf20Sopenharmony_ci		}
18908c2ecf20Sopenharmony_ci		read_unlock(&mrt_lock);
18918c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
18928c2ecf20Sopenharmony_ci	case SIOCGETSGCNT_IN6:
18938c2ecf20Sopenharmony_ci		if (copy_from_user(&sr, arg, sizeof(sr)))
18948c2ecf20Sopenharmony_ci			return -EFAULT;
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci		rcu_read_lock();
18978c2ecf20Sopenharmony_ci		c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr);
18988c2ecf20Sopenharmony_ci		if (c) {
18998c2ecf20Sopenharmony_ci			sr.pktcnt = c->_c.mfc_un.res.pkt;
19008c2ecf20Sopenharmony_ci			sr.bytecnt = c->_c.mfc_un.res.bytes;
19018c2ecf20Sopenharmony_ci			sr.wrong_if = c->_c.mfc_un.res.wrong_if;
19028c2ecf20Sopenharmony_ci			rcu_read_unlock();
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci			if (copy_to_user(arg, &sr, sizeof(sr)))
19058c2ecf20Sopenharmony_ci				return -EFAULT;
19068c2ecf20Sopenharmony_ci			return 0;
19078c2ecf20Sopenharmony_ci		}
19088c2ecf20Sopenharmony_ci		rcu_read_unlock();
19098c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
19108c2ecf20Sopenharmony_ci	default:
19118c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
19128c2ecf20Sopenharmony_ci	}
19138c2ecf20Sopenharmony_ci}
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
19168c2ecf20Sopenharmony_cistruct compat_sioc_sg_req6 {
19178c2ecf20Sopenharmony_ci	struct sockaddr_in6 src;
19188c2ecf20Sopenharmony_ci	struct sockaddr_in6 grp;
19198c2ecf20Sopenharmony_ci	compat_ulong_t pktcnt;
19208c2ecf20Sopenharmony_ci	compat_ulong_t bytecnt;
19218c2ecf20Sopenharmony_ci	compat_ulong_t wrong_if;
19228c2ecf20Sopenharmony_ci};
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_cistruct compat_sioc_mif_req6 {
19258c2ecf20Sopenharmony_ci	mifi_t	mifi;
19268c2ecf20Sopenharmony_ci	compat_ulong_t icount;
19278c2ecf20Sopenharmony_ci	compat_ulong_t ocount;
19288c2ecf20Sopenharmony_ci	compat_ulong_t ibytes;
19298c2ecf20Sopenharmony_ci	compat_ulong_t obytes;
19308c2ecf20Sopenharmony_ci};
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ciint ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
19338c2ecf20Sopenharmony_ci{
19348c2ecf20Sopenharmony_ci	struct compat_sioc_sg_req6 sr;
19358c2ecf20Sopenharmony_ci	struct compat_sioc_mif_req6 vr;
19368c2ecf20Sopenharmony_ci	struct vif_device *vif;
19378c2ecf20Sopenharmony_ci	struct mfc6_cache *c;
19388c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
19398c2ecf20Sopenharmony_ci	struct mr_table *mrt;
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
19428c2ecf20Sopenharmony_ci	if (!mrt)
19438c2ecf20Sopenharmony_ci		return -ENOENT;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	switch (cmd) {
19468c2ecf20Sopenharmony_ci	case SIOCGETMIFCNT_IN6:
19478c2ecf20Sopenharmony_ci		if (copy_from_user(&vr, arg, sizeof(vr)))
19488c2ecf20Sopenharmony_ci			return -EFAULT;
19498c2ecf20Sopenharmony_ci		if (vr.mifi >= mrt->maxvif)
19508c2ecf20Sopenharmony_ci			return -EINVAL;
19518c2ecf20Sopenharmony_ci		vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
19528c2ecf20Sopenharmony_ci		read_lock(&mrt_lock);
19538c2ecf20Sopenharmony_ci		vif = &mrt->vif_table[vr.mifi];
19548c2ecf20Sopenharmony_ci		if (VIF_EXISTS(mrt, vr.mifi)) {
19558c2ecf20Sopenharmony_ci			vr.icount = vif->pkt_in;
19568c2ecf20Sopenharmony_ci			vr.ocount = vif->pkt_out;
19578c2ecf20Sopenharmony_ci			vr.ibytes = vif->bytes_in;
19588c2ecf20Sopenharmony_ci			vr.obytes = vif->bytes_out;
19598c2ecf20Sopenharmony_ci			read_unlock(&mrt_lock);
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci			if (copy_to_user(arg, &vr, sizeof(vr)))
19628c2ecf20Sopenharmony_ci				return -EFAULT;
19638c2ecf20Sopenharmony_ci			return 0;
19648c2ecf20Sopenharmony_ci		}
19658c2ecf20Sopenharmony_ci		read_unlock(&mrt_lock);
19668c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
19678c2ecf20Sopenharmony_ci	case SIOCGETSGCNT_IN6:
19688c2ecf20Sopenharmony_ci		if (copy_from_user(&sr, arg, sizeof(sr)))
19698c2ecf20Sopenharmony_ci			return -EFAULT;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci		rcu_read_lock();
19728c2ecf20Sopenharmony_ci		c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr);
19738c2ecf20Sopenharmony_ci		if (c) {
19748c2ecf20Sopenharmony_ci			sr.pktcnt = c->_c.mfc_un.res.pkt;
19758c2ecf20Sopenharmony_ci			sr.bytecnt = c->_c.mfc_un.res.bytes;
19768c2ecf20Sopenharmony_ci			sr.wrong_if = c->_c.mfc_un.res.wrong_if;
19778c2ecf20Sopenharmony_ci			rcu_read_unlock();
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci			if (copy_to_user(arg, &sr, sizeof(sr)))
19808c2ecf20Sopenharmony_ci				return -EFAULT;
19818c2ecf20Sopenharmony_ci			return 0;
19828c2ecf20Sopenharmony_ci		}
19838c2ecf20Sopenharmony_ci		rcu_read_unlock();
19848c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
19858c2ecf20Sopenharmony_ci	default:
19868c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
19878c2ecf20Sopenharmony_ci	}
19888c2ecf20Sopenharmony_ci}
19898c2ecf20Sopenharmony_ci#endif
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_cistatic inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
19928c2ecf20Sopenharmony_ci{
19938c2ecf20Sopenharmony_ci	IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
19948c2ecf20Sopenharmony_ci		      IPSTATS_MIB_OUTFORWDATAGRAMS);
19958c2ecf20Sopenharmony_ci	IP6_ADD_STATS(net, ip6_dst_idev(skb_dst(skb)),
19968c2ecf20Sopenharmony_ci		      IPSTATS_MIB_OUTOCTETS, skb->len);
19978c2ecf20Sopenharmony_ci	return dst_output(net, sk, skb);
19988c2ecf20Sopenharmony_ci}
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci/*
20018c2ecf20Sopenharmony_ci *	Processing handlers for ip6mr_forward
20028c2ecf20Sopenharmony_ci */
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_cistatic int ip6mr_forward2(struct net *net, struct mr_table *mrt,
20058c2ecf20Sopenharmony_ci			  struct sk_buff *skb, int vifi)
20068c2ecf20Sopenharmony_ci{
20078c2ecf20Sopenharmony_ci	struct ipv6hdr *ipv6h;
20088c2ecf20Sopenharmony_ci	struct vif_device *vif = &mrt->vif_table[vifi];
20098c2ecf20Sopenharmony_ci	struct net_device *dev;
20108c2ecf20Sopenharmony_ci	struct dst_entry *dst;
20118c2ecf20Sopenharmony_ci	struct flowi6 fl6;
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	if (!vif->dev)
20148c2ecf20Sopenharmony_ci		goto out_free;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2
20178c2ecf20Sopenharmony_ci	if (vif->flags & MIFF_REGISTER) {
20188c2ecf20Sopenharmony_ci		vif->pkt_out++;
20198c2ecf20Sopenharmony_ci		vif->bytes_out += skb->len;
20208c2ecf20Sopenharmony_ci		vif->dev->stats.tx_bytes += skb->len;
20218c2ecf20Sopenharmony_ci		vif->dev->stats.tx_packets++;
20228c2ecf20Sopenharmony_ci		ip6mr_cache_report(mrt, skb, vifi, MRT6MSG_WHOLEPKT);
20238c2ecf20Sopenharmony_ci		goto out_free;
20248c2ecf20Sopenharmony_ci	}
20258c2ecf20Sopenharmony_ci#endif
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	fl6 = (struct flowi6) {
20308c2ecf20Sopenharmony_ci		.flowi6_oif = vif->link,
20318c2ecf20Sopenharmony_ci		.daddr = ipv6h->daddr,
20328c2ecf20Sopenharmony_ci	};
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	dst = ip6_route_output(net, NULL, &fl6);
20358c2ecf20Sopenharmony_ci	if (dst->error) {
20368c2ecf20Sopenharmony_ci		dst_release(dst);
20378c2ecf20Sopenharmony_ci		goto out_free;
20388c2ecf20Sopenharmony_ci	}
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
20418c2ecf20Sopenharmony_ci	skb_dst_set(skb, dst);
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	/*
20448c2ecf20Sopenharmony_ci	 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
20458c2ecf20Sopenharmony_ci	 * not only before forwarding, but after forwarding on all output
20468c2ecf20Sopenharmony_ci	 * interfaces. It is clear, if mrouter runs a multicasting
20478c2ecf20Sopenharmony_ci	 * program, it should receive packets not depending to what interface
20488c2ecf20Sopenharmony_ci	 * program is joined.
20498c2ecf20Sopenharmony_ci	 * If we will not make it, the program will have to join on all
20508c2ecf20Sopenharmony_ci	 * interfaces. On the other hand, multihoming host (or router, but
20518c2ecf20Sopenharmony_ci	 * not mrouter) cannot join to more than one interface - it will
20528c2ecf20Sopenharmony_ci	 * result in receiving multiple packets.
20538c2ecf20Sopenharmony_ci	 */
20548c2ecf20Sopenharmony_ci	dev = vif->dev;
20558c2ecf20Sopenharmony_ci	skb->dev = dev;
20568c2ecf20Sopenharmony_ci	vif->pkt_out++;
20578c2ecf20Sopenharmony_ci	vif->bytes_out += skb->len;
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	/* We are about to write */
20608c2ecf20Sopenharmony_ci	/* XXX: extension headers? */
20618c2ecf20Sopenharmony_ci	if (skb_cow(skb, sizeof(*ipv6h) + LL_RESERVED_SPACE(dev)))
20628c2ecf20Sopenharmony_ci		goto out_free;
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
20658c2ecf20Sopenharmony_ci	ipv6h->hop_limit--;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	IP6CB(skb)->flags |= IP6SKB_FORWARDED;
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD,
20708c2ecf20Sopenharmony_ci		       net, NULL, skb, skb->dev, dev,
20718c2ecf20Sopenharmony_ci		       ip6mr_forward2_finish);
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ciout_free:
20748c2ecf20Sopenharmony_ci	kfree_skb(skb);
20758c2ecf20Sopenharmony_ci	return 0;
20768c2ecf20Sopenharmony_ci}
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_cistatic int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev)
20798c2ecf20Sopenharmony_ci{
20808c2ecf20Sopenharmony_ci	int ct;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	for (ct = mrt->maxvif - 1; ct >= 0; ct--) {
20838c2ecf20Sopenharmony_ci		if (mrt->vif_table[ct].dev == dev)
20848c2ecf20Sopenharmony_ci			break;
20858c2ecf20Sopenharmony_ci	}
20868c2ecf20Sopenharmony_ci	return ct;
20878c2ecf20Sopenharmony_ci}
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_cistatic void ip6_mr_forward(struct net *net, struct mr_table *mrt,
20908c2ecf20Sopenharmony_ci			   struct net_device *dev, struct sk_buff *skb,
20918c2ecf20Sopenharmony_ci			   struct mfc6_cache *c)
20928c2ecf20Sopenharmony_ci{
20938c2ecf20Sopenharmony_ci	int psend = -1;
20948c2ecf20Sopenharmony_ci	int vif, ct;
20958c2ecf20Sopenharmony_ci	int true_vifi = ip6mr_find_vif(mrt, dev);
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	vif = c->_c.mfc_parent;
20988c2ecf20Sopenharmony_ci	c->_c.mfc_un.res.pkt++;
20998c2ecf20Sopenharmony_ci	c->_c.mfc_un.res.bytes += skb->len;
21008c2ecf20Sopenharmony_ci	c->_c.mfc_un.res.lastuse = jiffies;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	if (ipv6_addr_any(&c->mf6c_origin) && true_vifi >= 0) {
21038c2ecf20Sopenharmony_ci		struct mfc6_cache *cache_proxy;
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci		/* For an (*,G) entry, we only check that the incoming
21068c2ecf20Sopenharmony_ci		 * interface is part of the static tree.
21078c2ecf20Sopenharmony_ci		 */
21088c2ecf20Sopenharmony_ci		rcu_read_lock();
21098c2ecf20Sopenharmony_ci		cache_proxy = mr_mfc_find_any_parent(mrt, vif);
21108c2ecf20Sopenharmony_ci		if (cache_proxy &&
21118c2ecf20Sopenharmony_ci		    cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255) {
21128c2ecf20Sopenharmony_ci			rcu_read_unlock();
21138c2ecf20Sopenharmony_ci			goto forward;
21148c2ecf20Sopenharmony_ci		}
21158c2ecf20Sopenharmony_ci		rcu_read_unlock();
21168c2ecf20Sopenharmony_ci	}
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	/*
21198c2ecf20Sopenharmony_ci	 * Wrong interface: drop packet and (maybe) send PIM assert.
21208c2ecf20Sopenharmony_ci	 */
21218c2ecf20Sopenharmony_ci	if (mrt->vif_table[vif].dev != dev) {
21228c2ecf20Sopenharmony_ci		c->_c.mfc_un.res.wrong_if++;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci		if (true_vifi >= 0 && mrt->mroute_do_assert &&
21258c2ecf20Sopenharmony_ci		    /* pimsm uses asserts, when switching from RPT to SPT,
21268c2ecf20Sopenharmony_ci		       so that we cannot check that packet arrived on an oif.
21278c2ecf20Sopenharmony_ci		       It is bad, but otherwise we would need to move pretty
21288c2ecf20Sopenharmony_ci		       large chunk of pimd to kernel. Ough... --ANK
21298c2ecf20Sopenharmony_ci		     */
21308c2ecf20Sopenharmony_ci		    (mrt->mroute_do_pim ||
21318c2ecf20Sopenharmony_ci		     c->_c.mfc_un.res.ttls[true_vifi] < 255) &&
21328c2ecf20Sopenharmony_ci		    time_after(jiffies,
21338c2ecf20Sopenharmony_ci			       c->_c.mfc_un.res.last_assert +
21348c2ecf20Sopenharmony_ci			       MFC_ASSERT_THRESH)) {
21358c2ecf20Sopenharmony_ci			c->_c.mfc_un.res.last_assert = jiffies;
21368c2ecf20Sopenharmony_ci			ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF);
21378c2ecf20Sopenharmony_ci		}
21388c2ecf20Sopenharmony_ci		goto dont_forward;
21398c2ecf20Sopenharmony_ci	}
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ciforward:
21428c2ecf20Sopenharmony_ci	mrt->vif_table[vif].pkt_in++;
21438c2ecf20Sopenharmony_ci	mrt->vif_table[vif].bytes_in += skb->len;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	/*
21468c2ecf20Sopenharmony_ci	 *	Forward the frame
21478c2ecf20Sopenharmony_ci	 */
21488c2ecf20Sopenharmony_ci	if (ipv6_addr_any(&c->mf6c_origin) &&
21498c2ecf20Sopenharmony_ci	    ipv6_addr_any(&c->mf6c_mcastgrp)) {
21508c2ecf20Sopenharmony_ci		if (true_vifi >= 0 &&
21518c2ecf20Sopenharmony_ci		    true_vifi != c->_c.mfc_parent &&
21528c2ecf20Sopenharmony_ci		    ipv6_hdr(skb)->hop_limit >
21538c2ecf20Sopenharmony_ci				c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) {
21548c2ecf20Sopenharmony_ci			/* It's an (*,*) entry and the packet is not coming from
21558c2ecf20Sopenharmony_ci			 * the upstream: forward the packet to the upstream
21568c2ecf20Sopenharmony_ci			 * only.
21578c2ecf20Sopenharmony_ci			 */
21588c2ecf20Sopenharmony_ci			psend = c->_c.mfc_parent;
21598c2ecf20Sopenharmony_ci			goto last_forward;
21608c2ecf20Sopenharmony_ci		}
21618c2ecf20Sopenharmony_ci		goto dont_forward;
21628c2ecf20Sopenharmony_ci	}
21638c2ecf20Sopenharmony_ci	for (ct = c->_c.mfc_un.res.maxvif - 1;
21648c2ecf20Sopenharmony_ci	     ct >= c->_c.mfc_un.res.minvif; ct--) {
21658c2ecf20Sopenharmony_ci		/* For (*,G) entry, don't forward to the incoming interface */
21668c2ecf20Sopenharmony_ci		if ((!ipv6_addr_any(&c->mf6c_origin) || ct != true_vifi) &&
21678c2ecf20Sopenharmony_ci		    ipv6_hdr(skb)->hop_limit > c->_c.mfc_un.res.ttls[ct]) {
21688c2ecf20Sopenharmony_ci			if (psend != -1) {
21698c2ecf20Sopenharmony_ci				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
21708c2ecf20Sopenharmony_ci				if (skb2)
21718c2ecf20Sopenharmony_ci					ip6mr_forward2(net, mrt, skb2, psend);
21728c2ecf20Sopenharmony_ci			}
21738c2ecf20Sopenharmony_ci			psend = ct;
21748c2ecf20Sopenharmony_ci		}
21758c2ecf20Sopenharmony_ci	}
21768c2ecf20Sopenharmony_cilast_forward:
21778c2ecf20Sopenharmony_ci	if (psend != -1) {
21788c2ecf20Sopenharmony_ci		ip6mr_forward2(net, mrt, skb, psend);
21798c2ecf20Sopenharmony_ci		return;
21808c2ecf20Sopenharmony_ci	}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_cidont_forward:
21838c2ecf20Sopenharmony_ci	kfree_skb(skb);
21848c2ecf20Sopenharmony_ci}
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci/*
21888c2ecf20Sopenharmony_ci *	Multicast packets for forwarding arrive here
21898c2ecf20Sopenharmony_ci */
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ciint ip6_mr_input(struct sk_buff *skb)
21928c2ecf20Sopenharmony_ci{
21938c2ecf20Sopenharmony_ci	struct mfc6_cache *cache;
21948c2ecf20Sopenharmony_ci	struct net *net = dev_net(skb->dev);
21958c2ecf20Sopenharmony_ci	struct mr_table *mrt;
21968c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
21978c2ecf20Sopenharmony_ci		.flowi6_iif	= skb->dev->ifindex,
21988c2ecf20Sopenharmony_ci		.flowi6_mark	= skb->mark,
21998c2ecf20Sopenharmony_ci	};
22008c2ecf20Sopenharmony_ci	int err;
22018c2ecf20Sopenharmony_ci	struct net_device *dev;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	/* skb->dev passed in is the master dev for vrfs.
22048c2ecf20Sopenharmony_ci	 * Get the proper interface that does have a vif associated with it.
22058c2ecf20Sopenharmony_ci	 */
22068c2ecf20Sopenharmony_ci	dev = skb->dev;
22078c2ecf20Sopenharmony_ci	if (netif_is_l3_master(skb->dev)) {
22088c2ecf20Sopenharmony_ci		dev = dev_get_by_index_rcu(net, IPCB(skb)->iif);
22098c2ecf20Sopenharmony_ci		if (!dev) {
22108c2ecf20Sopenharmony_ci			kfree_skb(skb);
22118c2ecf20Sopenharmony_ci			return -ENODEV;
22128c2ecf20Sopenharmony_ci		}
22138c2ecf20Sopenharmony_ci	}
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	err = ip6mr_fib_lookup(net, &fl6, &mrt);
22168c2ecf20Sopenharmony_ci	if (err < 0) {
22178c2ecf20Sopenharmony_ci		kfree_skb(skb);
22188c2ecf20Sopenharmony_ci		return err;
22198c2ecf20Sopenharmony_ci	}
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	read_lock(&mrt_lock);
22228c2ecf20Sopenharmony_ci	cache = ip6mr_cache_find(mrt,
22238c2ecf20Sopenharmony_ci				 &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
22248c2ecf20Sopenharmony_ci	if (!cache) {
22258c2ecf20Sopenharmony_ci		int vif = ip6mr_find_vif(mrt, dev);
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci		if (vif >= 0)
22288c2ecf20Sopenharmony_ci			cache = ip6mr_cache_find_any(mrt,
22298c2ecf20Sopenharmony_ci						     &ipv6_hdr(skb)->daddr,
22308c2ecf20Sopenharmony_ci						     vif);
22318c2ecf20Sopenharmony_ci	}
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci	/*
22348c2ecf20Sopenharmony_ci	 *	No usable cache entry
22358c2ecf20Sopenharmony_ci	 */
22368c2ecf20Sopenharmony_ci	if (!cache) {
22378c2ecf20Sopenharmony_ci		int vif;
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci		vif = ip6mr_find_vif(mrt, dev);
22408c2ecf20Sopenharmony_ci		if (vif >= 0) {
22418c2ecf20Sopenharmony_ci			int err = ip6mr_cache_unresolved(mrt, vif, skb, dev);
22428c2ecf20Sopenharmony_ci			read_unlock(&mrt_lock);
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci			return err;
22458c2ecf20Sopenharmony_ci		}
22468c2ecf20Sopenharmony_ci		read_unlock(&mrt_lock);
22478c2ecf20Sopenharmony_ci		kfree_skb(skb);
22488c2ecf20Sopenharmony_ci		return -ENODEV;
22498c2ecf20Sopenharmony_ci	}
22508c2ecf20Sopenharmony_ci
22518c2ecf20Sopenharmony_ci	ip6_mr_forward(net, mrt, dev, skb, cache);
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	read_unlock(&mrt_lock);
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	return 0;
22568c2ecf20Sopenharmony_ci}
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ciint ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
22598c2ecf20Sopenharmony_ci		    u32 portid)
22608c2ecf20Sopenharmony_ci{
22618c2ecf20Sopenharmony_ci	int err;
22628c2ecf20Sopenharmony_ci	struct mr_table *mrt;
22638c2ecf20Sopenharmony_ci	struct mfc6_cache *cache;
22648c2ecf20Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
22678c2ecf20Sopenharmony_ci	if (!mrt)
22688c2ecf20Sopenharmony_ci		return -ENOENT;
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci	read_lock(&mrt_lock);
22718c2ecf20Sopenharmony_ci	cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr);
22728c2ecf20Sopenharmony_ci	if (!cache && skb->dev) {
22738c2ecf20Sopenharmony_ci		int vif = ip6mr_find_vif(mrt, skb->dev);
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci		if (vif >= 0)
22768c2ecf20Sopenharmony_ci			cache = ip6mr_cache_find_any(mrt, &rt->rt6i_dst.addr,
22778c2ecf20Sopenharmony_ci						     vif);
22788c2ecf20Sopenharmony_ci	}
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	if (!cache) {
22818c2ecf20Sopenharmony_ci		struct sk_buff *skb2;
22828c2ecf20Sopenharmony_ci		struct ipv6hdr *iph;
22838c2ecf20Sopenharmony_ci		struct net_device *dev;
22848c2ecf20Sopenharmony_ci		int vif;
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci		dev = skb->dev;
22878c2ecf20Sopenharmony_ci		if (!dev || (vif = ip6mr_find_vif(mrt, dev)) < 0) {
22888c2ecf20Sopenharmony_ci			read_unlock(&mrt_lock);
22898c2ecf20Sopenharmony_ci			return -ENODEV;
22908c2ecf20Sopenharmony_ci		}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci		/* really correct? */
22938c2ecf20Sopenharmony_ci		skb2 = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
22948c2ecf20Sopenharmony_ci		if (!skb2) {
22958c2ecf20Sopenharmony_ci			read_unlock(&mrt_lock);
22968c2ecf20Sopenharmony_ci			return -ENOMEM;
22978c2ecf20Sopenharmony_ci		}
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_ci		NETLINK_CB(skb2).portid = portid;
23008c2ecf20Sopenharmony_ci		skb_reset_transport_header(skb2);
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci		skb_put(skb2, sizeof(struct ipv6hdr));
23038c2ecf20Sopenharmony_ci		skb_reset_network_header(skb2);
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci		iph = ipv6_hdr(skb2);
23068c2ecf20Sopenharmony_ci		iph->version = 0;
23078c2ecf20Sopenharmony_ci		iph->priority = 0;
23088c2ecf20Sopenharmony_ci		iph->flow_lbl[0] = 0;
23098c2ecf20Sopenharmony_ci		iph->flow_lbl[1] = 0;
23108c2ecf20Sopenharmony_ci		iph->flow_lbl[2] = 0;
23118c2ecf20Sopenharmony_ci		iph->payload_len = 0;
23128c2ecf20Sopenharmony_ci		iph->nexthdr = IPPROTO_NONE;
23138c2ecf20Sopenharmony_ci		iph->hop_limit = 0;
23148c2ecf20Sopenharmony_ci		iph->saddr = rt->rt6i_src.addr;
23158c2ecf20Sopenharmony_ci		iph->daddr = rt->rt6i_dst.addr;
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci		err = ip6mr_cache_unresolved(mrt, vif, skb2, dev);
23188c2ecf20Sopenharmony_ci		read_unlock(&mrt_lock);
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci		return err;
23218c2ecf20Sopenharmony_ci	}
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	err = mr_fill_mroute(mrt, skb, &cache->_c, rtm);
23248c2ecf20Sopenharmony_ci	read_unlock(&mrt_lock);
23258c2ecf20Sopenharmony_ci	return err;
23268c2ecf20Sopenharmony_ci}
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_cistatic int ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
23298c2ecf20Sopenharmony_ci			     u32 portid, u32 seq, struct mfc6_cache *c, int cmd,
23308c2ecf20Sopenharmony_ci			     int flags)
23318c2ecf20Sopenharmony_ci{
23328c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
23338c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
23348c2ecf20Sopenharmony_ci	int err;
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags);
23378c2ecf20Sopenharmony_ci	if (!nlh)
23388c2ecf20Sopenharmony_ci		return -EMSGSIZE;
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
23418c2ecf20Sopenharmony_ci	rtm->rtm_family   = RTNL_FAMILY_IP6MR;
23428c2ecf20Sopenharmony_ci	rtm->rtm_dst_len  = 128;
23438c2ecf20Sopenharmony_ci	rtm->rtm_src_len  = 128;
23448c2ecf20Sopenharmony_ci	rtm->rtm_tos      = 0;
23458c2ecf20Sopenharmony_ci	rtm->rtm_table    = mrt->id;
23468c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, RTA_TABLE, mrt->id))
23478c2ecf20Sopenharmony_ci		goto nla_put_failure;
23488c2ecf20Sopenharmony_ci	rtm->rtm_type = RTN_MULTICAST;
23498c2ecf20Sopenharmony_ci	rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
23508c2ecf20Sopenharmony_ci	if (c->_c.mfc_flags & MFC_STATIC)
23518c2ecf20Sopenharmony_ci		rtm->rtm_protocol = RTPROT_STATIC;
23528c2ecf20Sopenharmony_ci	else
23538c2ecf20Sopenharmony_ci		rtm->rtm_protocol = RTPROT_MROUTED;
23548c2ecf20Sopenharmony_ci	rtm->rtm_flags    = 0;
23558c2ecf20Sopenharmony_ci
23568c2ecf20Sopenharmony_ci	if (nla_put_in6_addr(skb, RTA_SRC, &c->mf6c_origin) ||
23578c2ecf20Sopenharmony_ci	    nla_put_in6_addr(skb, RTA_DST, &c->mf6c_mcastgrp))
23588c2ecf20Sopenharmony_ci		goto nla_put_failure;
23598c2ecf20Sopenharmony_ci	err = mr_fill_mroute(mrt, skb, &c->_c, rtm);
23608c2ecf20Sopenharmony_ci	/* do not break the dump if cache is unresolved */
23618c2ecf20Sopenharmony_ci	if (err < 0 && err != -ENOENT)
23628c2ecf20Sopenharmony_ci		goto nla_put_failure;
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
23658c2ecf20Sopenharmony_ci	return 0;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_cinla_put_failure:
23688c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
23698c2ecf20Sopenharmony_ci	return -EMSGSIZE;
23708c2ecf20Sopenharmony_ci}
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_cistatic int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
23738c2ecf20Sopenharmony_ci			      u32 portid, u32 seq, struct mr_mfc *c,
23748c2ecf20Sopenharmony_ci			      int cmd, int flags)
23758c2ecf20Sopenharmony_ci{
23768c2ecf20Sopenharmony_ci	return ip6mr_fill_mroute(mrt, skb, portid, seq, (struct mfc6_cache *)c,
23778c2ecf20Sopenharmony_ci				 cmd, flags);
23788c2ecf20Sopenharmony_ci}
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_cistatic int mr6_msgsize(bool unresolved, int maxvif)
23818c2ecf20Sopenharmony_ci{
23828c2ecf20Sopenharmony_ci	size_t len =
23838c2ecf20Sopenharmony_ci		NLMSG_ALIGN(sizeof(struct rtmsg))
23848c2ecf20Sopenharmony_ci		+ nla_total_size(4)	/* RTA_TABLE */
23858c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(struct in6_addr))	/* RTA_SRC */
23868c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(struct in6_addr))	/* RTA_DST */
23878c2ecf20Sopenharmony_ci		;
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci	if (!unresolved)
23908c2ecf20Sopenharmony_ci		len = len
23918c2ecf20Sopenharmony_ci		      + nla_total_size(4)	/* RTA_IIF */
23928c2ecf20Sopenharmony_ci		      + nla_total_size(0)	/* RTA_MULTIPATH */
23938c2ecf20Sopenharmony_ci		      + maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
23948c2ecf20Sopenharmony_ci						/* RTA_MFC_STATS */
23958c2ecf20Sopenharmony_ci		      + nla_total_size_64bit(sizeof(struct rta_mfc_stats))
23968c2ecf20Sopenharmony_ci		;
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	return len;
23998c2ecf20Sopenharmony_ci}
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_cistatic void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
24028c2ecf20Sopenharmony_ci			      int cmd)
24038c2ecf20Sopenharmony_ci{
24048c2ecf20Sopenharmony_ci	struct net *net = read_pnet(&mrt->net);
24058c2ecf20Sopenharmony_ci	struct sk_buff *skb;
24068c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ci	skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif),
24098c2ecf20Sopenharmony_ci			GFP_ATOMIC);
24108c2ecf20Sopenharmony_ci	if (!skb)
24118c2ecf20Sopenharmony_ci		goto errout;
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci	err = ip6mr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0);
24148c2ecf20Sopenharmony_ci	if (err < 0)
24158c2ecf20Sopenharmony_ci		goto errout;
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE, NULL, GFP_ATOMIC);
24188c2ecf20Sopenharmony_ci	return;
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_cierrout:
24218c2ecf20Sopenharmony_ci	kfree_skb(skb);
24228c2ecf20Sopenharmony_ci	if (err < 0)
24238c2ecf20Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err);
24248c2ecf20Sopenharmony_ci}
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_cistatic size_t mrt6msg_netlink_msgsize(size_t payloadlen)
24278c2ecf20Sopenharmony_ci{
24288c2ecf20Sopenharmony_ci	size_t len =
24298c2ecf20Sopenharmony_ci		NLMSG_ALIGN(sizeof(struct rtgenmsg))
24308c2ecf20Sopenharmony_ci		+ nla_total_size(1)	/* IP6MRA_CREPORT_MSGTYPE */
24318c2ecf20Sopenharmony_ci		+ nla_total_size(4)	/* IP6MRA_CREPORT_MIF_ID */
24328c2ecf20Sopenharmony_ci					/* IP6MRA_CREPORT_SRC_ADDR */
24338c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(struct in6_addr))
24348c2ecf20Sopenharmony_ci					/* IP6MRA_CREPORT_DST_ADDR */
24358c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(struct in6_addr))
24368c2ecf20Sopenharmony_ci					/* IP6MRA_CREPORT_PKT */
24378c2ecf20Sopenharmony_ci		+ nla_total_size(payloadlen)
24388c2ecf20Sopenharmony_ci		;
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	return len;
24418c2ecf20Sopenharmony_ci}
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_cistatic void mrt6msg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt)
24448c2ecf20Sopenharmony_ci{
24458c2ecf20Sopenharmony_ci	struct net *net = read_pnet(&mrt->net);
24468c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
24478c2ecf20Sopenharmony_ci	struct rtgenmsg *rtgenm;
24488c2ecf20Sopenharmony_ci	struct mrt6msg *msg;
24498c2ecf20Sopenharmony_ci	struct sk_buff *skb;
24508c2ecf20Sopenharmony_ci	struct nlattr *nla;
24518c2ecf20Sopenharmony_ci	int payloadlen;
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci	payloadlen = pkt->len - sizeof(struct mrt6msg);
24548c2ecf20Sopenharmony_ci	msg = (struct mrt6msg *)skb_transport_header(pkt);
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_ci	skb = nlmsg_new(mrt6msg_netlink_msgsize(payloadlen), GFP_ATOMIC);
24578c2ecf20Sopenharmony_ci	if (!skb)
24588c2ecf20Sopenharmony_ci		goto errout;
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT,
24618c2ecf20Sopenharmony_ci			sizeof(struct rtgenmsg), 0);
24628c2ecf20Sopenharmony_ci	if (!nlh)
24638c2ecf20Sopenharmony_ci		goto errout;
24648c2ecf20Sopenharmony_ci	rtgenm = nlmsg_data(nlh);
24658c2ecf20Sopenharmony_ci	rtgenm->rtgen_family = RTNL_FAMILY_IP6MR;
24668c2ecf20Sopenharmony_ci	if (nla_put_u8(skb, IP6MRA_CREPORT_MSGTYPE, msg->im6_msgtype) ||
24678c2ecf20Sopenharmony_ci	    nla_put_u32(skb, IP6MRA_CREPORT_MIF_ID, msg->im6_mif) ||
24688c2ecf20Sopenharmony_ci	    nla_put_in6_addr(skb, IP6MRA_CREPORT_SRC_ADDR,
24698c2ecf20Sopenharmony_ci			     &msg->im6_src) ||
24708c2ecf20Sopenharmony_ci	    nla_put_in6_addr(skb, IP6MRA_CREPORT_DST_ADDR,
24718c2ecf20Sopenharmony_ci			     &msg->im6_dst))
24728c2ecf20Sopenharmony_ci		goto nla_put_failure;
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci	nla = nla_reserve(skb, IP6MRA_CREPORT_PKT, payloadlen);
24758c2ecf20Sopenharmony_ci	if (!nla || skb_copy_bits(pkt, sizeof(struct mrt6msg),
24768c2ecf20Sopenharmony_ci				  nla_data(nla), payloadlen))
24778c2ecf20Sopenharmony_ci		goto nla_put_failure;
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE_R, NULL, GFP_ATOMIC);
24828c2ecf20Sopenharmony_ci	return;
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_cinla_put_failure:
24858c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
24868c2ecf20Sopenharmony_cierrout:
24878c2ecf20Sopenharmony_ci	kfree_skb(skb);
24888c2ecf20Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE_R, -ENOBUFS);
24898c2ecf20Sopenharmony_ci}
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_cistatic int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
24928c2ecf20Sopenharmony_ci{
24938c2ecf20Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
24948c2ecf20Sopenharmony_ci	struct fib_dump_filter filter = {};
24958c2ecf20Sopenharmony_ci	int err;
24968c2ecf20Sopenharmony_ci
24978c2ecf20Sopenharmony_ci	if (cb->strict_check) {
24988c2ecf20Sopenharmony_ci		err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh,
24998c2ecf20Sopenharmony_ci					    &filter, cb);
25008c2ecf20Sopenharmony_ci		if (err < 0)
25018c2ecf20Sopenharmony_ci			return err;
25028c2ecf20Sopenharmony_ci	}
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	if (filter.table_id) {
25058c2ecf20Sopenharmony_ci		struct mr_table *mrt;
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci		mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id);
25088c2ecf20Sopenharmony_ci		if (!mrt) {
25098c2ecf20Sopenharmony_ci			if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR)
25108c2ecf20Sopenharmony_ci				return skb->len;
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist");
25138c2ecf20Sopenharmony_ci			return -ENOENT;
25148c2ecf20Sopenharmony_ci		}
25158c2ecf20Sopenharmony_ci		err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute,
25168c2ecf20Sopenharmony_ci				    &mfc_unres_lock, &filter);
25178c2ecf20Sopenharmony_ci		return skb->len ? : err;
25188c2ecf20Sopenharmony_ci	}
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
25218c2ecf20Sopenharmony_ci				_ip6mr_fill_mroute, &mfc_unres_lock, &filter);
25228c2ecf20Sopenharmony_ci}
2523