xref: /kernel/linux/linux-5.10/net/ipv6/route.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Linux INET6 implementation
48c2ecf20Sopenharmony_ci *	FIB front-end.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	Authors:
78c2ecf20Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*	Changes:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI
138c2ecf20Sopenharmony_ci *		reworked default router selection.
148c2ecf20Sopenharmony_ci *		- respect outgoing interface
158c2ecf20Sopenharmony_ci *		- select from (probably) reachable routers (i.e.
168c2ecf20Sopenharmony_ci *		routers in REACHABLE, STALE, DELAY or PROBE states).
178c2ecf20Sopenharmony_ci *		- always select the same router if it is (probably)
188c2ecf20Sopenharmony_ci *		reachable.  otherwise, round-robin the list.
198c2ecf20Sopenharmony_ci *	Ville Nuorvala
208c2ecf20Sopenharmony_ci *		Fixed routing subtrees.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/capability.h>
268c2ecf20Sopenharmony_ci#include <linux/errno.h>
278c2ecf20Sopenharmony_ci#include <linux/export.h>
288c2ecf20Sopenharmony_ci#include <linux/types.h>
298c2ecf20Sopenharmony_ci#include <linux/times.h>
308c2ecf20Sopenharmony_ci#include <linux/socket.h>
318c2ecf20Sopenharmony_ci#include <linux/sockios.h>
328c2ecf20Sopenharmony_ci#include <linux/net.h>
338c2ecf20Sopenharmony_ci#include <linux/route.h>
348c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
358c2ecf20Sopenharmony_ci#include <linux/in6.h>
368c2ecf20Sopenharmony_ci#include <linux/mroute6.h>
378c2ecf20Sopenharmony_ci#include <linux/init.h>
388c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
398c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
408c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
418c2ecf20Sopenharmony_ci#include <linux/nsproxy.h>
428c2ecf20Sopenharmony_ci#include <linux/slab.h>
438c2ecf20Sopenharmony_ci#include <linux/jhash.h>
448c2ecf20Sopenharmony_ci#include <linux/siphash.h>
458c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
468c2ecf20Sopenharmony_ci#include <net/snmp.h>
478c2ecf20Sopenharmony_ci#include <net/ipv6.h>
488c2ecf20Sopenharmony_ci#include <net/ip6_fib.h>
498c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
508c2ecf20Sopenharmony_ci#include <net/ndisc.h>
518c2ecf20Sopenharmony_ci#include <net/addrconf.h>
528c2ecf20Sopenharmony_ci#include <net/tcp.h>
538c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
548c2ecf20Sopenharmony_ci#include <net/dst.h>
558c2ecf20Sopenharmony_ci#include <net/dst_metadata.h>
568c2ecf20Sopenharmony_ci#include <net/xfrm.h>
578c2ecf20Sopenharmony_ci#include <net/netevent.h>
588c2ecf20Sopenharmony_ci#include <net/netlink.h>
598c2ecf20Sopenharmony_ci#include <net/rtnh.h>
608c2ecf20Sopenharmony_ci#include <net/lwtunnel.h>
618c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h>
628c2ecf20Sopenharmony_ci#include <net/l3mdev.h>
638c2ecf20Sopenharmony_ci#include <net/ip.h>
648c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
658c2ecf20Sopenharmony_ci#include <linux/btf_ids.h>
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
688c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
698c2ecf20Sopenharmony_ci#endif
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
748c2ecf20Sopenharmony_ci#include <trace/events/fib6.h>
758c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
768c2ecf20Sopenharmony_ci#undef CREATE_TRACE_POINTS
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cienum rt6_nud_state {
798c2ecf20Sopenharmony_ci	RT6_NUD_FAIL_HARD = -3,
808c2ecf20Sopenharmony_ci	RT6_NUD_FAIL_PROBE = -2,
818c2ecf20Sopenharmony_ci	RT6_NUD_FAIL_DO_RR = -1,
828c2ecf20Sopenharmony_ci	RT6_NUD_SUCCEED = 1
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
868c2ecf20Sopenharmony_cistatic unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
878c2ecf20Sopenharmony_cistatic unsigned int	 ip6_mtu(const struct dst_entry *dst);
888c2ecf20Sopenharmony_cistatic void		ip6_negative_advice(struct sock *sk,
898c2ecf20Sopenharmony_ci					    struct dst_entry *dst);
908c2ecf20Sopenharmony_cistatic void		ip6_dst_destroy(struct dst_entry *);
918c2ecf20Sopenharmony_cistatic void		ip6_dst_ifdown(struct dst_entry *,
928c2ecf20Sopenharmony_ci				       struct net_device *dev, int how);
938c2ecf20Sopenharmony_cistatic void		 ip6_dst_gc(struct dst_ops *ops);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int		ip6_pkt_discard(struct sk_buff *skb);
968c2ecf20Sopenharmony_cistatic int		ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
978c2ecf20Sopenharmony_cistatic int		ip6_pkt_prohibit(struct sk_buff *skb);
988c2ecf20Sopenharmony_cistatic int		ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
998c2ecf20Sopenharmony_cistatic void		ip6_link_failure(struct sk_buff *skb);
1008c2ecf20Sopenharmony_cistatic void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
1018c2ecf20Sopenharmony_ci					   struct sk_buff *skb, u32 mtu,
1028c2ecf20Sopenharmony_ci					   bool confirm_neigh);
1038c2ecf20Sopenharmony_cistatic void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
1048c2ecf20Sopenharmony_ci					struct sk_buff *skb);
1058c2ecf20Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
1068c2ecf20Sopenharmony_ci			   int strict);
1078c2ecf20Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i);
1088c2ecf20Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb,
1098c2ecf20Sopenharmony_ci			 struct fib6_info *rt, struct dst_entry *dst,
1108c2ecf20Sopenharmony_ci			 struct in6_addr *dest, struct in6_addr *src,
1118c2ecf20Sopenharmony_ci			 int iif, int type, u32 portid, u32 seq,
1128c2ecf20Sopenharmony_ci			 unsigned int flags);
1138c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
1148c2ecf20Sopenharmony_ci					   const struct in6_addr *daddr,
1158c2ecf20Sopenharmony_ci					   const struct in6_addr *saddr);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
1188c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net,
1198c2ecf20Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
1208c2ecf20Sopenharmony_ci					   const struct in6_addr *gwaddr,
1218c2ecf20Sopenharmony_ci					   struct net_device *dev,
1228c2ecf20Sopenharmony_ci					   unsigned int pref);
1238c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net,
1248c2ecf20Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
1258c2ecf20Sopenharmony_ci					   const struct in6_addr *gwaddr,
1268c2ecf20Sopenharmony_ci					   struct net_device *dev);
1278c2ecf20Sopenharmony_ci#endif
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistruct uncached_list {
1308c2ecf20Sopenharmony_ci	spinlock_t		lock;
1318c2ecf20Sopenharmony_ci	struct list_head	head;
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_civoid rt6_uncached_list_add(struct rt6_info *rt)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	rt->rt6i_uncached_list = ul;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	spin_lock_bh(&ul->lock);
1438c2ecf20Sopenharmony_ci	list_add_tail(&rt->rt6i_uncached, &ul->head);
1448c2ecf20Sopenharmony_ci	spin_unlock_bh(&ul->lock);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_civoid rt6_uncached_list_del(struct rt6_info *rt)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	if (!list_empty(&rt->rt6i_uncached)) {
1508c2ecf20Sopenharmony_ci		struct uncached_list *ul = rt->rt6i_uncached_list;
1518c2ecf20Sopenharmony_ci		struct net *net = dev_net(rt->dst.dev);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		spin_lock_bh(&ul->lock);
1548c2ecf20Sopenharmony_ci		list_del(&rt->rt6i_uncached);
1558c2ecf20Sopenharmony_ci		atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache);
1568c2ecf20Sopenharmony_ci		spin_unlock_bh(&ul->lock);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct net_device *loopback_dev = net->loopback_dev;
1638c2ecf20Sopenharmony_ci	int cpu;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (dev == loopback_dev)
1668c2ecf20Sopenharmony_ci		return;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
1698c2ecf20Sopenharmony_ci		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
1708c2ecf20Sopenharmony_ci		struct rt6_info *rt;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		spin_lock_bh(&ul->lock);
1738c2ecf20Sopenharmony_ci		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
1748c2ecf20Sopenharmony_ci			struct inet6_dev *rt_idev = rt->rt6i_idev;
1758c2ecf20Sopenharmony_ci			struct net_device *rt_dev = rt->dst.dev;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci			if (rt_idev->dev == dev) {
1788c2ecf20Sopenharmony_ci				rt->rt6i_idev = in6_dev_get(loopback_dev);
1798c2ecf20Sopenharmony_ci				in6_dev_put(rt_idev);
1808c2ecf20Sopenharmony_ci			}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci			if (rt_dev == dev) {
1838c2ecf20Sopenharmony_ci				rt->dst.dev = blackhole_netdev;
1848c2ecf20Sopenharmony_ci				dev_hold(rt->dst.dev);
1858c2ecf20Sopenharmony_ci				dev_put(rt_dev);
1868c2ecf20Sopenharmony_ci			}
1878c2ecf20Sopenharmony_ci		}
1888c2ecf20Sopenharmony_ci		spin_unlock_bh(&ul->lock);
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic inline const void *choose_neigh_daddr(const struct in6_addr *p,
1938c2ecf20Sopenharmony_ci					     struct sk_buff *skb,
1948c2ecf20Sopenharmony_ci					     const void *daddr)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(p))
1978c2ecf20Sopenharmony_ci		return (const void *) p;
1988c2ecf20Sopenharmony_ci	else if (skb)
1998c2ecf20Sopenharmony_ci		return &ipv6_hdr(skb)->daddr;
2008c2ecf20Sopenharmony_ci	return daddr;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistruct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
2048c2ecf20Sopenharmony_ci				   struct net_device *dev,
2058c2ecf20Sopenharmony_ci				   struct sk_buff *skb,
2068c2ecf20Sopenharmony_ci				   const void *daddr)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct neighbour *n;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	daddr = choose_neigh_daddr(gw, skb, daddr);
2118c2ecf20Sopenharmony_ci	n = __ipv6_neigh_lookup(dev, daddr);
2128c2ecf20Sopenharmony_ci	if (n)
2138c2ecf20Sopenharmony_ci		return n;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	n = neigh_create(&nd_tbl, daddr, dev);
2168c2ecf20Sopenharmony_ci	return IS_ERR(n) ? NULL : n;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
2208c2ecf20Sopenharmony_ci					      struct sk_buff *skb,
2218c2ecf20Sopenharmony_ci					      const void *daddr)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any),
2268c2ecf20Sopenharmony_ci				dst->dev, skb, daddr);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct net_device *dev = dst->dev;
2328c2ecf20Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)dst;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr);
2358c2ecf20Sopenharmony_ci	if (!daddr)
2368c2ecf20Sopenharmony_ci		return;
2378c2ecf20Sopenharmony_ci	if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
2388c2ecf20Sopenharmony_ci		return;
2398c2ecf20Sopenharmony_ci	if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
2408c2ecf20Sopenharmony_ci		return;
2418c2ecf20Sopenharmony_ci	__ipv6_confirm_neigh(dev, daddr);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic struct dst_ops ip6_dst_ops_template = {
2458c2ecf20Sopenharmony_ci	.family			=	AF_INET6,
2468c2ecf20Sopenharmony_ci	.gc			=	ip6_dst_gc,
2478c2ecf20Sopenharmony_ci	.gc_thresh		=	1024,
2488c2ecf20Sopenharmony_ci	.check			=	ip6_dst_check,
2498c2ecf20Sopenharmony_ci	.default_advmss		=	ip6_default_advmss,
2508c2ecf20Sopenharmony_ci	.mtu			=	ip6_mtu,
2518c2ecf20Sopenharmony_ci	.cow_metrics		=	dst_cow_metrics_generic,
2528c2ecf20Sopenharmony_ci	.destroy		=	ip6_dst_destroy,
2538c2ecf20Sopenharmony_ci	.ifdown			=	ip6_dst_ifdown,
2548c2ecf20Sopenharmony_ci	.negative_advice	=	ip6_negative_advice,
2558c2ecf20Sopenharmony_ci	.link_failure		=	ip6_link_failure,
2568c2ecf20Sopenharmony_ci	.update_pmtu		=	ip6_rt_update_pmtu,
2578c2ecf20Sopenharmony_ci	.redirect		=	rt6_do_redirect,
2588c2ecf20Sopenharmony_ci	.local_out		=	__ip6_local_out,
2598c2ecf20Sopenharmony_ci	.neigh_lookup		=	ip6_dst_neigh_lookup,
2608c2ecf20Sopenharmony_ci	.confirm_neigh		=	ip6_confirm_neigh,
2618c2ecf20Sopenharmony_ci};
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic struct dst_ops ip6_dst_blackhole_ops = {
2648c2ecf20Sopenharmony_ci	.family			= AF_INET6,
2658c2ecf20Sopenharmony_ci	.default_advmss		= ip6_default_advmss,
2668c2ecf20Sopenharmony_ci	.neigh_lookup		= ip6_dst_neigh_lookup,
2678c2ecf20Sopenharmony_ci	.check			= ip6_dst_check,
2688c2ecf20Sopenharmony_ci	.destroy		= ip6_dst_destroy,
2698c2ecf20Sopenharmony_ci	.cow_metrics		= dst_cow_metrics_generic,
2708c2ecf20Sopenharmony_ci	.update_pmtu		= dst_blackhole_update_pmtu,
2718c2ecf20Sopenharmony_ci	.redirect		= dst_blackhole_redirect,
2728c2ecf20Sopenharmony_ci	.mtu			= dst_blackhole_mtu,
2738c2ecf20Sopenharmony_ci};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic const u32 ip6_template_metrics[RTAX_MAX] = {
2768c2ecf20Sopenharmony_ci	[RTAX_HOPLIMIT - 1] = 0,
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic const struct fib6_info fib6_null_entry_template = {
2808c2ecf20Sopenharmony_ci	.fib6_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2818c2ecf20Sopenharmony_ci	.fib6_protocol  = RTPROT_KERNEL,
2828c2ecf20Sopenharmony_ci	.fib6_metric	= ~(u32)0,
2838c2ecf20Sopenharmony_ci	.fib6_ref	= REFCOUNT_INIT(1),
2848c2ecf20Sopenharmony_ci	.fib6_type	= RTN_UNREACHABLE,
2858c2ecf20Sopenharmony_ci	.fib6_metrics	= (struct dst_metrics *)&dst_default_metrics,
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic const struct rt6_info ip6_null_entry_template = {
2898c2ecf20Sopenharmony_ci	.dst = {
2908c2ecf20Sopenharmony_ci		.__refcnt	= ATOMIC_INIT(1),
2918c2ecf20Sopenharmony_ci		.__use		= 1,
2928c2ecf20Sopenharmony_ci		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2938c2ecf20Sopenharmony_ci		.error		= -ENETUNREACH,
2948c2ecf20Sopenharmony_ci		.input		= ip6_pkt_discard,
2958c2ecf20Sopenharmony_ci		.output		= ip6_pkt_discard_out,
2968c2ecf20Sopenharmony_ci	},
2978c2ecf20Sopenharmony_ci	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2988c2ecf20Sopenharmony_ci};
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic const struct rt6_info ip6_prohibit_entry_template = {
3038c2ecf20Sopenharmony_ci	.dst = {
3048c2ecf20Sopenharmony_ci		.__refcnt	= ATOMIC_INIT(1),
3058c2ecf20Sopenharmony_ci		.__use		= 1,
3068c2ecf20Sopenharmony_ci		.obsolete	= DST_OBSOLETE_FORCE_CHK,
3078c2ecf20Sopenharmony_ci		.error		= -EACCES,
3088c2ecf20Sopenharmony_ci		.input		= ip6_pkt_prohibit,
3098c2ecf20Sopenharmony_ci		.output		= ip6_pkt_prohibit_out,
3108c2ecf20Sopenharmony_ci	},
3118c2ecf20Sopenharmony_ci	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic const struct rt6_info ip6_blk_hole_entry_template = {
3158c2ecf20Sopenharmony_ci	.dst = {
3168c2ecf20Sopenharmony_ci		.__refcnt	= ATOMIC_INIT(1),
3178c2ecf20Sopenharmony_ci		.__use		= 1,
3188c2ecf20Sopenharmony_ci		.obsolete	= DST_OBSOLETE_FORCE_CHK,
3198c2ecf20Sopenharmony_ci		.error		= -EINVAL,
3208c2ecf20Sopenharmony_ci		.input		= dst_discard,
3218c2ecf20Sopenharmony_ci		.output		= dst_discard_out,
3228c2ecf20Sopenharmony_ci	},
3238c2ecf20Sopenharmony_ci	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3248c2ecf20Sopenharmony_ci};
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci#endif
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic void rt6_info_init(struct rt6_info *rt)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct dst_entry *dst = &rt->dst;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
3338c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rt->rt6i_uncached);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/* allocate dst with ip6_dst_ops */
3378c2ecf20Sopenharmony_cistruct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
3388c2ecf20Sopenharmony_ci			       int flags)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3418c2ecf20Sopenharmony_ci					1, DST_OBSOLETE_FORCE_CHK, flags);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (rt) {
3448c2ecf20Sopenharmony_ci		rt6_info_init(rt);
3458c2ecf20Sopenharmony_ci		atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return rt;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip6_dst_alloc);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void ip6_dst_destroy(struct dst_entry *dst)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)dst;
3558c2ecf20Sopenharmony_ci	struct fib6_info *from;
3568c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ip_dst_metrics_put(dst);
3598c2ecf20Sopenharmony_ci	rt6_uncached_list_del(rt);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	idev = rt->rt6i_idev;
3628c2ecf20Sopenharmony_ci	if (idev) {
3638c2ecf20Sopenharmony_ci		rt->rt6i_idev = NULL;
3648c2ecf20Sopenharmony_ci		in6_dev_put(idev);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	from = xchg((__force struct fib6_info **)&rt->from, NULL);
3688c2ecf20Sopenharmony_ci	fib6_info_release(from);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3728c2ecf20Sopenharmony_ci			   int how)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)dst;
3758c2ecf20Sopenharmony_ci	struct inet6_dev *idev = rt->rt6i_idev;
3768c2ecf20Sopenharmony_ci	struct net_device *loopback_dev =
3778c2ecf20Sopenharmony_ci		dev_net(dev)->loopback_dev;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (idev && idev->dev != loopback_dev) {
3808c2ecf20Sopenharmony_ci		struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev);
3818c2ecf20Sopenharmony_ci		if (loopback_idev) {
3828c2ecf20Sopenharmony_ci			rt->rt6i_idev = loopback_idev;
3838c2ecf20Sopenharmony_ci			in6_dev_put(idev);
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic bool __rt6_check_expired(const struct rt6_info *rt)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	if (rt->rt6i_flags & RTF_EXPIRES)
3918c2ecf20Sopenharmony_ci		return time_after(jiffies, rt->dst.expires);
3928c2ecf20Sopenharmony_ci	else
3938c2ecf20Sopenharmony_ci		return false;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic bool rt6_check_expired(const struct rt6_info *rt)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct fib6_info *from;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	from = rcu_dereference(rt->from);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (rt->rt6i_flags & RTF_EXPIRES) {
4038c2ecf20Sopenharmony_ci		if (time_after(jiffies, rt->dst.expires))
4048c2ecf20Sopenharmony_ci			return true;
4058c2ecf20Sopenharmony_ci	} else if (from) {
4068c2ecf20Sopenharmony_ci		return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
4078c2ecf20Sopenharmony_ci			fib6_check_expired(from);
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	return false;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_civoid fib6_select_path(const struct net *net, struct fib6_result *res,
4138c2ecf20Sopenharmony_ci		      struct flowi6 *fl6, int oif, bool have_oif_match,
4148c2ecf20Sopenharmony_ci		      const struct sk_buff *skb, int strict)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct fib6_info *sibling, *next_sibling;
4178c2ecf20Sopenharmony_ci	struct fib6_info *match = res->f6i;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (!match->nh && (!match->fib6_nsiblings || have_oif_match))
4208c2ecf20Sopenharmony_ci		goto out;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (match->nh && have_oif_match && res->nh)
4238c2ecf20Sopenharmony_ci		return;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* We might have already computed the hash for ICMPv6 errors. In such
4268c2ecf20Sopenharmony_ci	 * case it will always be non-zero. Otherwise now is the time to do it.
4278c2ecf20Sopenharmony_ci	 */
4288c2ecf20Sopenharmony_ci	if (!fl6->mp_hash &&
4298c2ecf20Sopenharmony_ci	    (!match->nh || nexthop_is_multipath(match->nh)))
4308c2ecf20Sopenharmony_ci		fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (unlikely(match->nh)) {
4338c2ecf20Sopenharmony_ci		nexthop_path_fib6_result(res, fl6->mp_hash);
4348c2ecf20Sopenharmony_ci		return;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound))
4388c2ecf20Sopenharmony_ci		goto out;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
4418c2ecf20Sopenharmony_ci				 fib6_siblings) {
4428c2ecf20Sopenharmony_ci		const struct fib6_nh *nh = sibling->fib6_nh;
4438c2ecf20Sopenharmony_ci		int nh_upper_bound;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
4468c2ecf20Sopenharmony_ci		if (fl6->mp_hash > nh_upper_bound)
4478c2ecf20Sopenharmony_ci			continue;
4488c2ecf20Sopenharmony_ci		if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
4498c2ecf20Sopenharmony_ci			break;
4508c2ecf20Sopenharmony_ci		match = sibling;
4518c2ecf20Sopenharmony_ci		break;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ciout:
4558c2ecf20Sopenharmony_ci	res->f6i = match;
4568c2ecf20Sopenharmony_ci	res->nh = match->fib6_nh;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/*
4608c2ecf20Sopenharmony_ci *	Route lookup. rcu_read_lock() should be held.
4618c2ecf20Sopenharmony_ci */
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
4648c2ecf20Sopenharmony_ci			       const struct in6_addr *saddr, int oif, int flags)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	const struct net_device *dev;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD)
4698c2ecf20Sopenharmony_ci		return false;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	dev = nh->fib_nh_dev;
4728c2ecf20Sopenharmony_ci	if (oif) {
4738c2ecf20Sopenharmony_ci		if (dev->ifindex == oif)
4748c2ecf20Sopenharmony_ci			return true;
4758c2ecf20Sopenharmony_ci	} else {
4768c2ecf20Sopenharmony_ci		if (ipv6_chk_addr(net, saddr, dev,
4778c2ecf20Sopenharmony_ci				  flags & RT6_LOOKUP_F_IFACE))
4788c2ecf20Sopenharmony_ci			return true;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return false;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistruct fib6_nh_dm_arg {
4858c2ecf20Sopenharmony_ci	struct net		*net;
4868c2ecf20Sopenharmony_ci	const struct in6_addr	*saddr;
4878c2ecf20Sopenharmony_ci	int			oif;
4888c2ecf20Sopenharmony_ci	int			flags;
4898c2ecf20Sopenharmony_ci	struct fib6_nh		*nh;
4908c2ecf20Sopenharmony_ci};
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic int __rt6_nh_dev_match(struct fib6_nh *nh, void *_arg)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct fib6_nh_dm_arg *arg = _arg;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	arg->nh = nh;
4978c2ecf20Sopenharmony_ci	return __rt6_device_match(arg->net, nh, arg->saddr, arg->oif,
4988c2ecf20Sopenharmony_ci				  arg->flags);
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci/* returns fib6_nh from nexthop or NULL */
5028c2ecf20Sopenharmony_cistatic struct fib6_nh *rt6_nh_dev_match(struct net *net, struct nexthop *nh,
5038c2ecf20Sopenharmony_ci					struct fib6_result *res,
5048c2ecf20Sopenharmony_ci					const struct in6_addr *saddr,
5058c2ecf20Sopenharmony_ci					int oif, int flags)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	struct fib6_nh_dm_arg arg = {
5088c2ecf20Sopenharmony_ci		.net   = net,
5098c2ecf20Sopenharmony_ci		.saddr = saddr,
5108c2ecf20Sopenharmony_ci		.oif   = oif,
5118c2ecf20Sopenharmony_ci		.flags = flags,
5128c2ecf20Sopenharmony_ci	};
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (nexthop_is_blackhole(nh))
5158c2ecf20Sopenharmony_ci		return NULL;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (nexthop_for_each_fib6_nh(nh, __rt6_nh_dev_match, &arg))
5188c2ecf20Sopenharmony_ci		return arg.nh;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return NULL;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic void rt6_device_match(struct net *net, struct fib6_result *res,
5248c2ecf20Sopenharmony_ci			     const struct in6_addr *saddr, int oif, int flags)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
5278c2ecf20Sopenharmony_ci	struct fib6_info *spf6i;
5288c2ecf20Sopenharmony_ci	struct fib6_nh *nh;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (!oif && ipv6_addr_any(saddr)) {
5318c2ecf20Sopenharmony_ci		if (unlikely(f6i->nh)) {
5328c2ecf20Sopenharmony_ci			nh = nexthop_fib6_nh(f6i->nh);
5338c2ecf20Sopenharmony_ci			if (nexthop_is_blackhole(f6i->nh))
5348c2ecf20Sopenharmony_ci				goto out_blackhole;
5358c2ecf20Sopenharmony_ci		} else {
5368c2ecf20Sopenharmony_ci			nh = f6i->fib6_nh;
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci		if (!(nh->fib_nh_flags & RTNH_F_DEAD))
5398c2ecf20Sopenharmony_ci			goto out;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
5438c2ecf20Sopenharmony_ci		bool matched = false;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		if (unlikely(spf6i->nh)) {
5468c2ecf20Sopenharmony_ci			nh = rt6_nh_dev_match(net, spf6i->nh, res, saddr,
5478c2ecf20Sopenharmony_ci					      oif, flags);
5488c2ecf20Sopenharmony_ci			if (nh)
5498c2ecf20Sopenharmony_ci				matched = true;
5508c2ecf20Sopenharmony_ci		} else {
5518c2ecf20Sopenharmony_ci			nh = spf6i->fib6_nh;
5528c2ecf20Sopenharmony_ci			if (__rt6_device_match(net, nh, saddr, oif, flags))
5538c2ecf20Sopenharmony_ci				matched = true;
5548c2ecf20Sopenharmony_ci		}
5558c2ecf20Sopenharmony_ci		if (matched) {
5568c2ecf20Sopenharmony_ci			res->f6i = spf6i;
5578c2ecf20Sopenharmony_ci			goto out;
5588c2ecf20Sopenharmony_ci		}
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (oif && flags & RT6_LOOKUP_F_IFACE) {
5628c2ecf20Sopenharmony_ci		res->f6i = net->ipv6.fib6_null_entry;
5638c2ecf20Sopenharmony_ci		nh = res->f6i->fib6_nh;
5648c2ecf20Sopenharmony_ci		goto out;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (unlikely(f6i->nh)) {
5688c2ecf20Sopenharmony_ci		nh = nexthop_fib6_nh(f6i->nh);
5698c2ecf20Sopenharmony_ci		if (nexthop_is_blackhole(f6i->nh))
5708c2ecf20Sopenharmony_ci			goto out_blackhole;
5718c2ecf20Sopenharmony_ci	} else {
5728c2ecf20Sopenharmony_ci		nh = f6i->fib6_nh;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD) {
5768c2ecf20Sopenharmony_ci		res->f6i = net->ipv6.fib6_null_entry;
5778c2ecf20Sopenharmony_ci		nh = res->f6i->fib6_nh;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ciout:
5808c2ecf20Sopenharmony_ci	res->nh = nh;
5818c2ecf20Sopenharmony_ci	res->fib6_type = res->f6i->fib6_type;
5828c2ecf20Sopenharmony_ci	res->fib6_flags = res->f6i->fib6_flags;
5838c2ecf20Sopenharmony_ci	return;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ciout_blackhole:
5868c2ecf20Sopenharmony_ci	res->fib6_flags |= RTF_REJECT;
5878c2ecf20Sopenharmony_ci	res->fib6_type = RTN_BLACKHOLE;
5888c2ecf20Sopenharmony_ci	res->nh = nh;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
5928c2ecf20Sopenharmony_cistruct __rt6_probe_work {
5938c2ecf20Sopenharmony_ci	struct work_struct work;
5948c2ecf20Sopenharmony_ci	struct in6_addr target;
5958c2ecf20Sopenharmony_ci	struct net_device *dev;
5968c2ecf20Sopenharmony_ci};
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic void rt6_probe_deferred(struct work_struct *w)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct in6_addr mcaddr;
6018c2ecf20Sopenharmony_ci	struct __rt6_probe_work *work =
6028c2ecf20Sopenharmony_ci		container_of(w, struct __rt6_probe_work, work);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	addrconf_addr_solict_mult(&work->target, &mcaddr);
6058c2ecf20Sopenharmony_ci	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
6068c2ecf20Sopenharmony_ci	dev_put(work->dev);
6078c2ecf20Sopenharmony_ci	kfree(work);
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic void rt6_probe(struct fib6_nh *fib6_nh)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct __rt6_probe_work *work = NULL;
6138c2ecf20Sopenharmony_ci	const struct in6_addr *nh_gw;
6148c2ecf20Sopenharmony_ci	unsigned long last_probe;
6158c2ecf20Sopenharmony_ci	struct neighbour *neigh;
6168c2ecf20Sopenharmony_ci	struct net_device *dev;
6178c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/*
6208c2ecf20Sopenharmony_ci	 * Okay, this does not seem to be appropriate
6218c2ecf20Sopenharmony_ci	 * for now, however, we need to check if it
6228c2ecf20Sopenharmony_ci	 * is really so; aka Router Reachability Probing.
6238c2ecf20Sopenharmony_ci	 *
6248c2ecf20Sopenharmony_ci	 * Router Reachability Probe MUST be rate-limited
6258c2ecf20Sopenharmony_ci	 * to no more than one per minute.
6268c2ecf20Sopenharmony_ci	 */
6278c2ecf20Sopenharmony_ci	if (!fib6_nh->fib_nh_gw_family)
6288c2ecf20Sopenharmony_ci		return;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	nh_gw = &fib6_nh->fib_nh_gw6;
6318c2ecf20Sopenharmony_ci	dev = fib6_nh->fib_nh_dev;
6328c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
6338c2ecf20Sopenharmony_ci	last_probe = READ_ONCE(fib6_nh->last_probe);
6348c2ecf20Sopenharmony_ci	idev = __in6_dev_get(dev);
6358c2ecf20Sopenharmony_ci	if (!idev)
6368c2ecf20Sopenharmony_ci		goto out;
6378c2ecf20Sopenharmony_ci	neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
6388c2ecf20Sopenharmony_ci	if (neigh) {
6398c2ecf20Sopenharmony_ci		if (neigh->nud_state & NUD_VALID)
6408c2ecf20Sopenharmony_ci			goto out;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		write_lock(&neigh->lock);
6438c2ecf20Sopenharmony_ci		if (!(neigh->nud_state & NUD_VALID) &&
6448c2ecf20Sopenharmony_ci		    time_after(jiffies,
6458c2ecf20Sopenharmony_ci			       neigh->updated + idev->cnf.rtr_probe_interval)) {
6468c2ecf20Sopenharmony_ci			work = kmalloc(sizeof(*work), GFP_ATOMIC);
6478c2ecf20Sopenharmony_ci			if (work)
6488c2ecf20Sopenharmony_ci				__neigh_set_probe_once(neigh);
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci		write_unlock(&neigh->lock);
6518c2ecf20Sopenharmony_ci	} else if (time_after(jiffies, last_probe +
6528c2ecf20Sopenharmony_ci				       idev->cnf.rtr_probe_interval)) {
6538c2ecf20Sopenharmony_ci		work = kmalloc(sizeof(*work), GFP_ATOMIC);
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (!work || cmpxchg(&fib6_nh->last_probe,
6578c2ecf20Sopenharmony_ci			     last_probe, jiffies) != last_probe) {
6588c2ecf20Sopenharmony_ci		kfree(work);
6598c2ecf20Sopenharmony_ci	} else {
6608c2ecf20Sopenharmony_ci		INIT_WORK(&work->work, rt6_probe_deferred);
6618c2ecf20Sopenharmony_ci		work->target = *nh_gw;
6628c2ecf20Sopenharmony_ci		dev_hold(dev);
6638c2ecf20Sopenharmony_ci		work->dev = dev;
6648c2ecf20Sopenharmony_ci		schedule_work(&work->work);
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ciout:
6688c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci#else
6718c2ecf20Sopenharmony_cistatic inline void rt6_probe(struct fib6_nh *fib6_nh)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci#endif
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci/*
6778c2ecf20Sopenharmony_ci * Default Router Selection (RFC 2461 6.3.6)
6788c2ecf20Sopenharmony_ci */
6798c2ecf20Sopenharmony_cistatic enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
6828c2ecf20Sopenharmony_ci	struct neighbour *neigh;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
6858c2ecf20Sopenharmony_ci	neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
6868c2ecf20Sopenharmony_ci					  &fib6_nh->fib_nh_gw6);
6878c2ecf20Sopenharmony_ci	if (neigh) {
6888c2ecf20Sopenharmony_ci		read_lock(&neigh->lock);
6898c2ecf20Sopenharmony_ci		if (neigh->nud_state & NUD_VALID)
6908c2ecf20Sopenharmony_ci			ret = RT6_NUD_SUCCEED;
6918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
6928c2ecf20Sopenharmony_ci		else if (!(neigh->nud_state & NUD_FAILED))
6938c2ecf20Sopenharmony_ci			ret = RT6_NUD_SUCCEED;
6948c2ecf20Sopenharmony_ci		else
6958c2ecf20Sopenharmony_ci			ret = RT6_NUD_FAIL_PROBE;
6968c2ecf20Sopenharmony_ci#endif
6978c2ecf20Sopenharmony_ci		read_unlock(&neigh->lock);
6988c2ecf20Sopenharmony_ci	} else {
6998c2ecf20Sopenharmony_ci		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
7008c2ecf20Sopenharmony_ci		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	return ret;
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
7088c2ecf20Sopenharmony_ci			   int strict)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	int m = 0;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (!oif || nh->fib_nh_dev->ifindex == oif)
7138c2ecf20Sopenharmony_ci		m = 2;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (!m && (strict & RT6_LOOKUP_F_IFACE))
7168c2ecf20Sopenharmony_ci		return RT6_NUD_FAIL_HARD;
7178c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
7188c2ecf20Sopenharmony_ci	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
7198c2ecf20Sopenharmony_ci#endif
7208c2ecf20Sopenharmony_ci	if ((strict & RT6_LOOKUP_F_REACHABLE) &&
7218c2ecf20Sopenharmony_ci	    !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
7228c2ecf20Sopenharmony_ci		int n = rt6_check_neigh(nh);
7238c2ecf20Sopenharmony_ci		if (n < 0)
7248c2ecf20Sopenharmony_ci			return n;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci	return m;
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_cistatic bool find_match(struct fib6_nh *nh, u32 fib6_flags,
7308c2ecf20Sopenharmony_ci		       int oif, int strict, int *mpri, bool *do_rr)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	bool match_do_rr = false;
7338c2ecf20Sopenharmony_ci	bool rc = false;
7348c2ecf20Sopenharmony_ci	int m;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD)
7378c2ecf20Sopenharmony_ci		goto out;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
7408c2ecf20Sopenharmony_ci	    nh->fib_nh_flags & RTNH_F_LINKDOWN &&
7418c2ecf20Sopenharmony_ci	    !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
7428c2ecf20Sopenharmony_ci		goto out;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	m = rt6_score_route(nh, fib6_flags, oif, strict);
7458c2ecf20Sopenharmony_ci	if (m == RT6_NUD_FAIL_DO_RR) {
7468c2ecf20Sopenharmony_ci		match_do_rr = true;
7478c2ecf20Sopenharmony_ci		m = 0; /* lowest valid score */
7488c2ecf20Sopenharmony_ci	} else if (m == RT6_NUD_FAIL_HARD) {
7498c2ecf20Sopenharmony_ci		goto out;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	if (strict & RT6_LOOKUP_F_REACHABLE)
7538c2ecf20Sopenharmony_ci		rt6_probe(nh);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
7568c2ecf20Sopenharmony_ci	if (m > *mpri) {
7578c2ecf20Sopenharmony_ci		*do_rr = match_do_rr;
7588c2ecf20Sopenharmony_ci		*mpri = m;
7598c2ecf20Sopenharmony_ci		rc = true;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ciout:
7628c2ecf20Sopenharmony_ci	return rc;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistruct fib6_nh_frl_arg {
7668c2ecf20Sopenharmony_ci	u32		flags;
7678c2ecf20Sopenharmony_ci	int		oif;
7688c2ecf20Sopenharmony_ci	int		strict;
7698c2ecf20Sopenharmony_ci	int		*mpri;
7708c2ecf20Sopenharmony_ci	bool		*do_rr;
7718c2ecf20Sopenharmony_ci	struct fib6_nh	*nh;
7728c2ecf20Sopenharmony_ci};
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic int rt6_nh_find_match(struct fib6_nh *nh, void *_arg)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	struct fib6_nh_frl_arg *arg = _arg;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	arg->nh = nh;
7798c2ecf20Sopenharmony_ci	return find_match(nh, arg->flags, arg->oif, arg->strict,
7808c2ecf20Sopenharmony_ci			  arg->mpri, arg->do_rr);
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_cistatic void __find_rr_leaf(struct fib6_info *f6i_start,
7848c2ecf20Sopenharmony_ci			   struct fib6_info *nomatch, u32 metric,
7858c2ecf20Sopenharmony_ci			   struct fib6_result *res, struct fib6_info **cont,
7868c2ecf20Sopenharmony_ci			   int oif, int strict, bool *do_rr, int *mpri)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	struct fib6_info *f6i;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	for (f6i = f6i_start;
7918c2ecf20Sopenharmony_ci	     f6i && f6i != nomatch;
7928c2ecf20Sopenharmony_ci	     f6i = rcu_dereference(f6i->fib6_next)) {
7938c2ecf20Sopenharmony_ci		bool matched = false;
7948c2ecf20Sopenharmony_ci		struct fib6_nh *nh;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci		if (cont && f6i->fib6_metric != metric) {
7978c2ecf20Sopenharmony_ci			*cont = f6i;
7988c2ecf20Sopenharmony_ci			return;
7998c2ecf20Sopenharmony_ci		}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci		if (fib6_check_expired(f6i))
8028c2ecf20Sopenharmony_ci			continue;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci		if (unlikely(f6i->nh)) {
8058c2ecf20Sopenharmony_ci			struct fib6_nh_frl_arg arg = {
8068c2ecf20Sopenharmony_ci				.flags  = f6i->fib6_flags,
8078c2ecf20Sopenharmony_ci				.oif    = oif,
8088c2ecf20Sopenharmony_ci				.strict = strict,
8098c2ecf20Sopenharmony_ci				.mpri   = mpri,
8108c2ecf20Sopenharmony_ci				.do_rr  = do_rr
8118c2ecf20Sopenharmony_ci			};
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci			if (nexthop_is_blackhole(f6i->nh)) {
8148c2ecf20Sopenharmony_ci				res->fib6_flags = RTF_REJECT;
8158c2ecf20Sopenharmony_ci				res->fib6_type = RTN_BLACKHOLE;
8168c2ecf20Sopenharmony_ci				res->f6i = f6i;
8178c2ecf20Sopenharmony_ci				res->nh = nexthop_fib6_nh(f6i->nh);
8188c2ecf20Sopenharmony_ci				return;
8198c2ecf20Sopenharmony_ci			}
8208c2ecf20Sopenharmony_ci			if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match,
8218c2ecf20Sopenharmony_ci						     &arg)) {
8228c2ecf20Sopenharmony_ci				matched = true;
8238c2ecf20Sopenharmony_ci				nh = arg.nh;
8248c2ecf20Sopenharmony_ci			}
8258c2ecf20Sopenharmony_ci		} else {
8268c2ecf20Sopenharmony_ci			nh = f6i->fib6_nh;
8278c2ecf20Sopenharmony_ci			if (find_match(nh, f6i->fib6_flags, oif, strict,
8288c2ecf20Sopenharmony_ci				       mpri, do_rr))
8298c2ecf20Sopenharmony_ci				matched = true;
8308c2ecf20Sopenharmony_ci		}
8318c2ecf20Sopenharmony_ci		if (matched) {
8328c2ecf20Sopenharmony_ci			res->f6i = f6i;
8338c2ecf20Sopenharmony_ci			res->nh = nh;
8348c2ecf20Sopenharmony_ci			res->fib6_flags = f6i->fib6_flags;
8358c2ecf20Sopenharmony_ci			res->fib6_type = f6i->fib6_type;
8368c2ecf20Sopenharmony_ci		}
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
8418c2ecf20Sopenharmony_ci			 struct fib6_info *rr_head, int oif, int strict,
8428c2ecf20Sopenharmony_ci			 bool *do_rr, struct fib6_result *res)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	u32 metric = rr_head->fib6_metric;
8458c2ecf20Sopenharmony_ci	struct fib6_info *cont = NULL;
8468c2ecf20Sopenharmony_ci	int mpri = -1;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	__find_rr_leaf(rr_head, NULL, metric, res, &cont,
8498c2ecf20Sopenharmony_ci		       oif, strict, do_rr, &mpri);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	__find_rr_leaf(leaf, rr_head, metric, res, &cont,
8528c2ecf20Sopenharmony_ci		       oif, strict, do_rr, &mpri);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (res->f6i || !cont)
8558c2ecf20Sopenharmony_ci		return;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	__find_rr_leaf(cont, NULL, metric, res, NULL,
8588c2ecf20Sopenharmony_ci		       oif, strict, do_rr, &mpri);
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic void rt6_select(struct net *net, struct fib6_node *fn, int oif,
8628c2ecf20Sopenharmony_ci		       struct fib6_result *res, int strict)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	struct fib6_info *leaf = rcu_dereference(fn->leaf);
8658c2ecf20Sopenharmony_ci	struct fib6_info *rt0;
8668c2ecf20Sopenharmony_ci	bool do_rr = false;
8678c2ecf20Sopenharmony_ci	int key_plen;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* make sure this function or its helpers sets f6i */
8708c2ecf20Sopenharmony_ci	res->f6i = NULL;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	if (!leaf || leaf == net->ipv6.fib6_null_entry)
8738c2ecf20Sopenharmony_ci		goto out;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	rt0 = rcu_dereference(fn->rr_ptr);
8768c2ecf20Sopenharmony_ci	if (!rt0)
8778c2ecf20Sopenharmony_ci		rt0 = leaf;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* Double check to make sure fn is not an intermediate node
8808c2ecf20Sopenharmony_ci	 * and fn->leaf does not points to its child's leaf
8818c2ecf20Sopenharmony_ci	 * (This might happen if all routes under fn are deleted from
8828c2ecf20Sopenharmony_ci	 * the tree and fib6_repair_tree() is called on the node.)
8838c2ecf20Sopenharmony_ci	 */
8848c2ecf20Sopenharmony_ci	key_plen = rt0->fib6_dst.plen;
8858c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
8868c2ecf20Sopenharmony_ci	if (rt0->fib6_src.plen)
8878c2ecf20Sopenharmony_ci		key_plen = rt0->fib6_src.plen;
8888c2ecf20Sopenharmony_ci#endif
8898c2ecf20Sopenharmony_ci	if (fn->fn_bit != key_plen)
8908c2ecf20Sopenharmony_ci		goto out;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
8938c2ecf20Sopenharmony_ci	if (do_rr) {
8948c2ecf20Sopenharmony_ci		struct fib6_info *next = rcu_dereference(rt0->fib6_next);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		/* no entries matched; do round-robin */
8978c2ecf20Sopenharmony_ci		if (!next || next->fib6_metric != rt0->fib6_metric)
8988c2ecf20Sopenharmony_ci			next = leaf;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci		if (next != rt0) {
9018c2ecf20Sopenharmony_ci			spin_lock_bh(&leaf->fib6_table->tb6_lock);
9028c2ecf20Sopenharmony_ci			/* make sure next is not being deleted from the tree */
9038c2ecf20Sopenharmony_ci			if (next->fib6_node)
9048c2ecf20Sopenharmony_ci				rcu_assign_pointer(fn->rr_ptr, next);
9058c2ecf20Sopenharmony_ci			spin_unlock_bh(&leaf->fib6_table->tb6_lock);
9068c2ecf20Sopenharmony_ci		}
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ciout:
9108c2ecf20Sopenharmony_ci	if (!res->f6i) {
9118c2ecf20Sopenharmony_ci		res->f6i = net->ipv6.fib6_null_entry;
9128c2ecf20Sopenharmony_ci		res->nh = res->f6i->fib6_nh;
9138c2ecf20Sopenharmony_ci		res->fib6_flags = res->f6i->fib6_flags;
9148c2ecf20Sopenharmony_ci		res->fib6_type = res->f6i->fib6_type;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
9218c2ecf20Sopenharmony_ci	       res->nh->fib_nh_gw_family;
9228c2ecf20Sopenharmony_ci}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
9258c2ecf20Sopenharmony_ciint rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
9268c2ecf20Sopenharmony_ci		  const struct in6_addr *gwaddr)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
9298c2ecf20Sopenharmony_ci	struct route_info *rinfo = (struct route_info *) opt;
9308c2ecf20Sopenharmony_ci	struct in6_addr prefix_buf, *prefix;
9318c2ecf20Sopenharmony_ci	unsigned int pref;
9328c2ecf20Sopenharmony_ci	unsigned long lifetime;
9338c2ecf20Sopenharmony_ci	struct fib6_info *rt;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	if (len < sizeof(struct route_info)) {
9368c2ecf20Sopenharmony_ci		return -EINVAL;
9378c2ecf20Sopenharmony_ci	}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* Sanity check for prefix_len and length */
9408c2ecf20Sopenharmony_ci	if (rinfo->length > 3) {
9418c2ecf20Sopenharmony_ci		return -EINVAL;
9428c2ecf20Sopenharmony_ci	} else if (rinfo->prefix_len > 128) {
9438c2ecf20Sopenharmony_ci		return -EINVAL;
9448c2ecf20Sopenharmony_ci	} else if (rinfo->prefix_len > 64) {
9458c2ecf20Sopenharmony_ci		if (rinfo->length < 2) {
9468c2ecf20Sopenharmony_ci			return -EINVAL;
9478c2ecf20Sopenharmony_ci		}
9488c2ecf20Sopenharmony_ci	} else if (rinfo->prefix_len > 0) {
9498c2ecf20Sopenharmony_ci		if (rinfo->length < 1) {
9508c2ecf20Sopenharmony_ci			return -EINVAL;
9518c2ecf20Sopenharmony_ci		}
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	pref = rinfo->route_pref;
9558c2ecf20Sopenharmony_ci	if (pref == ICMPV6_ROUTER_PREF_INVALID)
9568c2ecf20Sopenharmony_ci		return -EINVAL;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (rinfo->length == 3)
9618c2ecf20Sopenharmony_ci		prefix = (struct in6_addr *)rinfo->prefix;
9628c2ecf20Sopenharmony_ci	else {
9638c2ecf20Sopenharmony_ci		/* this function is safe */
9648c2ecf20Sopenharmony_ci		ipv6_addr_prefix(&prefix_buf,
9658c2ecf20Sopenharmony_ci				 (struct in6_addr *)rinfo->prefix,
9668c2ecf20Sopenharmony_ci				 rinfo->prefix_len);
9678c2ecf20Sopenharmony_ci		prefix = &prefix_buf;
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (rinfo->prefix_len == 0)
9718c2ecf20Sopenharmony_ci		rt = rt6_get_dflt_router(net, gwaddr, dev);
9728c2ecf20Sopenharmony_ci	else
9738c2ecf20Sopenharmony_ci		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
9748c2ecf20Sopenharmony_ci					gwaddr, dev);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	if (rt && !lifetime) {
9778c2ecf20Sopenharmony_ci		ip6_del_rt(net, rt, false);
9788c2ecf20Sopenharmony_ci		rt = NULL;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	if (!rt && lifetime)
9828c2ecf20Sopenharmony_ci		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
9838c2ecf20Sopenharmony_ci					dev, pref);
9848c2ecf20Sopenharmony_ci	else if (rt)
9858c2ecf20Sopenharmony_ci		rt->fib6_flags = RTF_ROUTEINFO |
9868c2ecf20Sopenharmony_ci				 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	if (rt) {
9898c2ecf20Sopenharmony_ci		if (!addrconf_finite_timeout(lifetime))
9908c2ecf20Sopenharmony_ci			fib6_clean_expires(rt);
9918c2ecf20Sopenharmony_ci		else
9928c2ecf20Sopenharmony_ci			fib6_set_expires(rt, jiffies + HZ * lifetime);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci		fib6_info_release(rt);
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci	return 0;
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci#endif
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci/*
10018c2ecf20Sopenharmony_ci *	Misc support functions
10028c2ecf20Sopenharmony_ci */
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci/* called with rcu_lock held */
10058c2ecf20Sopenharmony_cistatic struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct net_device *dev = res->nh->fib_nh_dev;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
10108c2ecf20Sopenharmony_ci		/* for copies of local routes, dst->dev needs to be the
10118c2ecf20Sopenharmony_ci		 * device if it is a master device, the master device if
10128c2ecf20Sopenharmony_ci		 * device is enslaved, and the loopback as the default
10138c2ecf20Sopenharmony_ci		 */
10148c2ecf20Sopenharmony_ci		if (netif_is_l3_slave(dev) &&
10158c2ecf20Sopenharmony_ci		    !rt6_need_strict(&res->f6i->fib6_dst.addr))
10168c2ecf20Sopenharmony_ci			dev = l3mdev_master_dev_rcu(dev);
10178c2ecf20Sopenharmony_ci		else if (!netif_is_l3_master(dev))
10188c2ecf20Sopenharmony_ci			dev = dev_net(dev)->loopback_dev;
10198c2ecf20Sopenharmony_ci		/* last case is netif_is_l3_master(dev) is true in which
10208c2ecf20Sopenharmony_ci		 * case we want dev returned to be dev
10218c2ecf20Sopenharmony_ci		 */
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	return dev;
10258c2ecf20Sopenharmony_ci}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_cistatic const int fib6_prop[RTN_MAX + 1] = {
10288c2ecf20Sopenharmony_ci	[RTN_UNSPEC]	= 0,
10298c2ecf20Sopenharmony_ci	[RTN_UNICAST]	= 0,
10308c2ecf20Sopenharmony_ci	[RTN_LOCAL]	= 0,
10318c2ecf20Sopenharmony_ci	[RTN_BROADCAST]	= 0,
10328c2ecf20Sopenharmony_ci	[RTN_ANYCAST]	= 0,
10338c2ecf20Sopenharmony_ci	[RTN_MULTICAST]	= 0,
10348c2ecf20Sopenharmony_ci	[RTN_BLACKHOLE]	= -EINVAL,
10358c2ecf20Sopenharmony_ci	[RTN_UNREACHABLE] = -EHOSTUNREACH,
10368c2ecf20Sopenharmony_ci	[RTN_PROHIBIT]	= -EACCES,
10378c2ecf20Sopenharmony_ci	[RTN_THROW]	= -EAGAIN,
10388c2ecf20Sopenharmony_ci	[RTN_NAT]	= -EINVAL,
10398c2ecf20Sopenharmony_ci	[RTN_XRESOLVE]	= -EINVAL,
10408c2ecf20Sopenharmony_ci};
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	return fib6_prop[fib6_type];
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic unsigned short fib6_info_dst_flags(struct fib6_info *rt)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	unsigned short flags = 0;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	if (rt->dst_nocount)
10528c2ecf20Sopenharmony_ci		flags |= DST_NOCOUNT;
10538c2ecf20Sopenharmony_ci	if (rt->dst_nopolicy)
10548c2ecf20Sopenharmony_ci		flags |= DST_NOPOLICY;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	return flags;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	rt->dst.error = ip6_rt_type_to_error(fib6_type);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	switch (fib6_type) {
10648c2ecf20Sopenharmony_ci	case RTN_BLACKHOLE:
10658c2ecf20Sopenharmony_ci		rt->dst.output = dst_discard_out;
10668c2ecf20Sopenharmony_ci		rt->dst.input = dst_discard;
10678c2ecf20Sopenharmony_ci		break;
10688c2ecf20Sopenharmony_ci	case RTN_PROHIBIT:
10698c2ecf20Sopenharmony_ci		rt->dst.output = ip6_pkt_prohibit_out;
10708c2ecf20Sopenharmony_ci		rt->dst.input = ip6_pkt_prohibit;
10718c2ecf20Sopenharmony_ci		break;
10728c2ecf20Sopenharmony_ci	case RTN_THROW:
10738c2ecf20Sopenharmony_ci	case RTN_UNREACHABLE:
10748c2ecf20Sopenharmony_ci	default:
10758c2ecf20Sopenharmony_ci		rt->dst.output = ip6_pkt_discard_out;
10768c2ecf20Sopenharmony_ci		rt->dst.input = ip6_pkt_discard;
10778c2ecf20Sopenharmony_ci		break;
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cistatic void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
10828c2ecf20Sopenharmony_ci{
10838c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (res->fib6_flags & RTF_REJECT) {
10868c2ecf20Sopenharmony_ci		ip6_rt_init_dst_reject(rt, res->fib6_type);
10878c2ecf20Sopenharmony_ci		return;
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	rt->dst.error = 0;
10918c2ecf20Sopenharmony_ci	rt->dst.output = ip6_output;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) {
10948c2ecf20Sopenharmony_ci		rt->dst.input = ip6_input;
10958c2ecf20Sopenharmony_ci	} else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
10968c2ecf20Sopenharmony_ci		rt->dst.input = ip6_mc_input;
10978c2ecf20Sopenharmony_ci	} else {
10988c2ecf20Sopenharmony_ci		rt->dst.input = ip6_forward;
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	if (res->nh->fib_nh_lws) {
11028c2ecf20Sopenharmony_ci		rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
11038c2ecf20Sopenharmony_ci		lwtunnel_set_redirect(&rt->dst);
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	rt->dst.lastuse = jiffies;
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci/* Caller must already hold reference to @from */
11108c2ecf20Sopenharmony_cistatic void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	rt->rt6i_flags &= ~RTF_EXPIRES;
11138c2ecf20Sopenharmony_ci	rcu_assign_pointer(rt->from, from);
11148c2ecf20Sopenharmony_ci	ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci/* Caller must already hold reference to f6i in result */
11188c2ecf20Sopenharmony_cistatic void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
11198c2ecf20Sopenharmony_ci{
11208c2ecf20Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
11218c2ecf20Sopenharmony_ci	const struct net_device *dev = nh->fib_nh_dev;
11228c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	ip6_rt_init_dst(rt, res);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	rt->rt6i_dst = f6i->fib6_dst;
11278c2ecf20Sopenharmony_ci	rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
11288c2ecf20Sopenharmony_ci	rt->rt6i_flags = res->fib6_flags;
11298c2ecf20Sopenharmony_ci	if (nh->fib_nh_gw_family) {
11308c2ecf20Sopenharmony_ci		rt->rt6i_gateway = nh->fib_nh_gw6;
11318c2ecf20Sopenharmony_ci		rt->rt6i_flags |= RTF_GATEWAY;
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci	rt6_set_from(rt, f6i);
11348c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
11358c2ecf20Sopenharmony_ci	rt->rt6i_src = f6i->fib6_src;
11368c2ecf20Sopenharmony_ci#endif
11378c2ecf20Sopenharmony_ci}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cistatic struct fib6_node* fib6_backtrack(struct fib6_node *fn,
11408c2ecf20Sopenharmony_ci					struct in6_addr *saddr)
11418c2ecf20Sopenharmony_ci{
11428c2ecf20Sopenharmony_ci	struct fib6_node *pn, *sn;
11438c2ecf20Sopenharmony_ci	while (1) {
11448c2ecf20Sopenharmony_ci		if (fn->fn_flags & RTN_TL_ROOT)
11458c2ecf20Sopenharmony_ci			return NULL;
11468c2ecf20Sopenharmony_ci		pn = rcu_dereference(fn->parent);
11478c2ecf20Sopenharmony_ci		sn = FIB6_SUBTREE(pn);
11488c2ecf20Sopenharmony_ci		if (sn && sn != fn)
11498c2ecf20Sopenharmony_ci			fn = fib6_node_lookup(sn, NULL, saddr);
11508c2ecf20Sopenharmony_ci		else
11518c2ecf20Sopenharmony_ci			fn = pn;
11528c2ecf20Sopenharmony_ci		if (fn->fn_flags & RTN_RTINFO)
11538c2ecf20Sopenharmony_ci			return fn;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
11588c2ecf20Sopenharmony_ci{
11598c2ecf20Sopenharmony_ci	struct rt6_info *rt = *prt;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	if (dst_hold_safe(&rt->dst))
11628c2ecf20Sopenharmony_ci		return true;
11638c2ecf20Sopenharmony_ci	if (net) {
11648c2ecf20Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
11658c2ecf20Sopenharmony_ci		dst_hold(&rt->dst);
11668c2ecf20Sopenharmony_ci	} else {
11678c2ecf20Sopenharmony_ci		rt = NULL;
11688c2ecf20Sopenharmony_ci	}
11698c2ecf20Sopenharmony_ci	*prt = rt;
11708c2ecf20Sopenharmony_ci	return false;
11718c2ecf20Sopenharmony_ci}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci/* called with rcu_lock held */
11748c2ecf20Sopenharmony_cistatic struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	struct net_device *dev = res->nh->fib_nh_dev;
11778c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
11788c2ecf20Sopenharmony_ci	unsigned short flags;
11798c2ecf20Sopenharmony_ci	struct rt6_info *nrt;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	if (!fib6_info_hold_safe(f6i))
11828c2ecf20Sopenharmony_ci		goto fallback;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	flags = fib6_info_dst_flags(f6i);
11858c2ecf20Sopenharmony_ci	nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
11868c2ecf20Sopenharmony_ci	if (!nrt) {
11878c2ecf20Sopenharmony_ci		fib6_info_release(f6i);
11888c2ecf20Sopenharmony_ci		goto fallback;
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	ip6_rt_copy_init(nrt, res);
11928c2ecf20Sopenharmony_ci	return nrt;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_cifallback:
11958c2ecf20Sopenharmony_ci	nrt = dev_net(dev)->ipv6.ip6_null_entry;
11968c2ecf20Sopenharmony_ci	dst_hold(&nrt->dst);
11978c2ecf20Sopenharmony_ci	return nrt;
11988c2ecf20Sopenharmony_ci}
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net,
12018c2ecf20Sopenharmony_ci					     struct fib6_table *table,
12028c2ecf20Sopenharmony_ci					     struct flowi6 *fl6,
12038c2ecf20Sopenharmony_ci					     const struct sk_buff *skb,
12048c2ecf20Sopenharmony_ci					     int flags)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	struct fib6_result res = {};
12078c2ecf20Sopenharmony_ci	struct fib6_node *fn;
12088c2ecf20Sopenharmony_ci	struct rt6_info *rt;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
12118c2ecf20Sopenharmony_ci		flags &= ~RT6_LOOKUP_F_IFACE;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	rcu_read_lock();
12148c2ecf20Sopenharmony_ci	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
12158c2ecf20Sopenharmony_cirestart:
12168c2ecf20Sopenharmony_ci	res.f6i = rcu_dereference(fn->leaf);
12178c2ecf20Sopenharmony_ci	if (!res.f6i)
12188c2ecf20Sopenharmony_ci		res.f6i = net->ipv6.fib6_null_entry;
12198c2ecf20Sopenharmony_ci	else
12208c2ecf20Sopenharmony_ci		rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
12218c2ecf20Sopenharmony_ci				 flags);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	if (res.f6i == net->ipv6.fib6_null_entry) {
12248c2ecf20Sopenharmony_ci		fn = fib6_backtrack(fn, &fl6->saddr);
12258c2ecf20Sopenharmony_ci		if (fn)
12268c2ecf20Sopenharmony_ci			goto restart;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
12298c2ecf20Sopenharmony_ci		dst_hold(&rt->dst);
12308c2ecf20Sopenharmony_ci		goto out;
12318c2ecf20Sopenharmony_ci	} else if (res.fib6_flags & RTF_REJECT) {
12328c2ecf20Sopenharmony_ci		goto do_create;
12338c2ecf20Sopenharmony_ci	}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
12368c2ecf20Sopenharmony_ci			 fl6->flowi6_oif != 0, skb, flags);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	/* Search through exception table */
12398c2ecf20Sopenharmony_ci	rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
12408c2ecf20Sopenharmony_ci	if (rt) {
12418c2ecf20Sopenharmony_ci		if (ip6_hold_safe(net, &rt))
12428c2ecf20Sopenharmony_ci			dst_use_noref(&rt->dst, jiffies);
12438c2ecf20Sopenharmony_ci	} else {
12448c2ecf20Sopenharmony_cido_create:
12458c2ecf20Sopenharmony_ci		rt = ip6_create_rt_rcu(&res);
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ciout:
12498c2ecf20Sopenharmony_ci	trace_fib6_table_lookup(net, &res, table, fl6);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	rcu_read_unlock();
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	return rt;
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
12578c2ecf20Sopenharmony_ci				   const struct sk_buff *skb, int flags)
12588c2ecf20Sopenharmony_ci{
12598c2ecf20Sopenharmony_ci	return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_lookup);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cistruct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
12648c2ecf20Sopenharmony_ci			    const struct in6_addr *saddr, int oif,
12658c2ecf20Sopenharmony_ci			    const struct sk_buff *skb, int strict)
12668c2ecf20Sopenharmony_ci{
12678c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
12688c2ecf20Sopenharmony_ci		.flowi6_oif = oif,
12698c2ecf20Sopenharmony_ci		.daddr = *daddr,
12708c2ecf20Sopenharmony_ci	};
12718c2ecf20Sopenharmony_ci	struct dst_entry *dst;
12728c2ecf20Sopenharmony_ci	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	if (saddr) {
12758c2ecf20Sopenharmony_ci		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
12768c2ecf20Sopenharmony_ci		flags |= RT6_LOOKUP_F_HAS_SADDR;
12778c2ecf20Sopenharmony_ci	}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
12808c2ecf20Sopenharmony_ci	if (dst->error == 0)
12818c2ecf20Sopenharmony_ci		return (struct rt6_info *) dst;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	dst_release(dst);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	return NULL;
12868c2ecf20Sopenharmony_ci}
12878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rt6_lookup);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci/* ip6_ins_rt is called with FREE table->tb6_lock.
12908c2ecf20Sopenharmony_ci * It takes new route entry, the addition fails by any reason the
12918c2ecf20Sopenharmony_ci * route is released.
12928c2ecf20Sopenharmony_ci * Caller must hold dst before calling it.
12938c2ecf20Sopenharmony_ci */
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_cistatic int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
12968c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack)
12978c2ecf20Sopenharmony_ci{
12988c2ecf20Sopenharmony_ci	int err;
12998c2ecf20Sopenharmony_ci	struct fib6_table *table;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	table = rt->fib6_table;
13028c2ecf20Sopenharmony_ci	spin_lock_bh(&table->tb6_lock);
13038c2ecf20Sopenharmony_ci	err = fib6_add(&table->tb6_root, rt, info, extack);
13048c2ecf20Sopenharmony_ci	spin_unlock_bh(&table->tb6_lock);
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	return err;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ciint ip6_ins_rt(struct net *net, struct fib6_info *rt)
13108c2ecf20Sopenharmony_ci{
13118c2ecf20Sopenharmony_ci	struct nl_info info = {	.nl_net = net, };
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	return __ip6_ins_rt(rt, &info, NULL);
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
13178c2ecf20Sopenharmony_ci					   const struct in6_addr *daddr,
13188c2ecf20Sopenharmony_ci					   const struct in6_addr *saddr)
13198c2ecf20Sopenharmony_ci{
13208c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
13218c2ecf20Sopenharmony_ci	struct net_device *dev;
13228c2ecf20Sopenharmony_ci	struct rt6_info *rt;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	/*
13258c2ecf20Sopenharmony_ci	 *	Clone the route.
13268c2ecf20Sopenharmony_ci	 */
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	if (!fib6_info_hold_safe(f6i))
13298c2ecf20Sopenharmony_ci		return NULL;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	dev = ip6_rt_get_dev_rcu(res);
13328c2ecf20Sopenharmony_ci	rt = ip6_dst_alloc(dev_net(dev), dev, 0);
13338c2ecf20Sopenharmony_ci	if (!rt) {
13348c2ecf20Sopenharmony_ci		fib6_info_release(f6i);
13358c2ecf20Sopenharmony_ci		return NULL;
13368c2ecf20Sopenharmony_ci	}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	ip6_rt_copy_init(rt, res);
13398c2ecf20Sopenharmony_ci	rt->rt6i_flags |= RTF_CACHE;
13408c2ecf20Sopenharmony_ci	rt->rt6i_dst.addr = *daddr;
13418c2ecf20Sopenharmony_ci	rt->rt6i_dst.plen = 128;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	if (!rt6_is_gw_or_nonexthop(res)) {
13448c2ecf20Sopenharmony_ci		if (f6i->fib6_dst.plen != 128 &&
13458c2ecf20Sopenharmony_ci		    ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
13468c2ecf20Sopenharmony_ci			rt->rt6i_flags |= RTF_ANYCAST;
13478c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
13488c2ecf20Sopenharmony_ci		if (rt->rt6i_src.plen && saddr) {
13498c2ecf20Sopenharmony_ci			rt->rt6i_src.addr = *saddr;
13508c2ecf20Sopenharmony_ci			rt->rt6i_src.plen = 128;
13518c2ecf20Sopenharmony_ci		}
13528c2ecf20Sopenharmony_ci#endif
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	return rt;
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
13598c2ecf20Sopenharmony_ci{
13608c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
13618c2ecf20Sopenharmony_ci	unsigned short flags = fib6_info_dst_flags(f6i);
13628c2ecf20Sopenharmony_ci	struct net_device *dev;
13638c2ecf20Sopenharmony_ci	struct rt6_info *pcpu_rt;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	if (!fib6_info_hold_safe(f6i))
13668c2ecf20Sopenharmony_ci		return NULL;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	rcu_read_lock();
13698c2ecf20Sopenharmony_ci	dev = ip6_rt_get_dev_rcu(res);
13708c2ecf20Sopenharmony_ci	pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags | DST_NOCOUNT);
13718c2ecf20Sopenharmony_ci	rcu_read_unlock();
13728c2ecf20Sopenharmony_ci	if (!pcpu_rt) {
13738c2ecf20Sopenharmony_ci		fib6_info_release(f6i);
13748c2ecf20Sopenharmony_ci		return NULL;
13758c2ecf20Sopenharmony_ci	}
13768c2ecf20Sopenharmony_ci	ip6_rt_copy_init(pcpu_rt, res);
13778c2ecf20Sopenharmony_ci	pcpu_rt->rt6i_flags |= RTF_PCPU;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	if (f6i->nh)
13808c2ecf20Sopenharmony_ci		pcpu_rt->sernum = rt_genid_ipv6(dev_net(dev));
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	return pcpu_rt;
13838c2ecf20Sopenharmony_ci}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_cistatic bool rt6_is_valid(const struct rt6_info *rt6)
13868c2ecf20Sopenharmony_ci{
13878c2ecf20Sopenharmony_ci	return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev));
13888c2ecf20Sopenharmony_ci}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci/* It should be called with rcu_read_lock() acquired */
13918c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
13928c2ecf20Sopenharmony_ci{
13938c2ecf20Sopenharmony_ci	struct rt6_info *pcpu_rt;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if (pcpu_rt && pcpu_rt->sernum && !rt6_is_valid(pcpu_rt)) {
13988c2ecf20Sopenharmony_ci		struct rt6_info *prev, **p;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci		p = this_cpu_ptr(res->nh->rt6i_pcpu);
14018c2ecf20Sopenharmony_ci		/* Paired with READ_ONCE() in __fib6_drop_pcpu_from() */
14028c2ecf20Sopenharmony_ci		prev = xchg(p, NULL);
14038c2ecf20Sopenharmony_ci		if (prev) {
14048c2ecf20Sopenharmony_ci			dst_dev_put(&prev->dst);
14058c2ecf20Sopenharmony_ci			dst_release(&prev->dst);
14068c2ecf20Sopenharmony_ci		}
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci		pcpu_rt = NULL;
14098c2ecf20Sopenharmony_ci	}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	return pcpu_rt;
14128c2ecf20Sopenharmony_ci}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_make_pcpu_route(struct net *net,
14158c2ecf20Sopenharmony_ci					    const struct fib6_result *res)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	struct rt6_info *pcpu_rt, *prev, **p;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	pcpu_rt = ip6_rt_pcpu_alloc(res);
14208c2ecf20Sopenharmony_ci	if (!pcpu_rt)
14218c2ecf20Sopenharmony_ci		return NULL;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	p = this_cpu_ptr(res->nh->rt6i_pcpu);
14248c2ecf20Sopenharmony_ci	prev = cmpxchg(p, NULL, pcpu_rt);
14258c2ecf20Sopenharmony_ci	BUG_ON(prev);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	if (res->f6i->fib6_destroying) {
14288c2ecf20Sopenharmony_ci		struct fib6_info *from;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci		from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
14318c2ecf20Sopenharmony_ci		fib6_info_release(from);
14328c2ecf20Sopenharmony_ci	}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	return pcpu_rt;
14358c2ecf20Sopenharmony_ci}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci/* exception hash table implementation
14388c2ecf20Sopenharmony_ci */
14398c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(rt6_exception_lock);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci/* Remove rt6_ex from hash table and free the memory
14428c2ecf20Sopenharmony_ci * Caller must hold rt6_exception_lock
14438c2ecf20Sopenharmony_ci */
14448c2ecf20Sopenharmony_cistatic void rt6_remove_exception(struct rt6_exception_bucket *bucket,
14458c2ecf20Sopenharmony_ci				 struct rt6_exception *rt6_ex)
14468c2ecf20Sopenharmony_ci{
14478c2ecf20Sopenharmony_ci	struct fib6_info *from;
14488c2ecf20Sopenharmony_ci	struct net *net;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	if (!bucket || !rt6_ex)
14518c2ecf20Sopenharmony_ci		return;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	net = dev_net(rt6_ex->rt6i->dst.dev);
14548c2ecf20Sopenharmony_ci	net->ipv6.rt6_stats->fib_rt_cache--;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	/* purge completely the exception to allow releasing the held resources:
14578c2ecf20Sopenharmony_ci	 * some [sk] cache may keep the dst around for unlimited time
14588c2ecf20Sopenharmony_ci	 */
14598c2ecf20Sopenharmony_ci	from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL);
14608c2ecf20Sopenharmony_ci	fib6_info_release(from);
14618c2ecf20Sopenharmony_ci	dst_dev_put(&rt6_ex->rt6i->dst);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	hlist_del_rcu(&rt6_ex->hlist);
14648c2ecf20Sopenharmony_ci	dst_release(&rt6_ex->rt6i->dst);
14658c2ecf20Sopenharmony_ci	kfree_rcu(rt6_ex, rcu);
14668c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!bucket->depth);
14678c2ecf20Sopenharmony_ci	bucket->depth--;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci/* Remove oldest rt6_ex in bucket and free the memory
14718c2ecf20Sopenharmony_ci * Caller must hold rt6_exception_lock
14728c2ecf20Sopenharmony_ci */
14738c2ecf20Sopenharmony_cistatic void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
14748c2ecf20Sopenharmony_ci{
14758c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex, *oldest = NULL;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	if (!bucket)
14788c2ecf20Sopenharmony_ci		return;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
14818c2ecf20Sopenharmony_ci		if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
14828c2ecf20Sopenharmony_ci			oldest = rt6_ex;
14838c2ecf20Sopenharmony_ci	}
14848c2ecf20Sopenharmony_ci	rt6_remove_exception(bucket, oldest);
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic u32 rt6_exception_hash(const struct in6_addr *dst,
14888c2ecf20Sopenharmony_ci			      const struct in6_addr *src)
14898c2ecf20Sopenharmony_ci{
14908c2ecf20Sopenharmony_ci	static siphash_key_t rt6_exception_key __read_mostly;
14918c2ecf20Sopenharmony_ci	struct {
14928c2ecf20Sopenharmony_ci		struct in6_addr dst;
14938c2ecf20Sopenharmony_ci		struct in6_addr src;
14948c2ecf20Sopenharmony_ci	} __aligned(SIPHASH_ALIGNMENT) combined = {
14958c2ecf20Sopenharmony_ci		.dst = *dst,
14968c2ecf20Sopenharmony_ci	};
14978c2ecf20Sopenharmony_ci	u64 val;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	net_get_random_once(&rt6_exception_key, sizeof(rt6_exception_key));
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
15028c2ecf20Sopenharmony_ci	if (src)
15038c2ecf20Sopenharmony_ci		combined.src = *src;
15048c2ecf20Sopenharmony_ci#endif
15058c2ecf20Sopenharmony_ci	val = siphash(&combined, sizeof(combined), &rt6_exception_key);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	return hash_64(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
15088c2ecf20Sopenharmony_ci}
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci/* Helper function to find the cached rt in the hash table
15118c2ecf20Sopenharmony_ci * and update bucket pointer to point to the bucket for this
15128c2ecf20Sopenharmony_ci * (daddr, saddr) pair
15138c2ecf20Sopenharmony_ci * Caller must hold rt6_exception_lock
15148c2ecf20Sopenharmony_ci */
15158c2ecf20Sopenharmony_cistatic struct rt6_exception *
15168c2ecf20Sopenharmony_ci__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
15178c2ecf20Sopenharmony_ci			      const struct in6_addr *daddr,
15188c2ecf20Sopenharmony_ci			      const struct in6_addr *saddr)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
15218c2ecf20Sopenharmony_ci	u32 hval;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	if (!(*bucket) || !daddr)
15248c2ecf20Sopenharmony_ci		return NULL;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	hval = rt6_exception_hash(daddr, saddr);
15278c2ecf20Sopenharmony_ci	*bucket += hval;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
15308c2ecf20Sopenharmony_ci		struct rt6_info *rt6 = rt6_ex->rt6i;
15318c2ecf20Sopenharmony_ci		bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
15348c2ecf20Sopenharmony_ci		if (matched && saddr)
15358c2ecf20Sopenharmony_ci			matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
15368c2ecf20Sopenharmony_ci#endif
15378c2ecf20Sopenharmony_ci		if (matched)
15388c2ecf20Sopenharmony_ci			return rt6_ex;
15398c2ecf20Sopenharmony_ci	}
15408c2ecf20Sopenharmony_ci	return NULL;
15418c2ecf20Sopenharmony_ci}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci/* Helper function to find the cached rt in the hash table
15448c2ecf20Sopenharmony_ci * and update bucket pointer to point to the bucket for this
15458c2ecf20Sopenharmony_ci * (daddr, saddr) pair
15468c2ecf20Sopenharmony_ci * Caller must hold rcu_read_lock()
15478c2ecf20Sopenharmony_ci */
15488c2ecf20Sopenharmony_cistatic struct rt6_exception *
15498c2ecf20Sopenharmony_ci__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
15508c2ecf20Sopenharmony_ci			 const struct in6_addr *daddr,
15518c2ecf20Sopenharmony_ci			 const struct in6_addr *saddr)
15528c2ecf20Sopenharmony_ci{
15538c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
15548c2ecf20Sopenharmony_ci	u32 hval;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!rcu_read_lock_held());
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	if (!(*bucket) || !daddr)
15598c2ecf20Sopenharmony_ci		return NULL;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	hval = rt6_exception_hash(daddr, saddr);
15628c2ecf20Sopenharmony_ci	*bucket += hval;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
15658c2ecf20Sopenharmony_ci		struct rt6_info *rt6 = rt6_ex->rt6i;
15668c2ecf20Sopenharmony_ci		bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
15698c2ecf20Sopenharmony_ci		if (matched && saddr)
15708c2ecf20Sopenharmony_ci			matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
15718c2ecf20Sopenharmony_ci#endif
15728c2ecf20Sopenharmony_ci		if (matched)
15738c2ecf20Sopenharmony_ci			return rt6_ex;
15748c2ecf20Sopenharmony_ci	}
15758c2ecf20Sopenharmony_ci	return NULL;
15768c2ecf20Sopenharmony_ci}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_cistatic unsigned int fib6_mtu(const struct fib6_result *res)
15798c2ecf20Sopenharmony_ci{
15808c2ecf20Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
15818c2ecf20Sopenharmony_ci	unsigned int mtu;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	if (res->f6i->fib6_pmtu) {
15848c2ecf20Sopenharmony_ci		mtu = res->f6i->fib6_pmtu;
15858c2ecf20Sopenharmony_ci	} else {
15868c2ecf20Sopenharmony_ci		struct net_device *dev = nh->fib_nh_dev;
15878c2ecf20Sopenharmony_ci		struct inet6_dev *idev;
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci		rcu_read_lock();
15908c2ecf20Sopenharmony_ci		idev = __in6_dev_get(dev);
15918c2ecf20Sopenharmony_ci		mtu = idev->cnf.mtu6;
15928c2ecf20Sopenharmony_ci		rcu_read_unlock();
15938c2ecf20Sopenharmony_ci	}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
15988c2ecf20Sopenharmony_ci}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci#define FIB6_EXCEPTION_BUCKET_FLUSHED  0x1UL
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci/* used when the flushed bit is not relevant, only access to the bucket
16038c2ecf20Sopenharmony_ci * (ie., all bucket users except rt6_insert_exception);
16048c2ecf20Sopenharmony_ci *
16058c2ecf20Sopenharmony_ci * called under rcu lock; sometimes called with rt6_exception_lock held
16068c2ecf20Sopenharmony_ci */
16078c2ecf20Sopenharmony_cistatic
16088c2ecf20Sopenharmony_cistruct rt6_exception_bucket *fib6_nh_get_excptn_bucket(const struct fib6_nh *nh,
16098c2ecf20Sopenharmony_ci						       spinlock_t *lock)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	if (lock)
16148c2ecf20Sopenharmony_ci		bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
16158c2ecf20Sopenharmony_ci						   lockdep_is_held(lock));
16168c2ecf20Sopenharmony_ci	else
16178c2ecf20Sopenharmony_ci		bucket = rcu_dereference(nh->rt6i_exception_bucket);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	/* remove bucket flushed bit if set */
16208c2ecf20Sopenharmony_ci	if (bucket) {
16218c2ecf20Sopenharmony_ci		unsigned long p = (unsigned long)bucket;
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci		p &= ~FIB6_EXCEPTION_BUCKET_FLUSHED;
16248c2ecf20Sopenharmony_ci		bucket = (struct rt6_exception_bucket *)p;
16258c2ecf20Sopenharmony_ci	}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	return bucket;
16288c2ecf20Sopenharmony_ci}
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_cistatic bool fib6_nh_excptn_bucket_flushed(struct rt6_exception_bucket *bucket)
16318c2ecf20Sopenharmony_ci{
16328c2ecf20Sopenharmony_ci	unsigned long p = (unsigned long)bucket;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	return !!(p & FIB6_EXCEPTION_BUCKET_FLUSHED);
16358c2ecf20Sopenharmony_ci}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci/* called with rt6_exception_lock held */
16388c2ecf20Sopenharmony_cistatic void fib6_nh_excptn_bucket_set_flushed(struct fib6_nh *nh,
16398c2ecf20Sopenharmony_ci					      spinlock_t *lock)
16408c2ecf20Sopenharmony_ci{
16418c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
16428c2ecf20Sopenharmony_ci	unsigned long p;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
16458c2ecf20Sopenharmony_ci					   lockdep_is_held(lock));
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	p = (unsigned long)bucket;
16488c2ecf20Sopenharmony_ci	p |= FIB6_EXCEPTION_BUCKET_FLUSHED;
16498c2ecf20Sopenharmony_ci	bucket = (struct rt6_exception_bucket *)p;
16508c2ecf20Sopenharmony_ci	rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic int rt6_insert_exception(struct rt6_info *nrt,
16548c2ecf20Sopenharmony_ci				const struct fib6_result *res)
16558c2ecf20Sopenharmony_ci{
16568c2ecf20Sopenharmony_ci	struct net *net = dev_net(nrt->dst.dev);
16578c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
16588c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
16598c2ecf20Sopenharmony_ci	struct in6_addr *src_key = NULL;
16608c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
16618c2ecf20Sopenharmony_ci	struct fib6_nh *nh = res->nh;
16628c2ecf20Sopenharmony_ci	int max_depth;
16638c2ecf20Sopenharmony_ci	int err = 0;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
16688c2ecf20Sopenharmony_ci					  lockdep_is_held(&rt6_exception_lock));
16698c2ecf20Sopenharmony_ci	if (!bucket) {
16708c2ecf20Sopenharmony_ci		bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
16718c2ecf20Sopenharmony_ci				 GFP_ATOMIC);
16728c2ecf20Sopenharmony_ci		if (!bucket) {
16738c2ecf20Sopenharmony_ci			err = -ENOMEM;
16748c2ecf20Sopenharmony_ci			goto out;
16758c2ecf20Sopenharmony_ci		}
16768c2ecf20Sopenharmony_ci		rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
16778c2ecf20Sopenharmony_ci	} else if (fib6_nh_excptn_bucket_flushed(bucket)) {
16788c2ecf20Sopenharmony_ci		err = -EINVAL;
16798c2ecf20Sopenharmony_ci		goto out;
16808c2ecf20Sopenharmony_ci	}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
16838c2ecf20Sopenharmony_ci	/* fib6_src.plen != 0 indicates f6i is in subtree
16848c2ecf20Sopenharmony_ci	 * and exception table is indexed by a hash of
16858c2ecf20Sopenharmony_ci	 * both fib6_dst and fib6_src.
16868c2ecf20Sopenharmony_ci	 * Otherwise, the exception table is indexed by
16878c2ecf20Sopenharmony_ci	 * a hash of only fib6_dst.
16888c2ecf20Sopenharmony_ci	 */
16898c2ecf20Sopenharmony_ci	if (f6i->fib6_src.plen)
16908c2ecf20Sopenharmony_ci		src_key = &nrt->rt6i_src.addr;
16918c2ecf20Sopenharmony_ci#endif
16928c2ecf20Sopenharmony_ci	/* rt6_mtu_change() might lower mtu on f6i.
16938c2ecf20Sopenharmony_ci	 * Only insert this exception route if its mtu
16948c2ecf20Sopenharmony_ci	 * is less than f6i's mtu value.
16958c2ecf20Sopenharmony_ci	 */
16968c2ecf20Sopenharmony_ci	if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
16978c2ecf20Sopenharmony_ci		err = -EINVAL;
16988c2ecf20Sopenharmony_ci		goto out;
16998c2ecf20Sopenharmony_ci	}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
17028c2ecf20Sopenharmony_ci					       src_key);
17038c2ecf20Sopenharmony_ci	if (rt6_ex)
17048c2ecf20Sopenharmony_ci		rt6_remove_exception(bucket, rt6_ex);
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
17078c2ecf20Sopenharmony_ci	if (!rt6_ex) {
17088c2ecf20Sopenharmony_ci		err = -ENOMEM;
17098c2ecf20Sopenharmony_ci		goto out;
17108c2ecf20Sopenharmony_ci	}
17118c2ecf20Sopenharmony_ci	rt6_ex->rt6i = nrt;
17128c2ecf20Sopenharmony_ci	rt6_ex->stamp = jiffies;
17138c2ecf20Sopenharmony_ci	hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
17148c2ecf20Sopenharmony_ci	bucket->depth++;
17158c2ecf20Sopenharmony_ci	net->ipv6.rt6_stats->fib_rt_cache++;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	/* Randomize max depth to avoid some side channels attacks. */
17188c2ecf20Sopenharmony_ci	max_depth = FIB6_MAX_DEPTH + prandom_u32_max(FIB6_MAX_DEPTH);
17198c2ecf20Sopenharmony_ci	while (bucket->depth > max_depth)
17208c2ecf20Sopenharmony_ci		rt6_exception_remove_oldest(bucket);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ciout:
17238c2ecf20Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	/* Update fn->fn_sernum to invalidate all cached dst */
17268c2ecf20Sopenharmony_ci	if (!err) {
17278c2ecf20Sopenharmony_ci		spin_lock_bh(&f6i->fib6_table->tb6_lock);
17288c2ecf20Sopenharmony_ci		fib6_update_sernum(net, f6i);
17298c2ecf20Sopenharmony_ci		spin_unlock_bh(&f6i->fib6_table->tb6_lock);
17308c2ecf20Sopenharmony_ci		fib6_force_start_gc(net);
17318c2ecf20Sopenharmony_ci	}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	return err;
17348c2ecf20Sopenharmony_ci}
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_cistatic void fib6_nh_flush_exceptions(struct fib6_nh *nh, struct fib6_info *from)
17378c2ecf20Sopenharmony_ci{
17388c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
17398c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
17408c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
17418c2ecf20Sopenharmony_ci	int i;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
17468c2ecf20Sopenharmony_ci	if (!bucket)
17478c2ecf20Sopenharmony_ci		goto out;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	/* Prevent rt6_insert_exception() to recreate the bucket list */
17508c2ecf20Sopenharmony_ci	if (!from)
17518c2ecf20Sopenharmony_ci		fib6_nh_excptn_bucket_set_flushed(nh, &rt6_exception_lock);
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
17548c2ecf20Sopenharmony_ci		hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) {
17558c2ecf20Sopenharmony_ci			if (!from ||
17568c2ecf20Sopenharmony_ci			    rcu_access_pointer(rt6_ex->rt6i->from) == from)
17578c2ecf20Sopenharmony_ci				rt6_remove_exception(bucket, rt6_ex);
17588c2ecf20Sopenharmony_ci		}
17598c2ecf20Sopenharmony_ci		WARN_ON_ONCE(!from && bucket->depth);
17608c2ecf20Sopenharmony_ci		bucket++;
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ciout:
17638c2ecf20Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic int rt6_nh_flush_exceptions(struct fib6_nh *nh, void *arg)
17678c2ecf20Sopenharmony_ci{
17688c2ecf20Sopenharmony_ci	struct fib6_info *f6i = arg;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	fib6_nh_flush_exceptions(nh, f6i);
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	return 0;
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_civoid rt6_flush_exceptions(struct fib6_info *f6i)
17768c2ecf20Sopenharmony_ci{
17778c2ecf20Sopenharmony_ci	if (f6i->nh)
17788c2ecf20Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_flush_exceptions,
17798c2ecf20Sopenharmony_ci					 f6i);
17808c2ecf20Sopenharmony_ci	else
17818c2ecf20Sopenharmony_ci		fib6_nh_flush_exceptions(f6i->fib6_nh, f6i);
17828c2ecf20Sopenharmony_ci}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci/* Find cached rt in the hash table inside passed in rt
17858c2ecf20Sopenharmony_ci * Caller has to hold rcu_read_lock()
17868c2ecf20Sopenharmony_ci */
17878c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
17888c2ecf20Sopenharmony_ci					   const struct in6_addr *daddr,
17898c2ecf20Sopenharmony_ci					   const struct in6_addr *saddr)
17908c2ecf20Sopenharmony_ci{
17918c2ecf20Sopenharmony_ci	const struct in6_addr *src_key = NULL;
17928c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
17938c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
17948c2ecf20Sopenharmony_ci	struct rt6_info *ret = NULL;
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
17978c2ecf20Sopenharmony_ci	/* fib6i_src.plen != 0 indicates f6i is in subtree
17988c2ecf20Sopenharmony_ci	 * and exception table is indexed by a hash of
17998c2ecf20Sopenharmony_ci	 * both fib6_dst and fib6_src.
18008c2ecf20Sopenharmony_ci	 * However, the src addr used to create the hash
18018c2ecf20Sopenharmony_ci	 * might not be exactly the passed in saddr which
18028c2ecf20Sopenharmony_ci	 * is a /128 addr from the flow.
18038c2ecf20Sopenharmony_ci	 * So we need to use f6i->fib6_src to redo lookup
18048c2ecf20Sopenharmony_ci	 * if the passed in saddr does not find anything.
18058c2ecf20Sopenharmony_ci	 * (See the logic in ip6_rt_cache_alloc() on how
18068c2ecf20Sopenharmony_ci	 * rt->rt6i_src is updated.)
18078c2ecf20Sopenharmony_ci	 */
18088c2ecf20Sopenharmony_ci	if (res->f6i->fib6_src.plen)
18098c2ecf20Sopenharmony_ci		src_key = saddr;
18108c2ecf20Sopenharmony_cifind_ex:
18118c2ecf20Sopenharmony_ci#endif
18128c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(res->nh, NULL);
18138c2ecf20Sopenharmony_ci	rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
18168c2ecf20Sopenharmony_ci		ret = rt6_ex->rt6i;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
18198c2ecf20Sopenharmony_ci	/* Use fib6_src as src_key and redo lookup */
18208c2ecf20Sopenharmony_ci	if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) {
18218c2ecf20Sopenharmony_ci		src_key = &res->f6i->fib6_src.addr;
18228c2ecf20Sopenharmony_ci		goto find_ex;
18238c2ecf20Sopenharmony_ci	}
18248c2ecf20Sopenharmony_ci#endif
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	return ret;
18278c2ecf20Sopenharmony_ci}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci/* Remove the passed in cached rt from the hash table that contains it */
18308c2ecf20Sopenharmony_cistatic int fib6_nh_remove_exception(const struct fib6_nh *nh, int plen,
18318c2ecf20Sopenharmony_ci				    const struct rt6_info *rt)
18328c2ecf20Sopenharmony_ci{
18338c2ecf20Sopenharmony_ci	const struct in6_addr *src_key = NULL;
18348c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
18358c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
18368c2ecf20Sopenharmony_ci	int err;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
18398c2ecf20Sopenharmony_ci		return -ENOENT;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
18428c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
18458c2ecf20Sopenharmony_ci	/* rt6i_src.plen != 0 indicates 'from' is in subtree
18468c2ecf20Sopenharmony_ci	 * and exception table is indexed by a hash of
18478c2ecf20Sopenharmony_ci	 * both rt6i_dst and rt6i_src.
18488c2ecf20Sopenharmony_ci	 * Otherwise, the exception table is indexed by
18498c2ecf20Sopenharmony_ci	 * a hash of only rt6i_dst.
18508c2ecf20Sopenharmony_ci	 */
18518c2ecf20Sopenharmony_ci	if (plen)
18528c2ecf20Sopenharmony_ci		src_key = &rt->rt6i_src.addr;
18538c2ecf20Sopenharmony_ci#endif
18548c2ecf20Sopenharmony_ci	rt6_ex = __rt6_find_exception_spinlock(&bucket,
18558c2ecf20Sopenharmony_ci					       &rt->rt6i_dst.addr,
18568c2ecf20Sopenharmony_ci					       src_key);
18578c2ecf20Sopenharmony_ci	if (rt6_ex) {
18588c2ecf20Sopenharmony_ci		rt6_remove_exception(bucket, rt6_ex);
18598c2ecf20Sopenharmony_ci		err = 0;
18608c2ecf20Sopenharmony_ci	} else {
18618c2ecf20Sopenharmony_ci		err = -ENOENT;
18628c2ecf20Sopenharmony_ci	}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
18658c2ecf20Sopenharmony_ci	return err;
18668c2ecf20Sopenharmony_ci}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_cistruct fib6_nh_excptn_arg {
18698c2ecf20Sopenharmony_ci	struct rt6_info	*rt;
18708c2ecf20Sopenharmony_ci	int		plen;
18718c2ecf20Sopenharmony_ci};
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_cistatic int rt6_nh_remove_exception_rt(struct fib6_nh *nh, void *_arg)
18748c2ecf20Sopenharmony_ci{
18758c2ecf20Sopenharmony_ci	struct fib6_nh_excptn_arg *arg = _arg;
18768c2ecf20Sopenharmony_ci	int err;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	err = fib6_nh_remove_exception(nh, arg->plen, arg->rt);
18798c2ecf20Sopenharmony_ci	if (err == 0)
18808c2ecf20Sopenharmony_ci		return 1;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	return 0;
18838c2ecf20Sopenharmony_ci}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_cistatic int rt6_remove_exception_rt(struct rt6_info *rt)
18868c2ecf20Sopenharmony_ci{
18878c2ecf20Sopenharmony_ci	struct fib6_info *from;
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	from = rcu_dereference(rt->from);
18908c2ecf20Sopenharmony_ci	if (!from || !(rt->rt6i_flags & RTF_CACHE))
18918c2ecf20Sopenharmony_ci		return -EINVAL;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	if (from->nh) {
18948c2ecf20Sopenharmony_ci		struct fib6_nh_excptn_arg arg = {
18958c2ecf20Sopenharmony_ci			.rt = rt,
18968c2ecf20Sopenharmony_ci			.plen = from->fib6_src.plen
18978c2ecf20Sopenharmony_ci		};
18988c2ecf20Sopenharmony_ci		int rc;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci		/* rc = 1 means an entry was found */
19018c2ecf20Sopenharmony_ci		rc = nexthop_for_each_fib6_nh(from->nh,
19028c2ecf20Sopenharmony_ci					      rt6_nh_remove_exception_rt,
19038c2ecf20Sopenharmony_ci					      &arg);
19048c2ecf20Sopenharmony_ci		return rc ? 0 : -ENOENT;
19058c2ecf20Sopenharmony_ci	}
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	return fib6_nh_remove_exception(from->fib6_nh,
19088c2ecf20Sopenharmony_ci					from->fib6_src.plen, rt);
19098c2ecf20Sopenharmony_ci}
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci/* Find rt6_ex which contains the passed in rt cache and
19128c2ecf20Sopenharmony_ci * refresh its stamp
19138c2ecf20Sopenharmony_ci */
19148c2ecf20Sopenharmony_cistatic void fib6_nh_update_exception(const struct fib6_nh *nh, int plen,
19158c2ecf20Sopenharmony_ci				     const struct rt6_info *rt)
19168c2ecf20Sopenharmony_ci{
19178c2ecf20Sopenharmony_ci	const struct in6_addr *src_key = NULL;
19188c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
19198c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, NULL);
19228c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
19238c2ecf20Sopenharmony_ci	/* rt6i_src.plen != 0 indicates 'from' is in subtree
19248c2ecf20Sopenharmony_ci	 * and exception table is indexed by a hash of
19258c2ecf20Sopenharmony_ci	 * both rt6i_dst and rt6i_src.
19268c2ecf20Sopenharmony_ci	 * Otherwise, the exception table is indexed by
19278c2ecf20Sopenharmony_ci	 * a hash of only rt6i_dst.
19288c2ecf20Sopenharmony_ci	 */
19298c2ecf20Sopenharmony_ci	if (plen)
19308c2ecf20Sopenharmony_ci		src_key = &rt->rt6i_src.addr;
19318c2ecf20Sopenharmony_ci#endif
19328c2ecf20Sopenharmony_ci	rt6_ex = __rt6_find_exception_rcu(&bucket, &rt->rt6i_dst.addr, src_key);
19338c2ecf20Sopenharmony_ci	if (rt6_ex)
19348c2ecf20Sopenharmony_ci		rt6_ex->stamp = jiffies;
19358c2ecf20Sopenharmony_ci}
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_cistruct fib6_nh_match_arg {
19388c2ecf20Sopenharmony_ci	const struct net_device *dev;
19398c2ecf20Sopenharmony_ci	const struct in6_addr	*gw;
19408c2ecf20Sopenharmony_ci	struct fib6_nh		*match;
19418c2ecf20Sopenharmony_ci};
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci/* determine if fib6_nh has given device and gateway */
19448c2ecf20Sopenharmony_cistatic int fib6_nh_find_match(struct fib6_nh *nh, void *_arg)
19458c2ecf20Sopenharmony_ci{
19468c2ecf20Sopenharmony_ci	struct fib6_nh_match_arg *arg = _arg;
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci	if (arg->dev != nh->fib_nh_dev ||
19498c2ecf20Sopenharmony_ci	    (arg->gw && !nh->fib_nh_gw_family) ||
19508c2ecf20Sopenharmony_ci	    (!arg->gw && nh->fib_nh_gw_family) ||
19518c2ecf20Sopenharmony_ci	    (arg->gw && !ipv6_addr_equal(arg->gw, &nh->fib_nh_gw6)))
19528c2ecf20Sopenharmony_ci		return 0;
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	arg->match = nh;
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci	/* found a match, break the loop */
19578c2ecf20Sopenharmony_ci	return 1;
19588c2ecf20Sopenharmony_ci}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_cistatic void rt6_update_exception_stamp_rt(struct rt6_info *rt)
19618c2ecf20Sopenharmony_ci{
19628c2ecf20Sopenharmony_ci	struct fib6_info *from;
19638c2ecf20Sopenharmony_ci	struct fib6_nh *fib6_nh;
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci	rcu_read_lock();
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci	from = rcu_dereference(rt->from);
19688c2ecf20Sopenharmony_ci	if (!from || !(rt->rt6i_flags & RTF_CACHE))
19698c2ecf20Sopenharmony_ci		goto unlock;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	if (from->nh) {
19728c2ecf20Sopenharmony_ci		struct fib6_nh_match_arg arg = {
19738c2ecf20Sopenharmony_ci			.dev = rt->dst.dev,
19748c2ecf20Sopenharmony_ci			.gw = &rt->rt6i_gateway,
19758c2ecf20Sopenharmony_ci		};
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci		nexthop_for_each_fib6_nh(from->nh, fib6_nh_find_match, &arg);
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci		if (!arg.match)
19808c2ecf20Sopenharmony_ci			goto unlock;
19818c2ecf20Sopenharmony_ci		fib6_nh = arg.match;
19828c2ecf20Sopenharmony_ci	} else {
19838c2ecf20Sopenharmony_ci		fib6_nh = from->fib6_nh;
19848c2ecf20Sopenharmony_ci	}
19858c2ecf20Sopenharmony_ci	fib6_nh_update_exception(fib6_nh, from->fib6_src.plen, rt);
19868c2ecf20Sopenharmony_ciunlock:
19878c2ecf20Sopenharmony_ci	rcu_read_unlock();
19888c2ecf20Sopenharmony_ci}
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_cistatic bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
19918c2ecf20Sopenharmony_ci					 struct rt6_info *rt, int mtu)
19928c2ecf20Sopenharmony_ci{
19938c2ecf20Sopenharmony_ci	/* If the new MTU is lower than the route PMTU, this new MTU will be the
19948c2ecf20Sopenharmony_ci	 * lowest MTU in the path: always allow updating the route PMTU to
19958c2ecf20Sopenharmony_ci	 * reflect PMTU decreases.
19968c2ecf20Sopenharmony_ci	 *
19978c2ecf20Sopenharmony_ci	 * If the new MTU is higher, and the route PMTU is equal to the local
19988c2ecf20Sopenharmony_ci	 * MTU, this means the old MTU is the lowest in the path, so allow
19998c2ecf20Sopenharmony_ci	 * updating it: if other nodes now have lower MTUs, PMTU discovery will
20008c2ecf20Sopenharmony_ci	 * handle this.
20018c2ecf20Sopenharmony_ci	 */
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	if (dst_mtu(&rt->dst) >= mtu)
20048c2ecf20Sopenharmony_ci		return true;
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
20078c2ecf20Sopenharmony_ci		return true;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	return false;
20108c2ecf20Sopenharmony_ci}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_cistatic void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
20138c2ecf20Sopenharmony_ci				       const struct fib6_nh *nh, int mtu)
20148c2ecf20Sopenharmony_ci{
20158c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
20168c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
20178c2ecf20Sopenharmony_ci	int i;
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
20208c2ecf20Sopenharmony_ci	if (!bucket)
20218c2ecf20Sopenharmony_ci		return;
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
20248c2ecf20Sopenharmony_ci		hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
20258c2ecf20Sopenharmony_ci			struct rt6_info *entry = rt6_ex->rt6i;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci			/* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
20288c2ecf20Sopenharmony_ci			 * route), the metrics of its rt->from have already
20298c2ecf20Sopenharmony_ci			 * been updated.
20308c2ecf20Sopenharmony_ci			 */
20318c2ecf20Sopenharmony_ci			if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
20328c2ecf20Sopenharmony_ci			    rt6_mtu_change_route_allowed(idev, entry, mtu))
20338c2ecf20Sopenharmony_ci				dst_metric_set(&entry->dst, RTAX_MTU, mtu);
20348c2ecf20Sopenharmony_ci		}
20358c2ecf20Sopenharmony_ci		bucket++;
20368c2ecf20Sopenharmony_ci	}
20378c2ecf20Sopenharmony_ci}
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_ci#define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_cistatic void fib6_nh_exceptions_clean_tohost(const struct fib6_nh *nh,
20428c2ecf20Sopenharmony_ci					    const struct in6_addr *gateway)
20438c2ecf20Sopenharmony_ci{
20448c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
20458c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
20468c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
20478c2ecf20Sopenharmony_ci	int i;
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
20508c2ecf20Sopenharmony_ci		return;
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
20538c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
20548c2ecf20Sopenharmony_ci	if (bucket) {
20558c2ecf20Sopenharmony_ci		for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
20568c2ecf20Sopenharmony_ci			hlist_for_each_entry_safe(rt6_ex, tmp,
20578c2ecf20Sopenharmony_ci						  &bucket->chain, hlist) {
20588c2ecf20Sopenharmony_ci				struct rt6_info *entry = rt6_ex->rt6i;
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci				if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
20618c2ecf20Sopenharmony_ci				    RTF_CACHE_GATEWAY &&
20628c2ecf20Sopenharmony_ci				    ipv6_addr_equal(gateway,
20638c2ecf20Sopenharmony_ci						    &entry->rt6i_gateway)) {
20648c2ecf20Sopenharmony_ci					rt6_remove_exception(bucket, rt6_ex);
20658c2ecf20Sopenharmony_ci				}
20668c2ecf20Sopenharmony_ci			}
20678c2ecf20Sopenharmony_ci			bucket++;
20688c2ecf20Sopenharmony_ci		}
20698c2ecf20Sopenharmony_ci	}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
20728c2ecf20Sopenharmony_ci}
20738c2ecf20Sopenharmony_ci
20748c2ecf20Sopenharmony_cistatic void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
20758c2ecf20Sopenharmony_ci				      struct rt6_exception *rt6_ex,
20768c2ecf20Sopenharmony_ci				      struct fib6_gc_args *gc_args,
20778c2ecf20Sopenharmony_ci				      unsigned long now)
20788c2ecf20Sopenharmony_ci{
20798c2ecf20Sopenharmony_ci	struct rt6_info *rt = rt6_ex->rt6i;
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci	/* we are pruning and obsoleting aged-out and non gateway exceptions
20828c2ecf20Sopenharmony_ci	 * even if others have still references to them, so that on next
20838c2ecf20Sopenharmony_ci	 * dst_check() such references can be dropped.
20848c2ecf20Sopenharmony_ci	 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
20858c2ecf20Sopenharmony_ci	 * expired, independently from their aging, as per RFC 8201 section 4
20868c2ecf20Sopenharmony_ci	 */
20878c2ecf20Sopenharmony_ci	if (!(rt->rt6i_flags & RTF_EXPIRES)) {
20888c2ecf20Sopenharmony_ci		if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
20898c2ecf20Sopenharmony_ci			RT6_TRACE("aging clone %p\n", rt);
20908c2ecf20Sopenharmony_ci			rt6_remove_exception(bucket, rt6_ex);
20918c2ecf20Sopenharmony_ci			return;
20928c2ecf20Sopenharmony_ci		}
20938c2ecf20Sopenharmony_ci	} else if (time_after(jiffies, rt->dst.expires)) {
20948c2ecf20Sopenharmony_ci		RT6_TRACE("purging expired route %p\n", rt);
20958c2ecf20Sopenharmony_ci		rt6_remove_exception(bucket, rt6_ex);
20968c2ecf20Sopenharmony_ci		return;
20978c2ecf20Sopenharmony_ci	}
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	if (rt->rt6i_flags & RTF_GATEWAY) {
21008c2ecf20Sopenharmony_ci		struct neighbour *neigh;
21018c2ecf20Sopenharmony_ci		__u8 neigh_flags = 0;
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci		neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
21048c2ecf20Sopenharmony_ci		if (neigh)
21058c2ecf20Sopenharmony_ci			neigh_flags = neigh->flags;
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci		if (!(neigh_flags & NTF_ROUTER)) {
21088c2ecf20Sopenharmony_ci			RT6_TRACE("purging route %p via non-router but gateway\n",
21098c2ecf20Sopenharmony_ci				  rt);
21108c2ecf20Sopenharmony_ci			rt6_remove_exception(bucket, rt6_ex);
21118c2ecf20Sopenharmony_ci			return;
21128c2ecf20Sopenharmony_ci		}
21138c2ecf20Sopenharmony_ci	}
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci	gc_args->more++;
21168c2ecf20Sopenharmony_ci}
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_cistatic void fib6_nh_age_exceptions(const struct fib6_nh *nh,
21198c2ecf20Sopenharmony_ci				   struct fib6_gc_args *gc_args,
21208c2ecf20Sopenharmony_ci				   unsigned long now)
21218c2ecf20Sopenharmony_ci{
21228c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
21238c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
21248c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
21258c2ecf20Sopenharmony_ci	int i;
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
21288c2ecf20Sopenharmony_ci		return;
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
21318c2ecf20Sopenharmony_ci	spin_lock(&rt6_exception_lock);
21328c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
21338c2ecf20Sopenharmony_ci	if (bucket) {
21348c2ecf20Sopenharmony_ci		for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
21358c2ecf20Sopenharmony_ci			hlist_for_each_entry_safe(rt6_ex, tmp,
21368c2ecf20Sopenharmony_ci						  &bucket->chain, hlist) {
21378c2ecf20Sopenharmony_ci				rt6_age_examine_exception(bucket, rt6_ex,
21388c2ecf20Sopenharmony_ci							  gc_args, now);
21398c2ecf20Sopenharmony_ci			}
21408c2ecf20Sopenharmony_ci			bucket++;
21418c2ecf20Sopenharmony_ci		}
21428c2ecf20Sopenharmony_ci	}
21438c2ecf20Sopenharmony_ci	spin_unlock(&rt6_exception_lock);
21448c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
21458c2ecf20Sopenharmony_ci}
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_cistruct fib6_nh_age_excptn_arg {
21488c2ecf20Sopenharmony_ci	struct fib6_gc_args	*gc_args;
21498c2ecf20Sopenharmony_ci	unsigned long		now;
21508c2ecf20Sopenharmony_ci};
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_cistatic int rt6_nh_age_exceptions(struct fib6_nh *nh, void *_arg)
21538c2ecf20Sopenharmony_ci{
21548c2ecf20Sopenharmony_ci	struct fib6_nh_age_excptn_arg *arg = _arg;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	fib6_nh_age_exceptions(nh, arg->gc_args, arg->now);
21578c2ecf20Sopenharmony_ci	return 0;
21588c2ecf20Sopenharmony_ci}
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_civoid rt6_age_exceptions(struct fib6_info *f6i,
21618c2ecf20Sopenharmony_ci			struct fib6_gc_args *gc_args,
21628c2ecf20Sopenharmony_ci			unsigned long now)
21638c2ecf20Sopenharmony_ci{
21648c2ecf20Sopenharmony_ci	if (f6i->nh) {
21658c2ecf20Sopenharmony_ci		struct fib6_nh_age_excptn_arg arg = {
21668c2ecf20Sopenharmony_ci			.gc_args = gc_args,
21678c2ecf20Sopenharmony_ci			.now = now
21688c2ecf20Sopenharmony_ci		};
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_age_exceptions,
21718c2ecf20Sopenharmony_ci					 &arg);
21728c2ecf20Sopenharmony_ci	} else {
21738c2ecf20Sopenharmony_ci		fib6_nh_age_exceptions(f6i->fib6_nh, gc_args, now);
21748c2ecf20Sopenharmony_ci	}
21758c2ecf20Sopenharmony_ci}
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci/* must be called with rcu lock held */
21788c2ecf20Sopenharmony_ciint fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
21798c2ecf20Sopenharmony_ci		      struct flowi6 *fl6, struct fib6_result *res, int strict)
21808c2ecf20Sopenharmony_ci{
21818c2ecf20Sopenharmony_ci	struct fib6_node *fn, *saved_fn;
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
21848c2ecf20Sopenharmony_ci	saved_fn = fn;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
21878c2ecf20Sopenharmony_ci		oif = 0;
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ciredo_rt6_select:
21908c2ecf20Sopenharmony_ci	rt6_select(net, fn, oif, res, strict);
21918c2ecf20Sopenharmony_ci	if (res->f6i == net->ipv6.fib6_null_entry) {
21928c2ecf20Sopenharmony_ci		fn = fib6_backtrack(fn, &fl6->saddr);
21938c2ecf20Sopenharmony_ci		if (fn)
21948c2ecf20Sopenharmony_ci			goto redo_rt6_select;
21958c2ecf20Sopenharmony_ci		else if (strict & RT6_LOOKUP_F_REACHABLE) {
21968c2ecf20Sopenharmony_ci			/* also consider unreachable route */
21978c2ecf20Sopenharmony_ci			strict &= ~RT6_LOOKUP_F_REACHABLE;
21988c2ecf20Sopenharmony_ci			fn = saved_fn;
21998c2ecf20Sopenharmony_ci			goto redo_rt6_select;
22008c2ecf20Sopenharmony_ci		}
22018c2ecf20Sopenharmony_ci	}
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	trace_fib6_table_lookup(net, res, table, fl6);
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	return 0;
22068c2ecf20Sopenharmony_ci}
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_cistruct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
22098c2ecf20Sopenharmony_ci			       int oif, struct flowi6 *fl6,
22108c2ecf20Sopenharmony_ci			       const struct sk_buff *skb, int flags)
22118c2ecf20Sopenharmony_ci{
22128c2ecf20Sopenharmony_ci	struct fib6_result res = {};
22138c2ecf20Sopenharmony_ci	struct rt6_info *rt = NULL;
22148c2ecf20Sopenharmony_ci	int strict = 0;
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) &&
22178c2ecf20Sopenharmony_ci		     !rcu_read_lock_held());
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	strict |= flags & RT6_LOOKUP_F_IFACE;
22208c2ecf20Sopenharmony_ci	strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
22218c2ecf20Sopenharmony_ci	if (net->ipv6.devconf_all->forwarding == 0)
22228c2ecf20Sopenharmony_ci		strict |= RT6_LOOKUP_F_REACHABLE;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	rcu_read_lock();
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	fib6_table_lookup(net, table, oif, fl6, &res, strict);
22278c2ecf20Sopenharmony_ci	if (res.f6i == net->ipv6.fib6_null_entry)
22288c2ecf20Sopenharmony_ci		goto out;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	fib6_select_path(net, &res, fl6, oif, false, skb, strict);
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci	/*Search through exception table */
22338c2ecf20Sopenharmony_ci	rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
22348c2ecf20Sopenharmony_ci	if (rt) {
22358c2ecf20Sopenharmony_ci		goto out;
22368c2ecf20Sopenharmony_ci	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
22378c2ecf20Sopenharmony_ci			    !res.nh->fib_nh_gw_family)) {
22388c2ecf20Sopenharmony_ci		/* Create a RTF_CACHE clone which will not be
22398c2ecf20Sopenharmony_ci		 * owned by the fib6 tree.  It is for the special case where
22408c2ecf20Sopenharmony_ci		 * the daddr in the skb during the neighbor look-up is different
22418c2ecf20Sopenharmony_ci		 * from the fl6->daddr used to look-up route here.
22428c2ecf20Sopenharmony_ci		 */
22438c2ecf20Sopenharmony_ci		rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_ci		if (rt) {
22468c2ecf20Sopenharmony_ci			/* 1 refcnt is taken during ip6_rt_cache_alloc().
22478c2ecf20Sopenharmony_ci			 * As rt6_uncached_list_add() does not consume refcnt,
22488c2ecf20Sopenharmony_ci			 * this refcnt is always returned to the caller even
22498c2ecf20Sopenharmony_ci			 * if caller sets RT6_LOOKUP_F_DST_NOREF flag.
22508c2ecf20Sopenharmony_ci			 */
22518c2ecf20Sopenharmony_ci			rt6_uncached_list_add(rt);
22528c2ecf20Sopenharmony_ci			atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
22538c2ecf20Sopenharmony_ci			rcu_read_unlock();
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci			return rt;
22568c2ecf20Sopenharmony_ci		}
22578c2ecf20Sopenharmony_ci	} else {
22588c2ecf20Sopenharmony_ci		/* Get a percpu copy */
22598c2ecf20Sopenharmony_ci		local_bh_disable();
22608c2ecf20Sopenharmony_ci		rt = rt6_get_pcpu_route(&res);
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci		if (!rt)
22638c2ecf20Sopenharmony_ci			rt = rt6_make_pcpu_route(net, &res);
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci		local_bh_enable();
22668c2ecf20Sopenharmony_ci	}
22678c2ecf20Sopenharmony_ciout:
22688c2ecf20Sopenharmony_ci	if (!rt)
22698c2ecf20Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
22708c2ecf20Sopenharmony_ci	if (!(flags & RT6_LOOKUP_F_DST_NOREF))
22718c2ecf20Sopenharmony_ci		ip6_hold_safe(net, &rt);
22728c2ecf20Sopenharmony_ci	rcu_read_unlock();
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_ci	return rt;
22758c2ecf20Sopenharmony_ci}
22768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_pol_route);
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_input(struct net *net,
22798c2ecf20Sopenharmony_ci					    struct fib6_table *table,
22808c2ecf20Sopenharmony_ci					    struct flowi6 *fl6,
22818c2ecf20Sopenharmony_ci					    const struct sk_buff *skb,
22828c2ecf20Sopenharmony_ci					    int flags)
22838c2ecf20Sopenharmony_ci{
22848c2ecf20Sopenharmony_ci	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
22858c2ecf20Sopenharmony_ci}
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_input_lookup(struct net *net,
22888c2ecf20Sopenharmony_ci					 struct net_device *dev,
22898c2ecf20Sopenharmony_ci					 struct flowi6 *fl6,
22908c2ecf20Sopenharmony_ci					 const struct sk_buff *skb,
22918c2ecf20Sopenharmony_ci					 int flags)
22928c2ecf20Sopenharmony_ci{
22938c2ecf20Sopenharmony_ci	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
22948c2ecf20Sopenharmony_ci		flags |= RT6_LOOKUP_F_IFACE;
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
22978c2ecf20Sopenharmony_ci}
22988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_input_lookup);
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_cistatic void ip6_multipath_l3_keys(const struct sk_buff *skb,
23018c2ecf20Sopenharmony_ci				  struct flow_keys *keys,
23028c2ecf20Sopenharmony_ci				  struct flow_keys *flkeys)
23038c2ecf20Sopenharmony_ci{
23048c2ecf20Sopenharmony_ci	const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
23058c2ecf20Sopenharmony_ci	const struct ipv6hdr *key_iph = outer_iph;
23068c2ecf20Sopenharmony_ci	struct flow_keys *_flkeys = flkeys;
23078c2ecf20Sopenharmony_ci	const struct ipv6hdr *inner_iph;
23088c2ecf20Sopenharmony_ci	const struct icmp6hdr *icmph;
23098c2ecf20Sopenharmony_ci	struct ipv6hdr _inner_iph;
23108c2ecf20Sopenharmony_ci	struct icmp6hdr _icmph;
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
23138c2ecf20Sopenharmony_ci		goto out;
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	icmph = skb_header_pointer(skb, skb_transport_offset(skb),
23168c2ecf20Sopenharmony_ci				   sizeof(_icmph), &_icmph);
23178c2ecf20Sopenharmony_ci	if (!icmph)
23188c2ecf20Sopenharmony_ci		goto out;
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci	if (!icmpv6_is_err(icmph->icmp6_type))
23218c2ecf20Sopenharmony_ci		goto out;
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	inner_iph = skb_header_pointer(skb,
23248c2ecf20Sopenharmony_ci				       skb_transport_offset(skb) + sizeof(*icmph),
23258c2ecf20Sopenharmony_ci				       sizeof(_inner_iph), &_inner_iph);
23268c2ecf20Sopenharmony_ci	if (!inner_iph)
23278c2ecf20Sopenharmony_ci		goto out;
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	key_iph = inner_iph;
23308c2ecf20Sopenharmony_ci	_flkeys = NULL;
23318c2ecf20Sopenharmony_ciout:
23328c2ecf20Sopenharmony_ci	if (_flkeys) {
23338c2ecf20Sopenharmony_ci		keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
23348c2ecf20Sopenharmony_ci		keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
23358c2ecf20Sopenharmony_ci		keys->tags.flow_label = _flkeys->tags.flow_label;
23368c2ecf20Sopenharmony_ci		keys->basic.ip_proto = _flkeys->basic.ip_proto;
23378c2ecf20Sopenharmony_ci	} else {
23388c2ecf20Sopenharmony_ci		keys->addrs.v6addrs.src = key_iph->saddr;
23398c2ecf20Sopenharmony_ci		keys->addrs.v6addrs.dst = key_iph->daddr;
23408c2ecf20Sopenharmony_ci		keys->tags.flow_label = ip6_flowlabel(key_iph);
23418c2ecf20Sopenharmony_ci		keys->basic.ip_proto = key_iph->nexthdr;
23428c2ecf20Sopenharmony_ci	}
23438c2ecf20Sopenharmony_ci}
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_ci/* if skb is set it will be used and fl6 can be NULL */
23468c2ecf20Sopenharmony_ciu32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
23478c2ecf20Sopenharmony_ci		       const struct sk_buff *skb, struct flow_keys *flkeys)
23488c2ecf20Sopenharmony_ci{
23498c2ecf20Sopenharmony_ci	struct flow_keys hash_keys;
23508c2ecf20Sopenharmony_ci	u32 mhash;
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci	switch (ip6_multipath_hash_policy(net)) {
23538c2ecf20Sopenharmony_ci	case 0:
23548c2ecf20Sopenharmony_ci		memset(&hash_keys, 0, sizeof(hash_keys));
23558c2ecf20Sopenharmony_ci		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
23568c2ecf20Sopenharmony_ci		if (skb) {
23578c2ecf20Sopenharmony_ci			ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
23588c2ecf20Sopenharmony_ci		} else {
23598c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.src = fl6->saddr;
23608c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = fl6->daddr;
23618c2ecf20Sopenharmony_ci			hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
23628c2ecf20Sopenharmony_ci			hash_keys.basic.ip_proto = fl6->flowi6_proto;
23638c2ecf20Sopenharmony_ci		}
23648c2ecf20Sopenharmony_ci		break;
23658c2ecf20Sopenharmony_ci	case 1:
23668c2ecf20Sopenharmony_ci		if (skb) {
23678c2ecf20Sopenharmony_ci			unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
23688c2ecf20Sopenharmony_ci			struct flow_keys keys;
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci			/* short-circuit if we already have L4 hash present */
23718c2ecf20Sopenharmony_ci			if (skb->l4_hash)
23728c2ecf20Sopenharmony_ci				return skb_get_hash_raw(skb) >> 1;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci			memset(&hash_keys, 0, sizeof(hash_keys));
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci                        if (!flkeys) {
23778c2ecf20Sopenharmony_ci				skb_flow_dissect_flow_keys(skb, &keys, flag);
23788c2ecf20Sopenharmony_ci				flkeys = &keys;
23798c2ecf20Sopenharmony_ci			}
23808c2ecf20Sopenharmony_ci			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
23818c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
23828c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
23838c2ecf20Sopenharmony_ci			hash_keys.ports.src = flkeys->ports.src;
23848c2ecf20Sopenharmony_ci			hash_keys.ports.dst = flkeys->ports.dst;
23858c2ecf20Sopenharmony_ci			hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
23868c2ecf20Sopenharmony_ci		} else {
23878c2ecf20Sopenharmony_ci			memset(&hash_keys, 0, sizeof(hash_keys));
23888c2ecf20Sopenharmony_ci			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
23898c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.src = fl6->saddr;
23908c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = fl6->daddr;
23918c2ecf20Sopenharmony_ci			hash_keys.ports.src = fl6->fl6_sport;
23928c2ecf20Sopenharmony_ci			hash_keys.ports.dst = fl6->fl6_dport;
23938c2ecf20Sopenharmony_ci			hash_keys.basic.ip_proto = fl6->flowi6_proto;
23948c2ecf20Sopenharmony_ci		}
23958c2ecf20Sopenharmony_ci		break;
23968c2ecf20Sopenharmony_ci	case 2:
23978c2ecf20Sopenharmony_ci		memset(&hash_keys, 0, sizeof(hash_keys));
23988c2ecf20Sopenharmony_ci		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
23998c2ecf20Sopenharmony_ci		if (skb) {
24008c2ecf20Sopenharmony_ci			struct flow_keys keys;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci			if (!flkeys) {
24038c2ecf20Sopenharmony_ci				skb_flow_dissect_flow_keys(skb, &keys, 0);
24048c2ecf20Sopenharmony_ci				flkeys = &keys;
24058c2ecf20Sopenharmony_ci			}
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci			/* Inner can be v4 or v6 */
24088c2ecf20Sopenharmony_ci			if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
24098c2ecf20Sopenharmony_ci				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
24108c2ecf20Sopenharmony_ci				hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;
24118c2ecf20Sopenharmony_ci				hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;
24128c2ecf20Sopenharmony_ci			} else if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
24138c2ecf20Sopenharmony_ci				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
24148c2ecf20Sopenharmony_ci				hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
24158c2ecf20Sopenharmony_ci				hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
24168c2ecf20Sopenharmony_ci				hash_keys.tags.flow_label = flkeys->tags.flow_label;
24178c2ecf20Sopenharmony_ci				hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
24188c2ecf20Sopenharmony_ci			} else {
24198c2ecf20Sopenharmony_ci				/* Same as case 0 */
24208c2ecf20Sopenharmony_ci				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
24218c2ecf20Sopenharmony_ci				ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
24228c2ecf20Sopenharmony_ci			}
24238c2ecf20Sopenharmony_ci		} else {
24248c2ecf20Sopenharmony_ci			/* Same as case 0 */
24258c2ecf20Sopenharmony_ci			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
24268c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.src = fl6->saddr;
24278c2ecf20Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = fl6->daddr;
24288c2ecf20Sopenharmony_ci			hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
24298c2ecf20Sopenharmony_ci			hash_keys.basic.ip_proto = fl6->flowi6_proto;
24308c2ecf20Sopenharmony_ci		}
24318c2ecf20Sopenharmony_ci		break;
24328c2ecf20Sopenharmony_ci	}
24338c2ecf20Sopenharmony_ci	mhash = flow_hash_from_keys(&hash_keys);
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	return mhash >> 1;
24368c2ecf20Sopenharmony_ci}
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci/* Called with rcu held */
24398c2ecf20Sopenharmony_civoid ip6_route_input(struct sk_buff *skb)
24408c2ecf20Sopenharmony_ci{
24418c2ecf20Sopenharmony_ci	const struct ipv6hdr *iph = ipv6_hdr(skb);
24428c2ecf20Sopenharmony_ci	struct net *net = dev_net(skb->dev);
24438c2ecf20Sopenharmony_ci	int flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_DST_NOREF;
24448c2ecf20Sopenharmony_ci	struct ip_tunnel_info *tun_info;
24458c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
24468c2ecf20Sopenharmony_ci		.flowi6_iif = skb->dev->ifindex,
24478c2ecf20Sopenharmony_ci		.daddr = iph->daddr,
24488c2ecf20Sopenharmony_ci		.saddr = iph->saddr,
24498c2ecf20Sopenharmony_ci		.flowlabel = ip6_flowinfo(iph),
24508c2ecf20Sopenharmony_ci		.flowi6_mark = skb->mark,
24518c2ecf20Sopenharmony_ci		.flowi6_proto = iph->nexthdr,
24528c2ecf20Sopenharmony_ci	};
24538c2ecf20Sopenharmony_ci	struct flow_keys *flkeys = NULL, _flkeys;
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	tun_info = skb_tunnel_info(skb);
24568c2ecf20Sopenharmony_ci	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
24578c2ecf20Sopenharmony_ci		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
24608c2ecf20Sopenharmony_ci		flkeys = &_flkeys;
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
24638c2ecf20Sopenharmony_ci		fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
24648c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
24658c2ecf20Sopenharmony_ci	skb_dst_set_noref(skb, ip6_route_input_lookup(net, skb->dev,
24668c2ecf20Sopenharmony_ci						      &fl6, skb, flags));
24678c2ecf20Sopenharmony_ci}
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_output(struct net *net,
24708c2ecf20Sopenharmony_ci					     struct fib6_table *table,
24718c2ecf20Sopenharmony_ci					     struct flowi6 *fl6,
24728c2ecf20Sopenharmony_ci					     const struct sk_buff *skb,
24738c2ecf20Sopenharmony_ci					     int flags)
24748c2ecf20Sopenharmony_ci{
24758c2ecf20Sopenharmony_ci	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
24768c2ecf20Sopenharmony_ci}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_output_flags_noref(struct net *net,
24798c2ecf20Sopenharmony_ci					       const struct sock *sk,
24808c2ecf20Sopenharmony_ci					       struct flowi6 *fl6, int flags)
24818c2ecf20Sopenharmony_ci{
24828c2ecf20Sopenharmony_ci	bool any_src;
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_ci	if (ipv6_addr_type(&fl6->daddr) &
24858c2ecf20Sopenharmony_ci	    (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
24868c2ecf20Sopenharmony_ci		struct dst_entry *dst;
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci		/* This function does not take refcnt on the dst */
24898c2ecf20Sopenharmony_ci		dst = l3mdev_link_scope_lookup(net, fl6);
24908c2ecf20Sopenharmony_ci		if (dst)
24918c2ecf20Sopenharmony_ci			return dst;
24928c2ecf20Sopenharmony_ci	}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	fl6->flowi6_iif = LOOPBACK_IFINDEX;
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci	flags |= RT6_LOOKUP_F_DST_NOREF;
24978c2ecf20Sopenharmony_ci	any_src = ipv6_addr_any(&fl6->saddr);
24988c2ecf20Sopenharmony_ci	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
24998c2ecf20Sopenharmony_ci	    (fl6->flowi6_oif && any_src))
25008c2ecf20Sopenharmony_ci		flags |= RT6_LOOKUP_F_IFACE;
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	if (!any_src)
25038c2ecf20Sopenharmony_ci		flags |= RT6_LOOKUP_F_HAS_SADDR;
25048c2ecf20Sopenharmony_ci	else if (sk)
25058c2ecf20Sopenharmony_ci		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci	return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
25088c2ecf20Sopenharmony_ci}
25098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_output_flags_noref);
25108c2ecf20Sopenharmony_ci
25118c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_output_flags(struct net *net,
25128c2ecf20Sopenharmony_ci					 const struct sock *sk,
25138c2ecf20Sopenharmony_ci					 struct flowi6 *fl6,
25148c2ecf20Sopenharmony_ci					 int flags)
25158c2ecf20Sopenharmony_ci{
25168c2ecf20Sopenharmony_ci        struct dst_entry *dst;
25178c2ecf20Sopenharmony_ci        struct rt6_info *rt6;
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci        rcu_read_lock();
25208c2ecf20Sopenharmony_ci        dst = ip6_route_output_flags_noref(net, sk, fl6, flags);
25218c2ecf20Sopenharmony_ci        rt6 = (struct rt6_info *)dst;
25228c2ecf20Sopenharmony_ci        /* For dst cached in uncached_list, refcnt is already taken. */
25238c2ecf20Sopenharmony_ci        if (list_empty(&rt6->rt6i_uncached) && !dst_hold_safe(dst)) {
25248c2ecf20Sopenharmony_ci                dst = &net->ipv6.ip6_null_entry->dst;
25258c2ecf20Sopenharmony_ci                dst_hold(dst);
25268c2ecf20Sopenharmony_ci        }
25278c2ecf20Sopenharmony_ci        rcu_read_unlock();
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci        return dst;
25308c2ecf20Sopenharmony_ci}
25318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_output_flags);
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_cistruct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
25348c2ecf20Sopenharmony_ci{
25358c2ecf20Sopenharmony_ci	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
25368c2ecf20Sopenharmony_ci	struct net_device *loopback_dev = net->loopback_dev;
25378c2ecf20Sopenharmony_ci	struct dst_entry *new = NULL;
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
25408c2ecf20Sopenharmony_ci		       DST_OBSOLETE_DEAD, 0);
25418c2ecf20Sopenharmony_ci	if (rt) {
25428c2ecf20Sopenharmony_ci		rt6_info_init(rt);
25438c2ecf20Sopenharmony_ci		atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_ci		new = &rt->dst;
25468c2ecf20Sopenharmony_ci		new->__use = 1;
25478c2ecf20Sopenharmony_ci		new->input = dst_discard;
25488c2ecf20Sopenharmony_ci		new->output = dst_discard_out;
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci		dst_copy_metrics(new, &ort->dst);
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci		rt->rt6i_idev = in6_dev_get(loopback_dev);
25538c2ecf20Sopenharmony_ci		rt->rt6i_gateway = ort->rt6i_gateway;
25548c2ecf20Sopenharmony_ci		rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
25578c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
25588c2ecf20Sopenharmony_ci		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
25598c2ecf20Sopenharmony_ci#endif
25608c2ecf20Sopenharmony_ci	}
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci	dst_release(dst_orig);
25638c2ecf20Sopenharmony_ci	return new ? new : ERR_PTR(-ENOMEM);
25648c2ecf20Sopenharmony_ci}
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci/*
25678c2ecf20Sopenharmony_ci *	Destination cache support functions
25688c2ecf20Sopenharmony_ci */
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_cistatic bool fib6_check(struct fib6_info *f6i, u32 cookie)
25718c2ecf20Sopenharmony_ci{
25728c2ecf20Sopenharmony_ci	u32 rt_cookie = 0;
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci	if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
25758c2ecf20Sopenharmony_ci		return false;
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci	if (fib6_check_expired(f6i))
25788c2ecf20Sopenharmony_ci		return false;
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_ci	return true;
25818c2ecf20Sopenharmony_ci}
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_cistatic struct dst_entry *rt6_check(struct rt6_info *rt,
25848c2ecf20Sopenharmony_ci				   struct fib6_info *from,
25858c2ecf20Sopenharmony_ci				   u32 cookie)
25868c2ecf20Sopenharmony_ci{
25878c2ecf20Sopenharmony_ci	u32 rt_cookie = 0;
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_ci	if (!from || !fib6_get_cookie_safe(from, &rt_cookie) ||
25908c2ecf20Sopenharmony_ci	    rt_cookie != cookie)
25918c2ecf20Sopenharmony_ci		return NULL;
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci	if (rt6_check_expired(rt))
25948c2ecf20Sopenharmony_ci		return NULL;
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci	return &rt->dst;
25978c2ecf20Sopenharmony_ci}
25988c2ecf20Sopenharmony_ci
25998c2ecf20Sopenharmony_cistatic struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
26008c2ecf20Sopenharmony_ci					    struct fib6_info *from,
26018c2ecf20Sopenharmony_ci					    u32 cookie)
26028c2ecf20Sopenharmony_ci{
26038c2ecf20Sopenharmony_ci	if (!__rt6_check_expired(rt) &&
26048c2ecf20Sopenharmony_ci	    rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
26058c2ecf20Sopenharmony_ci	    fib6_check(from, cookie))
26068c2ecf20Sopenharmony_ci		return &rt->dst;
26078c2ecf20Sopenharmony_ci	else
26088c2ecf20Sopenharmony_ci		return NULL;
26098c2ecf20Sopenharmony_ci}
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_cistatic struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
26128c2ecf20Sopenharmony_ci{
26138c2ecf20Sopenharmony_ci	struct dst_entry *dst_ret;
26148c2ecf20Sopenharmony_ci	struct fib6_info *from;
26158c2ecf20Sopenharmony_ci	struct rt6_info *rt;
26168c2ecf20Sopenharmony_ci
26178c2ecf20Sopenharmony_ci	rt = container_of(dst, struct rt6_info, dst);
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	if (rt->sernum)
26208c2ecf20Sopenharmony_ci		return rt6_is_valid(rt) ? dst : NULL;
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	rcu_read_lock();
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	/* All IPV6 dsts are created with ->obsolete set to the value
26258c2ecf20Sopenharmony_ci	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
26268c2ecf20Sopenharmony_ci	 * into this function always.
26278c2ecf20Sopenharmony_ci	 */
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci	from = rcu_dereference(rt->from);
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	if (from && (rt->rt6i_flags & RTF_PCPU ||
26328c2ecf20Sopenharmony_ci	    unlikely(!list_empty(&rt->rt6i_uncached))))
26338c2ecf20Sopenharmony_ci		dst_ret = rt6_dst_from_check(rt, from, cookie);
26348c2ecf20Sopenharmony_ci	else
26358c2ecf20Sopenharmony_ci		dst_ret = rt6_check(rt, from, cookie);
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	rcu_read_unlock();
26388c2ecf20Sopenharmony_ci
26398c2ecf20Sopenharmony_ci	return dst_ret;
26408c2ecf20Sopenharmony_ci}
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_cistatic void ip6_negative_advice(struct sock *sk,
26438c2ecf20Sopenharmony_ci				struct dst_entry *dst)
26448c2ecf20Sopenharmony_ci{
26458c2ecf20Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *) dst;
26468c2ecf20Sopenharmony_ci
26478c2ecf20Sopenharmony_ci	if (rt->rt6i_flags & RTF_CACHE) {
26488c2ecf20Sopenharmony_ci		rcu_read_lock();
26498c2ecf20Sopenharmony_ci		if (rt6_check_expired(rt)) {
26508c2ecf20Sopenharmony_ci			/* counteract the dst_release() in sk_dst_reset() */
26518c2ecf20Sopenharmony_ci			dst_hold(dst);
26528c2ecf20Sopenharmony_ci			sk_dst_reset(sk);
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci			rt6_remove_exception_rt(rt);
26558c2ecf20Sopenharmony_ci		}
26568c2ecf20Sopenharmony_ci		rcu_read_unlock();
26578c2ecf20Sopenharmony_ci		return;
26588c2ecf20Sopenharmony_ci	}
26598c2ecf20Sopenharmony_ci	sk_dst_reset(sk);
26608c2ecf20Sopenharmony_ci}
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_cistatic void ip6_link_failure(struct sk_buff *skb)
26638c2ecf20Sopenharmony_ci{
26648c2ecf20Sopenharmony_ci	struct rt6_info *rt;
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
26678c2ecf20Sopenharmony_ci
26688c2ecf20Sopenharmony_ci	rt = (struct rt6_info *) skb_dst(skb);
26698c2ecf20Sopenharmony_ci	if (rt) {
26708c2ecf20Sopenharmony_ci		rcu_read_lock();
26718c2ecf20Sopenharmony_ci		if (rt->rt6i_flags & RTF_CACHE) {
26728c2ecf20Sopenharmony_ci			rt6_remove_exception_rt(rt);
26738c2ecf20Sopenharmony_ci		} else {
26748c2ecf20Sopenharmony_ci			struct fib6_info *from;
26758c2ecf20Sopenharmony_ci			struct fib6_node *fn;
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci			from = rcu_dereference(rt->from);
26788c2ecf20Sopenharmony_ci			if (from) {
26798c2ecf20Sopenharmony_ci				fn = rcu_dereference(from->fib6_node);
26808c2ecf20Sopenharmony_ci				if (fn && (rt->rt6i_flags & RTF_DEFAULT))
26818c2ecf20Sopenharmony_ci					WRITE_ONCE(fn->fn_sernum, -1);
26828c2ecf20Sopenharmony_ci			}
26838c2ecf20Sopenharmony_ci		}
26848c2ecf20Sopenharmony_ci		rcu_read_unlock();
26858c2ecf20Sopenharmony_ci	}
26868c2ecf20Sopenharmony_ci}
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_cistatic void rt6_update_expires(struct rt6_info *rt0, int timeout)
26898c2ecf20Sopenharmony_ci{
26908c2ecf20Sopenharmony_ci	if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
26918c2ecf20Sopenharmony_ci		struct fib6_info *from;
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ci		rcu_read_lock();
26948c2ecf20Sopenharmony_ci		from = rcu_dereference(rt0->from);
26958c2ecf20Sopenharmony_ci		if (from)
26968c2ecf20Sopenharmony_ci			rt0->dst.expires = from->expires;
26978c2ecf20Sopenharmony_ci		rcu_read_unlock();
26988c2ecf20Sopenharmony_ci	}
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	dst_set_expires(&rt0->dst, timeout);
27018c2ecf20Sopenharmony_ci	rt0->rt6i_flags |= RTF_EXPIRES;
27028c2ecf20Sopenharmony_ci}
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_cistatic void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
27058c2ecf20Sopenharmony_ci{
27068c2ecf20Sopenharmony_ci	struct net *net = dev_net(rt->dst.dev);
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	dst_metric_set(&rt->dst, RTAX_MTU, mtu);
27098c2ecf20Sopenharmony_ci	rt->rt6i_flags |= RTF_MODIFIED;
27108c2ecf20Sopenharmony_ci	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
27118c2ecf20Sopenharmony_ci}
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_cistatic bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
27148c2ecf20Sopenharmony_ci{
27158c2ecf20Sopenharmony_ci	return !(rt->rt6i_flags & RTF_CACHE) &&
27168c2ecf20Sopenharmony_ci		(rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
27178c2ecf20Sopenharmony_ci}
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_cistatic void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
27208c2ecf20Sopenharmony_ci				 const struct ipv6hdr *iph, u32 mtu,
27218c2ecf20Sopenharmony_ci				 bool confirm_neigh)
27228c2ecf20Sopenharmony_ci{
27238c2ecf20Sopenharmony_ci	const struct in6_addr *daddr, *saddr;
27248c2ecf20Sopenharmony_ci	struct rt6_info *rt6 = (struct rt6_info *)dst;
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci	/* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU)
27278c2ecf20Sopenharmony_ci	 * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it.
27288c2ecf20Sopenharmony_ci	 * [see also comment in rt6_mtu_change_route()]
27298c2ecf20Sopenharmony_ci	 */
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	if (iph) {
27328c2ecf20Sopenharmony_ci		daddr = &iph->daddr;
27338c2ecf20Sopenharmony_ci		saddr = &iph->saddr;
27348c2ecf20Sopenharmony_ci	} else if (sk) {
27358c2ecf20Sopenharmony_ci		daddr = &sk->sk_v6_daddr;
27368c2ecf20Sopenharmony_ci		saddr = &inet6_sk(sk)->saddr;
27378c2ecf20Sopenharmony_ci	} else {
27388c2ecf20Sopenharmony_ci		daddr = NULL;
27398c2ecf20Sopenharmony_ci		saddr = NULL;
27408c2ecf20Sopenharmony_ci	}
27418c2ecf20Sopenharmony_ci
27428c2ecf20Sopenharmony_ci	if (confirm_neigh)
27438c2ecf20Sopenharmony_ci		dst_confirm_neigh(dst, daddr);
27448c2ecf20Sopenharmony_ci
27458c2ecf20Sopenharmony_ci	if (mtu < IPV6_MIN_MTU)
27468c2ecf20Sopenharmony_ci		return;
27478c2ecf20Sopenharmony_ci	if (mtu >= dst_mtu(dst))
27488c2ecf20Sopenharmony_ci		return;
27498c2ecf20Sopenharmony_ci
27508c2ecf20Sopenharmony_ci	if (!rt6_cache_allowed_for_pmtu(rt6)) {
27518c2ecf20Sopenharmony_ci		rt6_do_update_pmtu(rt6, mtu);
27528c2ecf20Sopenharmony_ci		/* update rt6_ex->stamp for cache */
27538c2ecf20Sopenharmony_ci		if (rt6->rt6i_flags & RTF_CACHE)
27548c2ecf20Sopenharmony_ci			rt6_update_exception_stamp_rt(rt6);
27558c2ecf20Sopenharmony_ci	} else if (daddr) {
27568c2ecf20Sopenharmony_ci		struct fib6_result res = {};
27578c2ecf20Sopenharmony_ci		struct rt6_info *nrt6;
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci		rcu_read_lock();
27608c2ecf20Sopenharmony_ci		res.f6i = rcu_dereference(rt6->from);
27618c2ecf20Sopenharmony_ci		if (!res.f6i)
27628c2ecf20Sopenharmony_ci			goto out_unlock;
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci		res.fib6_flags = res.f6i->fib6_flags;
27658c2ecf20Sopenharmony_ci		res.fib6_type = res.f6i->fib6_type;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci		if (res.f6i->nh) {
27688c2ecf20Sopenharmony_ci			struct fib6_nh_match_arg arg = {
27698c2ecf20Sopenharmony_ci				.dev = dst->dev,
27708c2ecf20Sopenharmony_ci				.gw = &rt6->rt6i_gateway,
27718c2ecf20Sopenharmony_ci			};
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci			nexthop_for_each_fib6_nh(res.f6i->nh,
27748c2ecf20Sopenharmony_ci						 fib6_nh_find_match, &arg);
27758c2ecf20Sopenharmony_ci
27768c2ecf20Sopenharmony_ci			/* fib6_info uses a nexthop that does not have fib6_nh
27778c2ecf20Sopenharmony_ci			 * using the dst->dev + gw. Should be impossible.
27788c2ecf20Sopenharmony_ci			 */
27798c2ecf20Sopenharmony_ci			if (!arg.match)
27808c2ecf20Sopenharmony_ci				goto out_unlock;
27818c2ecf20Sopenharmony_ci
27828c2ecf20Sopenharmony_ci			res.nh = arg.match;
27838c2ecf20Sopenharmony_ci		} else {
27848c2ecf20Sopenharmony_ci			res.nh = res.f6i->fib6_nh;
27858c2ecf20Sopenharmony_ci		}
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_ci		nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
27888c2ecf20Sopenharmony_ci		if (nrt6) {
27898c2ecf20Sopenharmony_ci			rt6_do_update_pmtu(nrt6, mtu);
27908c2ecf20Sopenharmony_ci			if (rt6_insert_exception(nrt6, &res))
27918c2ecf20Sopenharmony_ci				dst_release_immediate(&nrt6->dst);
27928c2ecf20Sopenharmony_ci		}
27938c2ecf20Sopenharmony_ciout_unlock:
27948c2ecf20Sopenharmony_ci		rcu_read_unlock();
27958c2ecf20Sopenharmony_ci	}
27968c2ecf20Sopenharmony_ci}
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_cistatic void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
27998c2ecf20Sopenharmony_ci			       struct sk_buff *skb, u32 mtu,
28008c2ecf20Sopenharmony_ci			       bool confirm_neigh)
28018c2ecf20Sopenharmony_ci{
28028c2ecf20Sopenharmony_ci	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu,
28038c2ecf20Sopenharmony_ci			     confirm_neigh);
28048c2ecf20Sopenharmony_ci}
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_civoid ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
28078c2ecf20Sopenharmony_ci		     int oif, u32 mark, kuid_t uid)
28088c2ecf20Sopenharmony_ci{
28098c2ecf20Sopenharmony_ci	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
28108c2ecf20Sopenharmony_ci	struct dst_entry *dst;
28118c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
28128c2ecf20Sopenharmony_ci		.flowi6_oif = oif,
28138c2ecf20Sopenharmony_ci		.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
28148c2ecf20Sopenharmony_ci		.daddr = iph->daddr,
28158c2ecf20Sopenharmony_ci		.saddr = iph->saddr,
28168c2ecf20Sopenharmony_ci		.flowlabel = ip6_flowinfo(iph),
28178c2ecf20Sopenharmony_ci		.flowi6_uid = uid,
28188c2ecf20Sopenharmony_ci	};
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci	dst = ip6_route_output(net, NULL, &fl6);
28218c2ecf20Sopenharmony_ci	if (!dst->error)
28228c2ecf20Sopenharmony_ci		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true);
28238c2ecf20Sopenharmony_ci	dst_release(dst);
28248c2ecf20Sopenharmony_ci}
28258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_update_pmtu);
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_civoid ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
28288c2ecf20Sopenharmony_ci{
28298c2ecf20Sopenharmony_ci	int oif = sk->sk_bound_dev_if;
28308c2ecf20Sopenharmony_ci	struct dst_entry *dst;
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	if (!oif && skb->dev)
28338c2ecf20Sopenharmony_ci		oif = l3mdev_master_ifindex(skb->dev);
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_ci	dst = __sk_dst_get(sk);
28388c2ecf20Sopenharmony_ci	if (!dst || !dst->obsolete ||
28398c2ecf20Sopenharmony_ci	    dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
28408c2ecf20Sopenharmony_ci		return;
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
28438c2ecf20Sopenharmony_ci	if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
28448c2ecf20Sopenharmony_ci		ip6_datagram_dst_update(sk, false);
28458c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
28468c2ecf20Sopenharmony_ci}
28478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
28488c2ecf20Sopenharmony_ci
28498c2ecf20Sopenharmony_civoid ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
28508c2ecf20Sopenharmony_ci			   const struct flowi6 *fl6)
28518c2ecf20Sopenharmony_ci{
28528c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
28538c2ecf20Sopenharmony_ci	struct ipv6_pinfo *np = inet6_sk(sk);
28548c2ecf20Sopenharmony_ci#endif
28558c2ecf20Sopenharmony_ci
28568c2ecf20Sopenharmony_ci	ip6_dst_store(sk, dst,
28578c2ecf20Sopenharmony_ci		      ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
28588c2ecf20Sopenharmony_ci		      &sk->sk_v6_daddr : NULL,
28598c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
28608c2ecf20Sopenharmony_ci		      ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
28618c2ecf20Sopenharmony_ci		      &np->saddr :
28628c2ecf20Sopenharmony_ci#endif
28638c2ecf20Sopenharmony_ci		      NULL);
28648c2ecf20Sopenharmony_ci}
28658c2ecf20Sopenharmony_ci
28668c2ecf20Sopenharmony_cistatic bool ip6_redirect_nh_match(const struct fib6_result *res,
28678c2ecf20Sopenharmony_ci				  struct flowi6 *fl6,
28688c2ecf20Sopenharmony_ci				  const struct in6_addr *gw,
28698c2ecf20Sopenharmony_ci				  struct rt6_info **ret)
28708c2ecf20Sopenharmony_ci{
28718c2ecf20Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
28748c2ecf20Sopenharmony_ci	    fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
28758c2ecf20Sopenharmony_ci		return false;
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci	/* rt_cache's gateway might be different from its 'parent'
28788c2ecf20Sopenharmony_ci	 * in the case of an ip redirect.
28798c2ecf20Sopenharmony_ci	 * So we keep searching in the exception table if the gateway
28808c2ecf20Sopenharmony_ci	 * is different.
28818c2ecf20Sopenharmony_ci	 */
28828c2ecf20Sopenharmony_ci	if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
28838c2ecf20Sopenharmony_ci		struct rt6_info *rt_cache;
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci		rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
28868c2ecf20Sopenharmony_ci		if (rt_cache &&
28878c2ecf20Sopenharmony_ci		    ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
28888c2ecf20Sopenharmony_ci			*ret = rt_cache;
28898c2ecf20Sopenharmony_ci			return true;
28908c2ecf20Sopenharmony_ci		}
28918c2ecf20Sopenharmony_ci		return false;
28928c2ecf20Sopenharmony_ci	}
28938c2ecf20Sopenharmony_ci	return true;
28948c2ecf20Sopenharmony_ci}
28958c2ecf20Sopenharmony_ci
28968c2ecf20Sopenharmony_cistruct fib6_nh_rd_arg {
28978c2ecf20Sopenharmony_ci	struct fib6_result	*res;
28988c2ecf20Sopenharmony_ci	struct flowi6		*fl6;
28998c2ecf20Sopenharmony_ci	const struct in6_addr	*gw;
29008c2ecf20Sopenharmony_ci	struct rt6_info		**ret;
29018c2ecf20Sopenharmony_ci};
29028c2ecf20Sopenharmony_ci
29038c2ecf20Sopenharmony_cistatic int fib6_nh_redirect_match(struct fib6_nh *nh, void *_arg)
29048c2ecf20Sopenharmony_ci{
29058c2ecf20Sopenharmony_ci	struct fib6_nh_rd_arg *arg = _arg;
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci	arg->res->nh = nh;
29088c2ecf20Sopenharmony_ci	return ip6_redirect_nh_match(arg->res, arg->fl6, arg->gw, arg->ret);
29098c2ecf20Sopenharmony_ci}
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci/* Handle redirects */
29128c2ecf20Sopenharmony_cistruct ip6rd_flowi {
29138c2ecf20Sopenharmony_ci	struct flowi6 fl6;
29148c2ecf20Sopenharmony_ci	struct in6_addr gateway;
29158c2ecf20Sopenharmony_ci};
29168c2ecf20Sopenharmony_ci
29178c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net,
29188c2ecf20Sopenharmony_ci					     struct fib6_table *table,
29198c2ecf20Sopenharmony_ci					     struct flowi6 *fl6,
29208c2ecf20Sopenharmony_ci					     const struct sk_buff *skb,
29218c2ecf20Sopenharmony_ci					     int flags)
29228c2ecf20Sopenharmony_ci{
29238c2ecf20Sopenharmony_ci	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
29248c2ecf20Sopenharmony_ci	struct rt6_info *ret = NULL;
29258c2ecf20Sopenharmony_ci	struct fib6_result res = {};
29268c2ecf20Sopenharmony_ci	struct fib6_nh_rd_arg arg = {
29278c2ecf20Sopenharmony_ci		.res = &res,
29288c2ecf20Sopenharmony_ci		.fl6 = fl6,
29298c2ecf20Sopenharmony_ci		.gw  = &rdfl->gateway,
29308c2ecf20Sopenharmony_ci		.ret = &ret
29318c2ecf20Sopenharmony_ci	};
29328c2ecf20Sopenharmony_ci	struct fib6_info *rt;
29338c2ecf20Sopenharmony_ci	struct fib6_node *fn;
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci	/* l3mdev_update_flow overrides oif if the device is enslaved; in
29368c2ecf20Sopenharmony_ci	 * this case we must match on the real ingress device, so reset it
29378c2ecf20Sopenharmony_ci	 */
29388c2ecf20Sopenharmony_ci	if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
29398c2ecf20Sopenharmony_ci		fl6->flowi6_oif = skb->dev->ifindex;
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	/* Get the "current" route for this destination and
29428c2ecf20Sopenharmony_ci	 * check if the redirect has come from appropriate router.
29438c2ecf20Sopenharmony_ci	 *
29448c2ecf20Sopenharmony_ci	 * RFC 4861 specifies that redirects should only be
29458c2ecf20Sopenharmony_ci	 * accepted if they come from the nexthop to the target.
29468c2ecf20Sopenharmony_ci	 * Due to the way the routes are chosen, this notion
29478c2ecf20Sopenharmony_ci	 * is a bit fuzzy and one might need to check all possible
29488c2ecf20Sopenharmony_ci	 * routes.
29498c2ecf20Sopenharmony_ci	 */
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci	rcu_read_lock();
29528c2ecf20Sopenharmony_ci	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
29538c2ecf20Sopenharmony_cirestart:
29548c2ecf20Sopenharmony_ci	for_each_fib6_node_rt_rcu(fn) {
29558c2ecf20Sopenharmony_ci		res.f6i = rt;
29568c2ecf20Sopenharmony_ci		if (fib6_check_expired(rt))
29578c2ecf20Sopenharmony_ci			continue;
29588c2ecf20Sopenharmony_ci		if (rt->fib6_flags & RTF_REJECT)
29598c2ecf20Sopenharmony_ci			break;
29608c2ecf20Sopenharmony_ci		if (unlikely(rt->nh)) {
29618c2ecf20Sopenharmony_ci			if (nexthop_is_blackhole(rt->nh))
29628c2ecf20Sopenharmony_ci				continue;
29638c2ecf20Sopenharmony_ci			/* on match, res->nh is filled in and potentially ret */
29648c2ecf20Sopenharmony_ci			if (nexthop_for_each_fib6_nh(rt->nh,
29658c2ecf20Sopenharmony_ci						     fib6_nh_redirect_match,
29668c2ecf20Sopenharmony_ci						     &arg))
29678c2ecf20Sopenharmony_ci				goto out;
29688c2ecf20Sopenharmony_ci		} else {
29698c2ecf20Sopenharmony_ci			res.nh = rt->fib6_nh;
29708c2ecf20Sopenharmony_ci			if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway,
29718c2ecf20Sopenharmony_ci						  &ret))
29728c2ecf20Sopenharmony_ci				goto out;
29738c2ecf20Sopenharmony_ci		}
29748c2ecf20Sopenharmony_ci	}
29758c2ecf20Sopenharmony_ci
29768c2ecf20Sopenharmony_ci	if (!rt)
29778c2ecf20Sopenharmony_ci		rt = net->ipv6.fib6_null_entry;
29788c2ecf20Sopenharmony_ci	else if (rt->fib6_flags & RTF_REJECT) {
29798c2ecf20Sopenharmony_ci		ret = net->ipv6.ip6_null_entry;
29808c2ecf20Sopenharmony_ci		goto out;
29818c2ecf20Sopenharmony_ci	}
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry) {
29848c2ecf20Sopenharmony_ci		fn = fib6_backtrack(fn, &fl6->saddr);
29858c2ecf20Sopenharmony_ci		if (fn)
29868c2ecf20Sopenharmony_ci			goto restart;
29878c2ecf20Sopenharmony_ci	}
29888c2ecf20Sopenharmony_ci
29898c2ecf20Sopenharmony_ci	res.f6i = rt;
29908c2ecf20Sopenharmony_ci	res.nh = rt->fib6_nh;
29918c2ecf20Sopenharmony_ciout:
29928c2ecf20Sopenharmony_ci	if (ret) {
29938c2ecf20Sopenharmony_ci		ip6_hold_safe(net, &ret);
29948c2ecf20Sopenharmony_ci	} else {
29958c2ecf20Sopenharmony_ci		res.fib6_flags = res.f6i->fib6_flags;
29968c2ecf20Sopenharmony_ci		res.fib6_type = res.f6i->fib6_type;
29978c2ecf20Sopenharmony_ci		ret = ip6_create_rt_rcu(&res);
29988c2ecf20Sopenharmony_ci	}
29998c2ecf20Sopenharmony_ci
30008c2ecf20Sopenharmony_ci	rcu_read_unlock();
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_ci	trace_fib6_table_lookup(net, &res, table, fl6);
30038c2ecf20Sopenharmony_ci	return ret;
30048c2ecf20Sopenharmony_ci};
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_cistatic struct dst_entry *ip6_route_redirect(struct net *net,
30078c2ecf20Sopenharmony_ci					    const struct flowi6 *fl6,
30088c2ecf20Sopenharmony_ci					    const struct sk_buff *skb,
30098c2ecf20Sopenharmony_ci					    const struct in6_addr *gateway)
30108c2ecf20Sopenharmony_ci{
30118c2ecf20Sopenharmony_ci	int flags = RT6_LOOKUP_F_HAS_SADDR;
30128c2ecf20Sopenharmony_ci	struct ip6rd_flowi rdfl;
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci	rdfl.fl6 = *fl6;
30158c2ecf20Sopenharmony_ci	rdfl.gateway = *gateway;
30168c2ecf20Sopenharmony_ci
30178c2ecf20Sopenharmony_ci	return fib6_rule_lookup(net, &rdfl.fl6, skb,
30188c2ecf20Sopenharmony_ci				flags, __ip6_route_redirect);
30198c2ecf20Sopenharmony_ci}
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_civoid ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
30228c2ecf20Sopenharmony_ci		  kuid_t uid)
30238c2ecf20Sopenharmony_ci{
30248c2ecf20Sopenharmony_ci	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
30258c2ecf20Sopenharmony_ci	struct dst_entry *dst;
30268c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
30278c2ecf20Sopenharmony_ci		.flowi6_iif = LOOPBACK_IFINDEX,
30288c2ecf20Sopenharmony_ci		.flowi6_oif = oif,
30298c2ecf20Sopenharmony_ci		.flowi6_mark = mark,
30308c2ecf20Sopenharmony_ci		.daddr = iph->daddr,
30318c2ecf20Sopenharmony_ci		.saddr = iph->saddr,
30328c2ecf20Sopenharmony_ci		.flowlabel = ip6_flowinfo(iph),
30338c2ecf20Sopenharmony_ci		.flowi6_uid = uid,
30348c2ecf20Sopenharmony_ci	};
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_ci	dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
30378c2ecf20Sopenharmony_ci	rt6_do_redirect(dst, NULL, skb);
30388c2ecf20Sopenharmony_ci	dst_release(dst);
30398c2ecf20Sopenharmony_ci}
30408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_redirect);
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_civoid ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
30438c2ecf20Sopenharmony_ci{
30448c2ecf20Sopenharmony_ci	const struct ipv6hdr *iph = ipv6_hdr(skb);
30458c2ecf20Sopenharmony_ci	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
30468c2ecf20Sopenharmony_ci	struct dst_entry *dst;
30478c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
30488c2ecf20Sopenharmony_ci		.flowi6_iif = LOOPBACK_IFINDEX,
30498c2ecf20Sopenharmony_ci		.flowi6_oif = oif,
30508c2ecf20Sopenharmony_ci		.daddr = msg->dest,
30518c2ecf20Sopenharmony_ci		.saddr = iph->daddr,
30528c2ecf20Sopenharmony_ci		.flowi6_uid = sock_net_uid(net, NULL),
30538c2ecf20Sopenharmony_ci	};
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ci	dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
30568c2ecf20Sopenharmony_ci	rt6_do_redirect(dst, NULL, skb);
30578c2ecf20Sopenharmony_ci	dst_release(dst);
30588c2ecf20Sopenharmony_ci}
30598c2ecf20Sopenharmony_ci
30608c2ecf20Sopenharmony_civoid ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
30618c2ecf20Sopenharmony_ci{
30628c2ecf20Sopenharmony_ci	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
30638c2ecf20Sopenharmony_ci		     sk->sk_uid);
30648c2ecf20Sopenharmony_ci}
30658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_redirect);
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_cistatic unsigned int ip6_default_advmss(const struct dst_entry *dst)
30688c2ecf20Sopenharmony_ci{
30698c2ecf20Sopenharmony_ci	struct net_device *dev = dst->dev;
30708c2ecf20Sopenharmony_ci	unsigned int mtu = dst_mtu(dst);
30718c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_ci	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
30748c2ecf20Sopenharmony_ci
30758c2ecf20Sopenharmony_ci	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
30768c2ecf20Sopenharmony_ci		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	/*
30798c2ecf20Sopenharmony_ci	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
30808c2ecf20Sopenharmony_ci	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
30818c2ecf20Sopenharmony_ci	 * IPV6_MAXPLEN is also valid and means: "any MSS,
30828c2ecf20Sopenharmony_ci	 * rely only on pmtu discovery"
30838c2ecf20Sopenharmony_ci	 */
30848c2ecf20Sopenharmony_ci	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
30858c2ecf20Sopenharmony_ci		mtu = IPV6_MAXPLEN;
30868c2ecf20Sopenharmony_ci	return mtu;
30878c2ecf20Sopenharmony_ci}
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_cistatic unsigned int ip6_mtu(const struct dst_entry *dst)
30908c2ecf20Sopenharmony_ci{
30918c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
30928c2ecf20Sopenharmony_ci	unsigned int mtu;
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	mtu = dst_metric_raw(dst, RTAX_MTU);
30958c2ecf20Sopenharmony_ci	if (mtu)
30968c2ecf20Sopenharmony_ci		goto out;
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_ci	mtu = IPV6_MIN_MTU;
30998c2ecf20Sopenharmony_ci
31008c2ecf20Sopenharmony_ci	rcu_read_lock();
31018c2ecf20Sopenharmony_ci	idev = __in6_dev_get(dst->dev);
31028c2ecf20Sopenharmony_ci	if (idev)
31038c2ecf20Sopenharmony_ci		mtu = idev->cnf.mtu6;
31048c2ecf20Sopenharmony_ci	rcu_read_unlock();
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ciout:
31078c2ecf20Sopenharmony_ci	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
31088c2ecf20Sopenharmony_ci
31098c2ecf20Sopenharmony_ci	return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
31108c2ecf20Sopenharmony_ci}
31118c2ecf20Sopenharmony_ci
31128c2ecf20Sopenharmony_ci/* MTU selection:
31138c2ecf20Sopenharmony_ci * 1. mtu on route is locked - use it
31148c2ecf20Sopenharmony_ci * 2. mtu from nexthop exception
31158c2ecf20Sopenharmony_ci * 3. mtu from egress device
31168c2ecf20Sopenharmony_ci *
31178c2ecf20Sopenharmony_ci * based on ip6_dst_mtu_forward and exception logic of
31188c2ecf20Sopenharmony_ci * rt6_find_cached_rt; called with rcu_read_lock
31198c2ecf20Sopenharmony_ci */
31208c2ecf20Sopenharmony_ciu32 ip6_mtu_from_fib6(const struct fib6_result *res,
31218c2ecf20Sopenharmony_ci		      const struct in6_addr *daddr,
31228c2ecf20Sopenharmony_ci		      const struct in6_addr *saddr)
31238c2ecf20Sopenharmony_ci{
31248c2ecf20Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
31258c2ecf20Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
31268c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
31278c2ecf20Sopenharmony_ci	struct rt6_info *rt;
31288c2ecf20Sopenharmony_ci	u32 mtu = 0;
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ci	if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
31318c2ecf20Sopenharmony_ci		mtu = f6i->fib6_pmtu;
31328c2ecf20Sopenharmony_ci		if (mtu)
31338c2ecf20Sopenharmony_ci			goto out;
31348c2ecf20Sopenharmony_ci	}
31358c2ecf20Sopenharmony_ci
31368c2ecf20Sopenharmony_ci	rt = rt6_find_cached_rt(res, daddr, saddr);
31378c2ecf20Sopenharmony_ci	if (unlikely(rt)) {
31388c2ecf20Sopenharmony_ci		mtu = dst_metric_raw(&rt->dst, RTAX_MTU);
31398c2ecf20Sopenharmony_ci	} else {
31408c2ecf20Sopenharmony_ci		struct net_device *dev = nh->fib_nh_dev;
31418c2ecf20Sopenharmony_ci
31428c2ecf20Sopenharmony_ci		mtu = IPV6_MIN_MTU;
31438c2ecf20Sopenharmony_ci		idev = __in6_dev_get(dev);
31448c2ecf20Sopenharmony_ci		if (idev && idev->cnf.mtu6 > mtu)
31458c2ecf20Sopenharmony_ci			mtu = idev->cnf.mtu6;
31468c2ecf20Sopenharmony_ci	}
31478c2ecf20Sopenharmony_ci
31488c2ecf20Sopenharmony_ci	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
31498c2ecf20Sopenharmony_ciout:
31508c2ecf20Sopenharmony_ci	return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
31518c2ecf20Sopenharmony_ci}
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_cistruct dst_entry *icmp6_dst_alloc(struct net_device *dev,
31548c2ecf20Sopenharmony_ci				  struct flowi6 *fl6)
31558c2ecf20Sopenharmony_ci{
31568c2ecf20Sopenharmony_ci	struct dst_entry *dst;
31578c2ecf20Sopenharmony_ci	struct rt6_info *rt;
31588c2ecf20Sopenharmony_ci	struct inet6_dev *idev = in6_dev_get(dev);
31598c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_ci	if (unlikely(!idev))
31628c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
31638c2ecf20Sopenharmony_ci
31648c2ecf20Sopenharmony_ci	rt = ip6_dst_alloc(net, dev, 0);
31658c2ecf20Sopenharmony_ci	if (unlikely(!rt)) {
31668c2ecf20Sopenharmony_ci		in6_dev_put(idev);
31678c2ecf20Sopenharmony_ci		dst = ERR_PTR(-ENOMEM);
31688c2ecf20Sopenharmony_ci		goto out;
31698c2ecf20Sopenharmony_ci	}
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_ci	rt->dst.input = ip6_input;
31728c2ecf20Sopenharmony_ci	rt->dst.output  = ip6_output;
31738c2ecf20Sopenharmony_ci	rt->rt6i_gateway  = fl6->daddr;
31748c2ecf20Sopenharmony_ci	rt->rt6i_dst.addr = fl6->daddr;
31758c2ecf20Sopenharmony_ci	rt->rt6i_dst.plen = 128;
31768c2ecf20Sopenharmony_ci	rt->rt6i_idev     = idev;
31778c2ecf20Sopenharmony_ci	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
31788c2ecf20Sopenharmony_ci
31798c2ecf20Sopenharmony_ci	/* Add this dst into uncached_list so that rt6_disable_ip() can
31808c2ecf20Sopenharmony_ci	 * do proper release of the net_device
31818c2ecf20Sopenharmony_ci	 */
31828c2ecf20Sopenharmony_ci	rt6_uncached_list_add(rt);
31838c2ecf20Sopenharmony_ci	atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
31848c2ecf20Sopenharmony_ci
31858c2ecf20Sopenharmony_ci	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
31868c2ecf20Sopenharmony_ci
31878c2ecf20Sopenharmony_ciout:
31888c2ecf20Sopenharmony_ci	return dst;
31898c2ecf20Sopenharmony_ci}
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_cistatic void ip6_dst_gc(struct dst_ops *ops)
31928c2ecf20Sopenharmony_ci{
31938c2ecf20Sopenharmony_ci	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
31948c2ecf20Sopenharmony_ci	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
31958c2ecf20Sopenharmony_ci	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
31968c2ecf20Sopenharmony_ci	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
31978c2ecf20Sopenharmony_ci	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
31988c2ecf20Sopenharmony_ci	unsigned int val;
31998c2ecf20Sopenharmony_ci	int entries;
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci	entries = dst_entries_get_fast(ops);
32028c2ecf20Sopenharmony_ci	if (entries > ops->gc_thresh)
32038c2ecf20Sopenharmony_ci		entries = dst_entries_get_slow(ops);
32048c2ecf20Sopenharmony_ci
32058c2ecf20Sopenharmony_ci	if (time_after(rt_last_gc + rt_min_interval, jiffies))
32068c2ecf20Sopenharmony_ci		goto out;
32078c2ecf20Sopenharmony_ci
32088c2ecf20Sopenharmony_ci	fib6_run_gc(atomic_inc_return(&net->ipv6.ip6_rt_gc_expire), net, true);
32098c2ecf20Sopenharmony_ci	entries = dst_entries_get_slow(ops);
32108c2ecf20Sopenharmony_ci	if (entries < ops->gc_thresh)
32118c2ecf20Sopenharmony_ci		atomic_set(&net->ipv6.ip6_rt_gc_expire, rt_gc_timeout >> 1);
32128c2ecf20Sopenharmony_ciout:
32138c2ecf20Sopenharmony_ci	val = atomic_read(&net->ipv6.ip6_rt_gc_expire);
32148c2ecf20Sopenharmony_ci	atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity));
32158c2ecf20Sopenharmony_ci}
32168c2ecf20Sopenharmony_ci
32178c2ecf20Sopenharmony_cistatic int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg,
32188c2ecf20Sopenharmony_ci			       const struct in6_addr *gw_addr, u32 tbid,
32198c2ecf20Sopenharmony_ci			       int flags, struct fib6_result *res)
32208c2ecf20Sopenharmony_ci{
32218c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
32228c2ecf20Sopenharmony_ci		.flowi6_oif = cfg->fc_ifindex,
32238c2ecf20Sopenharmony_ci		.daddr = *gw_addr,
32248c2ecf20Sopenharmony_ci		.saddr = cfg->fc_prefsrc,
32258c2ecf20Sopenharmony_ci	};
32268c2ecf20Sopenharmony_ci	struct fib6_table *table;
32278c2ecf20Sopenharmony_ci	int err;
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci	table = fib6_get_table(net, tbid);
32308c2ecf20Sopenharmony_ci	if (!table)
32318c2ecf20Sopenharmony_ci		return -EINVAL;
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(&cfg->fc_prefsrc))
32348c2ecf20Sopenharmony_ci		flags |= RT6_LOOKUP_F_HAS_SADDR;
32358c2ecf20Sopenharmony_ci
32368c2ecf20Sopenharmony_ci	flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
32378c2ecf20Sopenharmony_ci
32388c2ecf20Sopenharmony_ci	err = fib6_table_lookup(net, table, cfg->fc_ifindex, &fl6, res, flags);
32398c2ecf20Sopenharmony_ci	if (!err && res->f6i != net->ipv6.fib6_null_entry)
32408c2ecf20Sopenharmony_ci		fib6_select_path(net, res, &fl6, cfg->fc_ifindex,
32418c2ecf20Sopenharmony_ci				 cfg->fc_ifindex != 0, NULL, flags);
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_ci	return err;
32448c2ecf20Sopenharmony_ci}
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_cistatic int ip6_route_check_nh_onlink(struct net *net,
32478c2ecf20Sopenharmony_ci				     struct fib6_config *cfg,
32488c2ecf20Sopenharmony_ci				     const struct net_device *dev,
32498c2ecf20Sopenharmony_ci				     struct netlink_ext_ack *extack)
32508c2ecf20Sopenharmony_ci{
32518c2ecf20Sopenharmony_ci	u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
32528c2ecf20Sopenharmony_ci	const struct in6_addr *gw_addr = &cfg->fc_gateway;
32538c2ecf20Sopenharmony_ci	struct fib6_result res = {};
32548c2ecf20Sopenharmony_ci	int err;
32558c2ecf20Sopenharmony_ci
32568c2ecf20Sopenharmony_ci	err = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0, &res);
32578c2ecf20Sopenharmony_ci	if (!err && !(res.fib6_flags & RTF_REJECT) &&
32588c2ecf20Sopenharmony_ci	    /* ignore match if it is the default route */
32598c2ecf20Sopenharmony_ci	    !ipv6_addr_any(&res.f6i->fib6_dst.addr) &&
32608c2ecf20Sopenharmony_ci	    (res.fib6_type != RTN_UNICAST || dev != res.nh->fib_nh_dev)) {
32618c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
32628c2ecf20Sopenharmony_ci			       "Nexthop has invalid gateway or device mismatch");
32638c2ecf20Sopenharmony_ci		err = -EINVAL;
32648c2ecf20Sopenharmony_ci	}
32658c2ecf20Sopenharmony_ci
32668c2ecf20Sopenharmony_ci	return err;
32678c2ecf20Sopenharmony_ci}
32688c2ecf20Sopenharmony_ci
32698c2ecf20Sopenharmony_cistatic int ip6_route_check_nh(struct net *net,
32708c2ecf20Sopenharmony_ci			      struct fib6_config *cfg,
32718c2ecf20Sopenharmony_ci			      struct net_device **_dev,
32728c2ecf20Sopenharmony_ci			      struct inet6_dev **idev)
32738c2ecf20Sopenharmony_ci{
32748c2ecf20Sopenharmony_ci	const struct in6_addr *gw_addr = &cfg->fc_gateway;
32758c2ecf20Sopenharmony_ci	struct net_device *dev = _dev ? *_dev : NULL;
32768c2ecf20Sopenharmony_ci	int flags = RT6_LOOKUP_F_IFACE;
32778c2ecf20Sopenharmony_ci	struct fib6_result res = {};
32788c2ecf20Sopenharmony_ci	int err = -EHOSTUNREACH;
32798c2ecf20Sopenharmony_ci
32808c2ecf20Sopenharmony_ci	if (cfg->fc_table) {
32818c2ecf20Sopenharmony_ci		err = ip6_nh_lookup_table(net, cfg, gw_addr,
32828c2ecf20Sopenharmony_ci					  cfg->fc_table, flags, &res);
32838c2ecf20Sopenharmony_ci		/* gw_addr can not require a gateway or resolve to a reject
32848c2ecf20Sopenharmony_ci		 * route. If a device is given, it must match the result.
32858c2ecf20Sopenharmony_ci		 */
32868c2ecf20Sopenharmony_ci		if (err || res.fib6_flags & RTF_REJECT ||
32878c2ecf20Sopenharmony_ci		    res.nh->fib_nh_gw_family ||
32888c2ecf20Sopenharmony_ci		    (dev && dev != res.nh->fib_nh_dev))
32898c2ecf20Sopenharmony_ci			err = -EHOSTUNREACH;
32908c2ecf20Sopenharmony_ci	}
32918c2ecf20Sopenharmony_ci
32928c2ecf20Sopenharmony_ci	if (err < 0) {
32938c2ecf20Sopenharmony_ci		struct flowi6 fl6 = {
32948c2ecf20Sopenharmony_ci			.flowi6_oif = cfg->fc_ifindex,
32958c2ecf20Sopenharmony_ci			.daddr = *gw_addr,
32968c2ecf20Sopenharmony_ci		};
32978c2ecf20Sopenharmony_ci
32988c2ecf20Sopenharmony_ci		err = fib6_lookup(net, cfg->fc_ifindex, &fl6, &res, flags);
32998c2ecf20Sopenharmony_ci		if (err || res.fib6_flags & RTF_REJECT ||
33008c2ecf20Sopenharmony_ci		    res.nh->fib_nh_gw_family)
33018c2ecf20Sopenharmony_ci			err = -EHOSTUNREACH;
33028c2ecf20Sopenharmony_ci
33038c2ecf20Sopenharmony_ci		if (err)
33048c2ecf20Sopenharmony_ci			return err;
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci		fib6_select_path(net, &res, &fl6, cfg->fc_ifindex,
33078c2ecf20Sopenharmony_ci				 cfg->fc_ifindex != 0, NULL, flags);
33088c2ecf20Sopenharmony_ci	}
33098c2ecf20Sopenharmony_ci
33108c2ecf20Sopenharmony_ci	err = 0;
33118c2ecf20Sopenharmony_ci	if (dev) {
33128c2ecf20Sopenharmony_ci		if (dev != res.nh->fib_nh_dev)
33138c2ecf20Sopenharmony_ci			err = -EHOSTUNREACH;
33148c2ecf20Sopenharmony_ci	} else {
33158c2ecf20Sopenharmony_ci		*_dev = dev = res.nh->fib_nh_dev;
33168c2ecf20Sopenharmony_ci		dev_hold(dev);
33178c2ecf20Sopenharmony_ci		*idev = in6_dev_get(dev);
33188c2ecf20Sopenharmony_ci	}
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci	return err;
33218c2ecf20Sopenharmony_ci}
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_cistatic int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
33248c2ecf20Sopenharmony_ci			   struct net_device **_dev, struct inet6_dev **idev,
33258c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
33268c2ecf20Sopenharmony_ci{
33278c2ecf20Sopenharmony_ci	const struct in6_addr *gw_addr = &cfg->fc_gateway;
33288c2ecf20Sopenharmony_ci	int gwa_type = ipv6_addr_type(gw_addr);
33298c2ecf20Sopenharmony_ci	bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
33308c2ecf20Sopenharmony_ci	const struct net_device *dev = *_dev;
33318c2ecf20Sopenharmony_ci	bool need_addr_check = !dev;
33328c2ecf20Sopenharmony_ci	int err = -EINVAL;
33338c2ecf20Sopenharmony_ci
33348c2ecf20Sopenharmony_ci	/* if gw_addr is local we will fail to detect this in case
33358c2ecf20Sopenharmony_ci	 * address is still TENTATIVE (DAD in progress). rt6_lookup()
33368c2ecf20Sopenharmony_ci	 * will return already-added prefix route via interface that
33378c2ecf20Sopenharmony_ci	 * prefix route was assigned to, which might be non-loopback.
33388c2ecf20Sopenharmony_ci	 */
33398c2ecf20Sopenharmony_ci	if (dev &&
33408c2ecf20Sopenharmony_ci	    ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
33418c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
33428c2ecf20Sopenharmony_ci		goto out;
33438c2ecf20Sopenharmony_ci	}
33448c2ecf20Sopenharmony_ci
33458c2ecf20Sopenharmony_ci	if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
33468c2ecf20Sopenharmony_ci		/* IPv6 strictly inhibits using not link-local
33478c2ecf20Sopenharmony_ci		 * addresses as nexthop address.
33488c2ecf20Sopenharmony_ci		 * Otherwise, router will not able to send redirects.
33498c2ecf20Sopenharmony_ci		 * It is very good, but in some (rare!) circumstances
33508c2ecf20Sopenharmony_ci		 * (SIT, PtP, NBMA NOARP links) it is handy to allow
33518c2ecf20Sopenharmony_ci		 * some exceptions. --ANK
33528c2ecf20Sopenharmony_ci		 * We allow IPv4-mapped nexthops to support RFC4798-type
33538c2ecf20Sopenharmony_ci		 * addressing
33548c2ecf20Sopenharmony_ci		 */
33558c2ecf20Sopenharmony_ci		if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
33568c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid gateway address");
33578c2ecf20Sopenharmony_ci			goto out;
33588c2ecf20Sopenharmony_ci		}
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_ci		rcu_read_lock();
33618c2ecf20Sopenharmony_ci
33628c2ecf20Sopenharmony_ci		if (cfg->fc_flags & RTNH_F_ONLINK)
33638c2ecf20Sopenharmony_ci			err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
33648c2ecf20Sopenharmony_ci		else
33658c2ecf20Sopenharmony_ci			err = ip6_route_check_nh(net, cfg, _dev, idev);
33668c2ecf20Sopenharmony_ci
33678c2ecf20Sopenharmony_ci		rcu_read_unlock();
33688c2ecf20Sopenharmony_ci
33698c2ecf20Sopenharmony_ci		if (err)
33708c2ecf20Sopenharmony_ci			goto out;
33718c2ecf20Sopenharmony_ci	}
33728c2ecf20Sopenharmony_ci
33738c2ecf20Sopenharmony_ci	/* reload in case device was changed */
33748c2ecf20Sopenharmony_ci	dev = *_dev;
33758c2ecf20Sopenharmony_ci
33768c2ecf20Sopenharmony_ci	err = -EINVAL;
33778c2ecf20Sopenharmony_ci	if (!dev) {
33788c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Egress device not specified");
33798c2ecf20Sopenharmony_ci		goto out;
33808c2ecf20Sopenharmony_ci	} else if (dev->flags & IFF_LOOPBACK) {
33818c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
33828c2ecf20Sopenharmony_ci			       "Egress device can not be loopback device for this route");
33838c2ecf20Sopenharmony_ci		goto out;
33848c2ecf20Sopenharmony_ci	}
33858c2ecf20Sopenharmony_ci
33868c2ecf20Sopenharmony_ci	/* if we did not check gw_addr above, do so now that the
33878c2ecf20Sopenharmony_ci	 * egress device has been resolved.
33888c2ecf20Sopenharmony_ci	 */
33898c2ecf20Sopenharmony_ci	if (need_addr_check &&
33908c2ecf20Sopenharmony_ci	    ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
33918c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
33928c2ecf20Sopenharmony_ci		goto out;
33938c2ecf20Sopenharmony_ci	}
33948c2ecf20Sopenharmony_ci
33958c2ecf20Sopenharmony_ci	err = 0;
33968c2ecf20Sopenharmony_ciout:
33978c2ecf20Sopenharmony_ci	return err;
33988c2ecf20Sopenharmony_ci}
33998c2ecf20Sopenharmony_ci
34008c2ecf20Sopenharmony_cistatic bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
34018c2ecf20Sopenharmony_ci{
34028c2ecf20Sopenharmony_ci	if ((flags & RTF_REJECT) ||
34038c2ecf20Sopenharmony_ci	    (dev && (dev->flags & IFF_LOOPBACK) &&
34048c2ecf20Sopenharmony_ci	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
34058c2ecf20Sopenharmony_ci	     !(flags & (RTF_ANYCAST | RTF_LOCAL))))
34068c2ecf20Sopenharmony_ci		return true;
34078c2ecf20Sopenharmony_ci
34088c2ecf20Sopenharmony_ci	return false;
34098c2ecf20Sopenharmony_ci}
34108c2ecf20Sopenharmony_ci
34118c2ecf20Sopenharmony_ciint fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
34128c2ecf20Sopenharmony_ci		 struct fib6_config *cfg, gfp_t gfp_flags,
34138c2ecf20Sopenharmony_ci		 struct netlink_ext_ack *extack)
34148c2ecf20Sopenharmony_ci{
34158c2ecf20Sopenharmony_ci	struct net_device *dev = NULL;
34168c2ecf20Sopenharmony_ci	struct inet6_dev *idev = NULL;
34178c2ecf20Sopenharmony_ci	int addr_type;
34188c2ecf20Sopenharmony_ci	int err;
34198c2ecf20Sopenharmony_ci
34208c2ecf20Sopenharmony_ci	fib6_nh->fib_nh_family = AF_INET6;
34218c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
34228c2ecf20Sopenharmony_ci	fib6_nh->last_probe = jiffies;
34238c2ecf20Sopenharmony_ci#endif
34248c2ecf20Sopenharmony_ci	if (cfg->fc_is_fdb) {
34258c2ecf20Sopenharmony_ci		fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
34268c2ecf20Sopenharmony_ci		fib6_nh->fib_nh_gw_family = AF_INET6;
34278c2ecf20Sopenharmony_ci		return 0;
34288c2ecf20Sopenharmony_ci	}
34298c2ecf20Sopenharmony_ci
34308c2ecf20Sopenharmony_ci	err = -ENODEV;
34318c2ecf20Sopenharmony_ci	if (cfg->fc_ifindex) {
34328c2ecf20Sopenharmony_ci		dev = dev_get_by_index(net, cfg->fc_ifindex);
34338c2ecf20Sopenharmony_ci		if (!dev)
34348c2ecf20Sopenharmony_ci			goto out;
34358c2ecf20Sopenharmony_ci		idev = in6_dev_get(dev);
34368c2ecf20Sopenharmony_ci		if (!idev)
34378c2ecf20Sopenharmony_ci			goto out;
34388c2ecf20Sopenharmony_ci	}
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_ci	if (cfg->fc_flags & RTNH_F_ONLINK) {
34418c2ecf20Sopenharmony_ci		if (!dev) {
34428c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack,
34438c2ecf20Sopenharmony_ci				       "Nexthop device required for onlink");
34448c2ecf20Sopenharmony_ci			goto out;
34458c2ecf20Sopenharmony_ci		}
34468c2ecf20Sopenharmony_ci
34478c2ecf20Sopenharmony_ci		if (!(dev->flags & IFF_UP)) {
34488c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthop device is not up");
34498c2ecf20Sopenharmony_ci			err = -ENETDOWN;
34508c2ecf20Sopenharmony_ci			goto out;
34518c2ecf20Sopenharmony_ci		}
34528c2ecf20Sopenharmony_ci
34538c2ecf20Sopenharmony_ci		fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
34548c2ecf20Sopenharmony_ci	}
34558c2ecf20Sopenharmony_ci
34568c2ecf20Sopenharmony_ci	fib6_nh->fib_nh_weight = 1;
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_ci	/* We cannot add true routes via loopback here,
34598c2ecf20Sopenharmony_ci	 * they would result in kernel looping; promote them to reject routes
34608c2ecf20Sopenharmony_ci	 */
34618c2ecf20Sopenharmony_ci	addr_type = ipv6_addr_type(&cfg->fc_dst);
34628c2ecf20Sopenharmony_ci	if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
34638c2ecf20Sopenharmony_ci		/* hold loopback dev/idev if we haven't done so. */
34648c2ecf20Sopenharmony_ci		if (dev != net->loopback_dev) {
34658c2ecf20Sopenharmony_ci			if (dev) {
34668c2ecf20Sopenharmony_ci				dev_put(dev);
34678c2ecf20Sopenharmony_ci				in6_dev_put(idev);
34688c2ecf20Sopenharmony_ci			}
34698c2ecf20Sopenharmony_ci			dev = net->loopback_dev;
34708c2ecf20Sopenharmony_ci			dev_hold(dev);
34718c2ecf20Sopenharmony_ci			idev = in6_dev_get(dev);
34728c2ecf20Sopenharmony_ci			if (!idev) {
34738c2ecf20Sopenharmony_ci				err = -ENODEV;
34748c2ecf20Sopenharmony_ci				goto out;
34758c2ecf20Sopenharmony_ci			}
34768c2ecf20Sopenharmony_ci		}
34778c2ecf20Sopenharmony_ci		goto pcpu_alloc;
34788c2ecf20Sopenharmony_ci	}
34798c2ecf20Sopenharmony_ci
34808c2ecf20Sopenharmony_ci	if (cfg->fc_flags & RTF_GATEWAY) {
34818c2ecf20Sopenharmony_ci		err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
34828c2ecf20Sopenharmony_ci		if (err)
34838c2ecf20Sopenharmony_ci			goto out;
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ci		fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
34868c2ecf20Sopenharmony_ci		fib6_nh->fib_nh_gw_family = AF_INET6;
34878c2ecf20Sopenharmony_ci	}
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci	err = -ENODEV;
34908c2ecf20Sopenharmony_ci	if (!dev)
34918c2ecf20Sopenharmony_ci		goto out;
34928c2ecf20Sopenharmony_ci
34938c2ecf20Sopenharmony_ci	if (!idev || idev->cnf.disable_ipv6) {
34948c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
34958c2ecf20Sopenharmony_ci		err = -EACCES;
34968c2ecf20Sopenharmony_ci		goto out;
34978c2ecf20Sopenharmony_ci	}
34988c2ecf20Sopenharmony_ci
34998c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
35008c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Nexthop device is not up");
35018c2ecf20Sopenharmony_ci		err = -ENETDOWN;
35028c2ecf20Sopenharmony_ci		goto out;
35038c2ecf20Sopenharmony_ci	}
35048c2ecf20Sopenharmony_ci
35058c2ecf20Sopenharmony_ci	if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
35068c2ecf20Sopenharmony_ci	    !netif_carrier_ok(dev))
35078c2ecf20Sopenharmony_ci		fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_ci	err = fib_nh_common_init(net, &fib6_nh->nh_common, cfg->fc_encap,
35108c2ecf20Sopenharmony_ci				 cfg->fc_encap_type, cfg, gfp_flags, extack);
35118c2ecf20Sopenharmony_ci	if (err)
35128c2ecf20Sopenharmony_ci		goto out;
35138c2ecf20Sopenharmony_ci
35148c2ecf20Sopenharmony_cipcpu_alloc:
35158c2ecf20Sopenharmony_ci	fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
35168c2ecf20Sopenharmony_ci	if (!fib6_nh->rt6i_pcpu) {
35178c2ecf20Sopenharmony_ci		err = -ENOMEM;
35188c2ecf20Sopenharmony_ci		goto out;
35198c2ecf20Sopenharmony_ci	}
35208c2ecf20Sopenharmony_ci
35218c2ecf20Sopenharmony_ci	fib6_nh->fib_nh_dev = dev;
35228c2ecf20Sopenharmony_ci	fib6_nh->fib_nh_oif = dev->ifindex;
35238c2ecf20Sopenharmony_ci	err = 0;
35248c2ecf20Sopenharmony_ciout:
35258c2ecf20Sopenharmony_ci	if (idev)
35268c2ecf20Sopenharmony_ci		in6_dev_put(idev);
35278c2ecf20Sopenharmony_ci
35288c2ecf20Sopenharmony_ci	if (err) {
35298c2ecf20Sopenharmony_ci		lwtstate_put(fib6_nh->fib_nh_lws);
35308c2ecf20Sopenharmony_ci		fib6_nh->fib_nh_lws = NULL;
35318c2ecf20Sopenharmony_ci		if (dev)
35328c2ecf20Sopenharmony_ci			dev_put(dev);
35338c2ecf20Sopenharmony_ci	}
35348c2ecf20Sopenharmony_ci
35358c2ecf20Sopenharmony_ci	return err;
35368c2ecf20Sopenharmony_ci}
35378c2ecf20Sopenharmony_ci
35388c2ecf20Sopenharmony_civoid fib6_nh_release(struct fib6_nh *fib6_nh)
35398c2ecf20Sopenharmony_ci{
35408c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
35418c2ecf20Sopenharmony_ci
35428c2ecf20Sopenharmony_ci	rcu_read_lock();
35438c2ecf20Sopenharmony_ci
35448c2ecf20Sopenharmony_ci	fib6_nh_flush_exceptions(fib6_nh, NULL);
35458c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(fib6_nh, NULL);
35468c2ecf20Sopenharmony_ci	if (bucket) {
35478c2ecf20Sopenharmony_ci		rcu_assign_pointer(fib6_nh->rt6i_exception_bucket, NULL);
35488c2ecf20Sopenharmony_ci		kfree(bucket);
35498c2ecf20Sopenharmony_ci	}
35508c2ecf20Sopenharmony_ci
35518c2ecf20Sopenharmony_ci	rcu_read_unlock();
35528c2ecf20Sopenharmony_ci
35538c2ecf20Sopenharmony_ci	if (fib6_nh->rt6i_pcpu) {
35548c2ecf20Sopenharmony_ci		int cpu;
35558c2ecf20Sopenharmony_ci
35568c2ecf20Sopenharmony_ci		for_each_possible_cpu(cpu) {
35578c2ecf20Sopenharmony_ci			struct rt6_info **ppcpu_rt;
35588c2ecf20Sopenharmony_ci			struct rt6_info *pcpu_rt;
35598c2ecf20Sopenharmony_ci
35608c2ecf20Sopenharmony_ci			ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
35618c2ecf20Sopenharmony_ci			pcpu_rt = *ppcpu_rt;
35628c2ecf20Sopenharmony_ci			if (pcpu_rt) {
35638c2ecf20Sopenharmony_ci				dst_dev_put(&pcpu_rt->dst);
35648c2ecf20Sopenharmony_ci				dst_release(&pcpu_rt->dst);
35658c2ecf20Sopenharmony_ci				*ppcpu_rt = NULL;
35668c2ecf20Sopenharmony_ci			}
35678c2ecf20Sopenharmony_ci		}
35688c2ecf20Sopenharmony_ci
35698c2ecf20Sopenharmony_ci		free_percpu(fib6_nh->rt6i_pcpu);
35708c2ecf20Sopenharmony_ci	}
35718c2ecf20Sopenharmony_ci
35728c2ecf20Sopenharmony_ci	fib_nh_common_release(&fib6_nh->nh_common);
35738c2ecf20Sopenharmony_ci}
35748c2ecf20Sopenharmony_ci
35758c2ecf20Sopenharmony_civoid fib6_nh_release_dsts(struct fib6_nh *fib6_nh)
35768c2ecf20Sopenharmony_ci{
35778c2ecf20Sopenharmony_ci	int cpu;
35788c2ecf20Sopenharmony_ci
35798c2ecf20Sopenharmony_ci	if (!fib6_nh->rt6i_pcpu)
35808c2ecf20Sopenharmony_ci		return;
35818c2ecf20Sopenharmony_ci
35828c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
35838c2ecf20Sopenharmony_ci		struct rt6_info *pcpu_rt, **ppcpu_rt;
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ci		ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
35868c2ecf20Sopenharmony_ci		pcpu_rt = xchg(ppcpu_rt, NULL);
35878c2ecf20Sopenharmony_ci		if (pcpu_rt) {
35888c2ecf20Sopenharmony_ci			dst_dev_put(&pcpu_rt->dst);
35898c2ecf20Sopenharmony_ci			dst_release(&pcpu_rt->dst);
35908c2ecf20Sopenharmony_ci		}
35918c2ecf20Sopenharmony_ci	}
35928c2ecf20Sopenharmony_ci}
35938c2ecf20Sopenharmony_ci
35948c2ecf20Sopenharmony_cistatic struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
35958c2ecf20Sopenharmony_ci					      gfp_t gfp_flags,
35968c2ecf20Sopenharmony_ci					      struct netlink_ext_ack *extack)
35978c2ecf20Sopenharmony_ci{
35988c2ecf20Sopenharmony_ci	struct net *net = cfg->fc_nlinfo.nl_net;
35998c2ecf20Sopenharmony_ci	struct fib6_info *rt = NULL;
36008c2ecf20Sopenharmony_ci	struct nexthop *nh = NULL;
36018c2ecf20Sopenharmony_ci	struct fib6_table *table;
36028c2ecf20Sopenharmony_ci	struct fib6_nh *fib6_nh;
36038c2ecf20Sopenharmony_ci	int err = -EINVAL;
36048c2ecf20Sopenharmony_ci	int addr_type;
36058c2ecf20Sopenharmony_ci
36068c2ecf20Sopenharmony_ci	/* RTF_PCPU is an internal flag; can not be set by userspace */
36078c2ecf20Sopenharmony_ci	if (cfg->fc_flags & RTF_PCPU) {
36088c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
36098c2ecf20Sopenharmony_ci		goto out;
36108c2ecf20Sopenharmony_ci	}
36118c2ecf20Sopenharmony_ci
36128c2ecf20Sopenharmony_ci	/* RTF_CACHE is an internal flag; can not be set by userspace */
36138c2ecf20Sopenharmony_ci	if (cfg->fc_flags & RTF_CACHE) {
36148c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
36158c2ecf20Sopenharmony_ci		goto out;
36168c2ecf20Sopenharmony_ci	}
36178c2ecf20Sopenharmony_ci
36188c2ecf20Sopenharmony_ci	if (cfg->fc_type > RTN_MAX) {
36198c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid route type");
36208c2ecf20Sopenharmony_ci		goto out;
36218c2ecf20Sopenharmony_ci	}
36228c2ecf20Sopenharmony_ci
36238c2ecf20Sopenharmony_ci	if (cfg->fc_dst_len > 128) {
36248c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid prefix length");
36258c2ecf20Sopenharmony_ci		goto out;
36268c2ecf20Sopenharmony_ci	}
36278c2ecf20Sopenharmony_ci	if (cfg->fc_src_len > 128) {
36288c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid source address length");
36298c2ecf20Sopenharmony_ci		goto out;
36308c2ecf20Sopenharmony_ci	}
36318c2ecf20Sopenharmony_ci#ifndef CONFIG_IPV6_SUBTREES
36328c2ecf20Sopenharmony_ci	if (cfg->fc_src_len) {
36338c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
36348c2ecf20Sopenharmony_ci			       "Specifying source address requires IPV6_SUBTREES to be enabled");
36358c2ecf20Sopenharmony_ci		goto out;
36368c2ecf20Sopenharmony_ci	}
36378c2ecf20Sopenharmony_ci#endif
36388c2ecf20Sopenharmony_ci	if (cfg->fc_nh_id) {
36398c2ecf20Sopenharmony_ci		nh = nexthop_find_by_id(net, cfg->fc_nh_id);
36408c2ecf20Sopenharmony_ci		if (!nh) {
36418c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
36428c2ecf20Sopenharmony_ci			goto out;
36438c2ecf20Sopenharmony_ci		}
36448c2ecf20Sopenharmony_ci		err = fib6_check_nexthop(nh, cfg, extack);
36458c2ecf20Sopenharmony_ci		if (err)
36468c2ecf20Sopenharmony_ci			goto out;
36478c2ecf20Sopenharmony_ci	}
36488c2ecf20Sopenharmony_ci
36498c2ecf20Sopenharmony_ci	err = -ENOBUFS;
36508c2ecf20Sopenharmony_ci	if (cfg->fc_nlinfo.nlh &&
36518c2ecf20Sopenharmony_ci	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
36528c2ecf20Sopenharmony_ci		table = fib6_get_table(net, cfg->fc_table);
36538c2ecf20Sopenharmony_ci		if (!table) {
36548c2ecf20Sopenharmony_ci			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
36558c2ecf20Sopenharmony_ci			table = fib6_new_table(net, cfg->fc_table);
36568c2ecf20Sopenharmony_ci		}
36578c2ecf20Sopenharmony_ci	} else {
36588c2ecf20Sopenharmony_ci		table = fib6_new_table(net, cfg->fc_table);
36598c2ecf20Sopenharmony_ci	}
36608c2ecf20Sopenharmony_ci
36618c2ecf20Sopenharmony_ci	if (!table)
36628c2ecf20Sopenharmony_ci		goto out;
36638c2ecf20Sopenharmony_ci
36648c2ecf20Sopenharmony_ci	err = -ENOMEM;
36658c2ecf20Sopenharmony_ci	rt = fib6_info_alloc(gfp_flags, !nh);
36668c2ecf20Sopenharmony_ci	if (!rt)
36678c2ecf20Sopenharmony_ci		goto out;
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_ci	rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
36708c2ecf20Sopenharmony_ci					       extack);
36718c2ecf20Sopenharmony_ci	if (IS_ERR(rt->fib6_metrics)) {
36728c2ecf20Sopenharmony_ci		err = PTR_ERR(rt->fib6_metrics);
36738c2ecf20Sopenharmony_ci		/* Do not leave garbage there. */
36748c2ecf20Sopenharmony_ci		rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
36758c2ecf20Sopenharmony_ci		goto out_free;
36768c2ecf20Sopenharmony_ci	}
36778c2ecf20Sopenharmony_ci
36788c2ecf20Sopenharmony_ci	if (cfg->fc_flags & RTF_ADDRCONF)
36798c2ecf20Sopenharmony_ci		rt->dst_nocount = true;
36808c2ecf20Sopenharmony_ci
36818c2ecf20Sopenharmony_ci	if (cfg->fc_flags & RTF_EXPIRES)
36828c2ecf20Sopenharmony_ci		fib6_set_expires(rt, jiffies +
36838c2ecf20Sopenharmony_ci				clock_t_to_jiffies(cfg->fc_expires));
36848c2ecf20Sopenharmony_ci	else
36858c2ecf20Sopenharmony_ci		fib6_clean_expires(rt);
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci	if (cfg->fc_protocol == RTPROT_UNSPEC)
36888c2ecf20Sopenharmony_ci		cfg->fc_protocol = RTPROT_BOOT;
36898c2ecf20Sopenharmony_ci	rt->fib6_protocol = cfg->fc_protocol;
36908c2ecf20Sopenharmony_ci
36918c2ecf20Sopenharmony_ci	rt->fib6_table = table;
36928c2ecf20Sopenharmony_ci	rt->fib6_metric = cfg->fc_metric;
36938c2ecf20Sopenharmony_ci	rt->fib6_type = cfg->fc_type ? : RTN_UNICAST;
36948c2ecf20Sopenharmony_ci	rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
36958c2ecf20Sopenharmony_ci
36968c2ecf20Sopenharmony_ci	ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
36978c2ecf20Sopenharmony_ci	rt->fib6_dst.plen = cfg->fc_dst_len;
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
37008c2ecf20Sopenharmony_ci	ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
37018c2ecf20Sopenharmony_ci	rt->fib6_src.plen = cfg->fc_src_len;
37028c2ecf20Sopenharmony_ci#endif
37038c2ecf20Sopenharmony_ci	if (nh) {
37048c2ecf20Sopenharmony_ci		if (rt->fib6_src.plen) {
37058c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthops can not be used with source routing");
37068c2ecf20Sopenharmony_ci			goto out_free;
37078c2ecf20Sopenharmony_ci		}
37088c2ecf20Sopenharmony_ci		if (!nexthop_get(nh)) {
37098c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
37108c2ecf20Sopenharmony_ci			goto out_free;
37118c2ecf20Sopenharmony_ci		}
37128c2ecf20Sopenharmony_ci		rt->nh = nh;
37138c2ecf20Sopenharmony_ci		fib6_nh = nexthop_fib6_nh(rt->nh);
37148c2ecf20Sopenharmony_ci	} else {
37158c2ecf20Sopenharmony_ci		err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack);
37168c2ecf20Sopenharmony_ci		if (err)
37178c2ecf20Sopenharmony_ci			goto out;
37188c2ecf20Sopenharmony_ci
37198c2ecf20Sopenharmony_ci		fib6_nh = rt->fib6_nh;
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci		/* We cannot add true routes via loopback here, they would
37228c2ecf20Sopenharmony_ci		 * result in kernel looping; promote them to reject routes
37238c2ecf20Sopenharmony_ci		 */
37248c2ecf20Sopenharmony_ci		addr_type = ipv6_addr_type(&cfg->fc_dst);
37258c2ecf20Sopenharmony_ci		if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev,
37268c2ecf20Sopenharmony_ci				   addr_type))
37278c2ecf20Sopenharmony_ci			rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
37288c2ecf20Sopenharmony_ci	}
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
37318c2ecf20Sopenharmony_ci		struct net_device *dev = fib6_nh->fib_nh_dev;
37328c2ecf20Sopenharmony_ci
37338c2ecf20Sopenharmony_ci		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
37348c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid source address");
37358c2ecf20Sopenharmony_ci			err = -EINVAL;
37368c2ecf20Sopenharmony_ci			goto out;
37378c2ecf20Sopenharmony_ci		}
37388c2ecf20Sopenharmony_ci		rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
37398c2ecf20Sopenharmony_ci		rt->fib6_prefsrc.plen = 128;
37408c2ecf20Sopenharmony_ci	} else
37418c2ecf20Sopenharmony_ci		rt->fib6_prefsrc.plen = 0;
37428c2ecf20Sopenharmony_ci
37438c2ecf20Sopenharmony_ci	return rt;
37448c2ecf20Sopenharmony_ciout:
37458c2ecf20Sopenharmony_ci	fib6_info_release(rt);
37468c2ecf20Sopenharmony_ci	return ERR_PTR(err);
37478c2ecf20Sopenharmony_ciout_free:
37488c2ecf20Sopenharmony_ci	ip_fib_metrics_put(rt->fib6_metrics);
37498c2ecf20Sopenharmony_ci	kfree(rt);
37508c2ecf20Sopenharmony_ci	return ERR_PTR(err);
37518c2ecf20Sopenharmony_ci}
37528c2ecf20Sopenharmony_ci
37538c2ecf20Sopenharmony_ciint ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
37548c2ecf20Sopenharmony_ci		  struct netlink_ext_ack *extack)
37558c2ecf20Sopenharmony_ci{
37568c2ecf20Sopenharmony_ci	struct fib6_info *rt;
37578c2ecf20Sopenharmony_ci	int err;
37588c2ecf20Sopenharmony_ci
37598c2ecf20Sopenharmony_ci	rt = ip6_route_info_create(cfg, gfp_flags, extack);
37608c2ecf20Sopenharmony_ci	if (IS_ERR(rt))
37618c2ecf20Sopenharmony_ci		return PTR_ERR(rt);
37628c2ecf20Sopenharmony_ci
37638c2ecf20Sopenharmony_ci	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
37648c2ecf20Sopenharmony_ci	fib6_info_release(rt);
37658c2ecf20Sopenharmony_ci
37668c2ecf20Sopenharmony_ci	return err;
37678c2ecf20Sopenharmony_ci}
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_cistatic int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
37708c2ecf20Sopenharmony_ci{
37718c2ecf20Sopenharmony_ci	struct net *net = info->nl_net;
37728c2ecf20Sopenharmony_ci	struct fib6_table *table;
37738c2ecf20Sopenharmony_ci	int err;
37748c2ecf20Sopenharmony_ci
37758c2ecf20Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry) {
37768c2ecf20Sopenharmony_ci		err = -ENOENT;
37778c2ecf20Sopenharmony_ci		goto out;
37788c2ecf20Sopenharmony_ci	}
37798c2ecf20Sopenharmony_ci
37808c2ecf20Sopenharmony_ci	table = rt->fib6_table;
37818c2ecf20Sopenharmony_ci	spin_lock_bh(&table->tb6_lock);
37828c2ecf20Sopenharmony_ci	err = fib6_del(rt, info);
37838c2ecf20Sopenharmony_ci	spin_unlock_bh(&table->tb6_lock);
37848c2ecf20Sopenharmony_ci
37858c2ecf20Sopenharmony_ciout:
37868c2ecf20Sopenharmony_ci	fib6_info_release(rt);
37878c2ecf20Sopenharmony_ci	return err;
37888c2ecf20Sopenharmony_ci}
37898c2ecf20Sopenharmony_ci
37908c2ecf20Sopenharmony_ciint ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
37918c2ecf20Sopenharmony_ci{
37928c2ecf20Sopenharmony_ci	struct nl_info info = {
37938c2ecf20Sopenharmony_ci		.nl_net = net,
37948c2ecf20Sopenharmony_ci		.skip_notify = skip_notify
37958c2ecf20Sopenharmony_ci	};
37968c2ecf20Sopenharmony_ci
37978c2ecf20Sopenharmony_ci	return __ip6_del_rt(rt, &info);
37988c2ecf20Sopenharmony_ci}
37998c2ecf20Sopenharmony_ci
38008c2ecf20Sopenharmony_cistatic int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
38018c2ecf20Sopenharmony_ci{
38028c2ecf20Sopenharmony_ci	struct nl_info *info = &cfg->fc_nlinfo;
38038c2ecf20Sopenharmony_ci	struct net *net = info->nl_net;
38048c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
38058c2ecf20Sopenharmony_ci	struct fib6_table *table;
38068c2ecf20Sopenharmony_ci	int err = -ENOENT;
38078c2ecf20Sopenharmony_ci
38088c2ecf20Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry)
38098c2ecf20Sopenharmony_ci		goto out_put;
38108c2ecf20Sopenharmony_ci	table = rt->fib6_table;
38118c2ecf20Sopenharmony_ci	spin_lock_bh(&table->tb6_lock);
38128c2ecf20Sopenharmony_ci
38138c2ecf20Sopenharmony_ci	if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
38148c2ecf20Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
38158c2ecf20Sopenharmony_ci		struct fib6_node *fn;
38168c2ecf20Sopenharmony_ci
38178c2ecf20Sopenharmony_ci		/* prefer to send a single notification with all hops */
38188c2ecf20Sopenharmony_ci		skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
38198c2ecf20Sopenharmony_ci		if (skb) {
38208c2ecf20Sopenharmony_ci			u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
38218c2ecf20Sopenharmony_ci
38228c2ecf20Sopenharmony_ci			if (rt6_fill_node(net, skb, rt, NULL,
38238c2ecf20Sopenharmony_ci					  NULL, NULL, 0, RTM_DELROUTE,
38248c2ecf20Sopenharmony_ci					  info->portid, seq, 0) < 0) {
38258c2ecf20Sopenharmony_ci				kfree_skb(skb);
38268c2ecf20Sopenharmony_ci				skb = NULL;
38278c2ecf20Sopenharmony_ci			} else
38288c2ecf20Sopenharmony_ci				info->skip_notify = 1;
38298c2ecf20Sopenharmony_ci		}
38308c2ecf20Sopenharmony_ci
38318c2ecf20Sopenharmony_ci		/* 'rt' points to the first sibling route. If it is not the
38328c2ecf20Sopenharmony_ci		 * leaf, then we do not need to send a notification. Otherwise,
38338c2ecf20Sopenharmony_ci		 * we need to check if the last sibling has a next route or not
38348c2ecf20Sopenharmony_ci		 * and emit a replace or delete notification, respectively.
38358c2ecf20Sopenharmony_ci		 */
38368c2ecf20Sopenharmony_ci		info->skip_notify_kernel = 1;
38378c2ecf20Sopenharmony_ci		fn = rcu_dereference_protected(rt->fib6_node,
38388c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
38398c2ecf20Sopenharmony_ci		if (rcu_access_pointer(fn->leaf) == rt) {
38408c2ecf20Sopenharmony_ci			struct fib6_info *last_sibling, *replace_rt;
38418c2ecf20Sopenharmony_ci
38428c2ecf20Sopenharmony_ci			last_sibling = list_last_entry(&rt->fib6_siblings,
38438c2ecf20Sopenharmony_ci						       struct fib6_info,
38448c2ecf20Sopenharmony_ci						       fib6_siblings);
38458c2ecf20Sopenharmony_ci			replace_rt = rcu_dereference_protected(
38468c2ecf20Sopenharmony_ci					    last_sibling->fib6_next,
38478c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
38488c2ecf20Sopenharmony_ci			if (replace_rt)
38498c2ecf20Sopenharmony_ci				call_fib6_entry_notifiers_replace(net,
38508c2ecf20Sopenharmony_ci								  replace_rt);
38518c2ecf20Sopenharmony_ci			else
38528c2ecf20Sopenharmony_ci				call_fib6_multipath_entry_notifiers(net,
38538c2ecf20Sopenharmony_ci						       FIB_EVENT_ENTRY_DEL,
38548c2ecf20Sopenharmony_ci						       rt, rt->fib6_nsiblings,
38558c2ecf20Sopenharmony_ci						       NULL);
38568c2ecf20Sopenharmony_ci		}
38578c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
38588c2ecf20Sopenharmony_ci					 &rt->fib6_siblings,
38598c2ecf20Sopenharmony_ci					 fib6_siblings) {
38608c2ecf20Sopenharmony_ci			err = fib6_del(sibling, info);
38618c2ecf20Sopenharmony_ci			if (err)
38628c2ecf20Sopenharmony_ci				goto out_unlock;
38638c2ecf20Sopenharmony_ci		}
38648c2ecf20Sopenharmony_ci	}
38658c2ecf20Sopenharmony_ci
38668c2ecf20Sopenharmony_ci	err = fib6_del(rt, info);
38678c2ecf20Sopenharmony_ciout_unlock:
38688c2ecf20Sopenharmony_ci	spin_unlock_bh(&table->tb6_lock);
38698c2ecf20Sopenharmony_ciout_put:
38708c2ecf20Sopenharmony_ci	fib6_info_release(rt);
38718c2ecf20Sopenharmony_ci
38728c2ecf20Sopenharmony_ci	if (skb) {
38738c2ecf20Sopenharmony_ci		rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
38748c2ecf20Sopenharmony_ci			    info->nlh, gfp_any());
38758c2ecf20Sopenharmony_ci	}
38768c2ecf20Sopenharmony_ci	return err;
38778c2ecf20Sopenharmony_ci}
38788c2ecf20Sopenharmony_ci
38798c2ecf20Sopenharmony_cistatic int __ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
38808c2ecf20Sopenharmony_ci{
38818c2ecf20Sopenharmony_ci	int rc = -ESRCH;
38828c2ecf20Sopenharmony_ci
38838c2ecf20Sopenharmony_ci	if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
38848c2ecf20Sopenharmony_ci		goto out;
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_ci	if (cfg->fc_flags & RTF_GATEWAY &&
38878c2ecf20Sopenharmony_ci	    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
38888c2ecf20Sopenharmony_ci		goto out;
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_ci	rc = rt6_remove_exception_rt(rt);
38918c2ecf20Sopenharmony_ciout:
38928c2ecf20Sopenharmony_ci	return rc;
38938c2ecf20Sopenharmony_ci}
38948c2ecf20Sopenharmony_ci
38958c2ecf20Sopenharmony_cistatic int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt,
38968c2ecf20Sopenharmony_ci			     struct fib6_nh *nh)
38978c2ecf20Sopenharmony_ci{
38988c2ecf20Sopenharmony_ci	struct fib6_result res = {
38998c2ecf20Sopenharmony_ci		.f6i = rt,
39008c2ecf20Sopenharmony_ci		.nh = nh,
39018c2ecf20Sopenharmony_ci	};
39028c2ecf20Sopenharmony_ci	struct rt6_info *rt_cache;
39038c2ecf20Sopenharmony_ci
39048c2ecf20Sopenharmony_ci	rt_cache = rt6_find_cached_rt(&res, &cfg->fc_dst, &cfg->fc_src);
39058c2ecf20Sopenharmony_ci	if (rt_cache)
39068c2ecf20Sopenharmony_ci		return __ip6_del_cached_rt(rt_cache, cfg);
39078c2ecf20Sopenharmony_ci
39088c2ecf20Sopenharmony_ci	return 0;
39098c2ecf20Sopenharmony_ci}
39108c2ecf20Sopenharmony_ci
39118c2ecf20Sopenharmony_cistruct fib6_nh_del_cached_rt_arg {
39128c2ecf20Sopenharmony_ci	struct fib6_config *cfg;
39138c2ecf20Sopenharmony_ci	struct fib6_info *f6i;
39148c2ecf20Sopenharmony_ci};
39158c2ecf20Sopenharmony_ci
39168c2ecf20Sopenharmony_cistatic int fib6_nh_del_cached_rt(struct fib6_nh *nh, void *_arg)
39178c2ecf20Sopenharmony_ci{
39188c2ecf20Sopenharmony_ci	struct fib6_nh_del_cached_rt_arg *arg = _arg;
39198c2ecf20Sopenharmony_ci	int rc;
39208c2ecf20Sopenharmony_ci
39218c2ecf20Sopenharmony_ci	rc = ip6_del_cached_rt(arg->cfg, arg->f6i, nh);
39228c2ecf20Sopenharmony_ci	return rc != -ESRCH ? rc : 0;
39238c2ecf20Sopenharmony_ci}
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_cistatic int ip6_del_cached_rt_nh(struct fib6_config *cfg, struct fib6_info *f6i)
39268c2ecf20Sopenharmony_ci{
39278c2ecf20Sopenharmony_ci	struct fib6_nh_del_cached_rt_arg arg = {
39288c2ecf20Sopenharmony_ci		.cfg = cfg,
39298c2ecf20Sopenharmony_ci		.f6i = f6i
39308c2ecf20Sopenharmony_ci	};
39318c2ecf20Sopenharmony_ci
39328c2ecf20Sopenharmony_ci	return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_del_cached_rt, &arg);
39338c2ecf20Sopenharmony_ci}
39348c2ecf20Sopenharmony_ci
39358c2ecf20Sopenharmony_cistatic int ip6_route_del(struct fib6_config *cfg,
39368c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
39378c2ecf20Sopenharmony_ci{
39388c2ecf20Sopenharmony_ci	struct fib6_table *table;
39398c2ecf20Sopenharmony_ci	struct fib6_info *rt;
39408c2ecf20Sopenharmony_ci	struct fib6_node *fn;
39418c2ecf20Sopenharmony_ci	int err = -ESRCH;
39428c2ecf20Sopenharmony_ci
39438c2ecf20Sopenharmony_ci	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
39448c2ecf20Sopenharmony_ci	if (!table) {
39458c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "FIB table does not exist");
39468c2ecf20Sopenharmony_ci		return err;
39478c2ecf20Sopenharmony_ci	}
39488c2ecf20Sopenharmony_ci
39498c2ecf20Sopenharmony_ci	rcu_read_lock();
39508c2ecf20Sopenharmony_ci
39518c2ecf20Sopenharmony_ci	fn = fib6_locate(&table->tb6_root,
39528c2ecf20Sopenharmony_ci			 &cfg->fc_dst, cfg->fc_dst_len,
39538c2ecf20Sopenharmony_ci			 &cfg->fc_src, cfg->fc_src_len,
39548c2ecf20Sopenharmony_ci			 !(cfg->fc_flags & RTF_CACHE));
39558c2ecf20Sopenharmony_ci
39568c2ecf20Sopenharmony_ci	if (fn) {
39578c2ecf20Sopenharmony_ci		for_each_fib6_node_rt_rcu(fn) {
39588c2ecf20Sopenharmony_ci			struct fib6_nh *nh;
39598c2ecf20Sopenharmony_ci
39608c2ecf20Sopenharmony_ci			if (rt->nh && cfg->fc_nh_id &&
39618c2ecf20Sopenharmony_ci			    rt->nh->id != cfg->fc_nh_id)
39628c2ecf20Sopenharmony_ci				continue;
39638c2ecf20Sopenharmony_ci
39648c2ecf20Sopenharmony_ci			if (cfg->fc_flags & RTF_CACHE) {
39658c2ecf20Sopenharmony_ci				int rc = 0;
39668c2ecf20Sopenharmony_ci
39678c2ecf20Sopenharmony_ci				if (rt->nh) {
39688c2ecf20Sopenharmony_ci					rc = ip6_del_cached_rt_nh(cfg, rt);
39698c2ecf20Sopenharmony_ci				} else if (cfg->fc_nh_id) {
39708c2ecf20Sopenharmony_ci					continue;
39718c2ecf20Sopenharmony_ci				} else {
39728c2ecf20Sopenharmony_ci					nh = rt->fib6_nh;
39738c2ecf20Sopenharmony_ci					rc = ip6_del_cached_rt(cfg, rt, nh);
39748c2ecf20Sopenharmony_ci				}
39758c2ecf20Sopenharmony_ci				if (rc != -ESRCH) {
39768c2ecf20Sopenharmony_ci					rcu_read_unlock();
39778c2ecf20Sopenharmony_ci					return rc;
39788c2ecf20Sopenharmony_ci				}
39798c2ecf20Sopenharmony_ci				continue;
39808c2ecf20Sopenharmony_ci			}
39818c2ecf20Sopenharmony_ci
39828c2ecf20Sopenharmony_ci			if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
39838c2ecf20Sopenharmony_ci				continue;
39848c2ecf20Sopenharmony_ci			if (cfg->fc_protocol &&
39858c2ecf20Sopenharmony_ci			    cfg->fc_protocol != rt->fib6_protocol)
39868c2ecf20Sopenharmony_ci				continue;
39878c2ecf20Sopenharmony_ci
39888c2ecf20Sopenharmony_ci			if (rt->nh) {
39898c2ecf20Sopenharmony_ci				if (!fib6_info_hold_safe(rt))
39908c2ecf20Sopenharmony_ci					continue;
39918c2ecf20Sopenharmony_ci				rcu_read_unlock();
39928c2ecf20Sopenharmony_ci
39938c2ecf20Sopenharmony_ci				return __ip6_del_rt(rt, &cfg->fc_nlinfo);
39948c2ecf20Sopenharmony_ci			}
39958c2ecf20Sopenharmony_ci			if (cfg->fc_nh_id)
39968c2ecf20Sopenharmony_ci				continue;
39978c2ecf20Sopenharmony_ci
39988c2ecf20Sopenharmony_ci			nh = rt->fib6_nh;
39998c2ecf20Sopenharmony_ci			if (cfg->fc_ifindex &&
40008c2ecf20Sopenharmony_ci			    (!nh->fib_nh_dev ||
40018c2ecf20Sopenharmony_ci			     nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
40028c2ecf20Sopenharmony_ci				continue;
40038c2ecf20Sopenharmony_ci			if (cfg->fc_flags & RTF_GATEWAY &&
40048c2ecf20Sopenharmony_ci			    !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
40058c2ecf20Sopenharmony_ci				continue;
40068c2ecf20Sopenharmony_ci			if (!fib6_info_hold_safe(rt))
40078c2ecf20Sopenharmony_ci				continue;
40088c2ecf20Sopenharmony_ci			rcu_read_unlock();
40098c2ecf20Sopenharmony_ci
40108c2ecf20Sopenharmony_ci			/* if gateway was specified only delete the one hop */
40118c2ecf20Sopenharmony_ci			if (cfg->fc_flags & RTF_GATEWAY)
40128c2ecf20Sopenharmony_ci				return __ip6_del_rt(rt, &cfg->fc_nlinfo);
40138c2ecf20Sopenharmony_ci
40148c2ecf20Sopenharmony_ci			return __ip6_del_rt_siblings(rt, cfg);
40158c2ecf20Sopenharmony_ci		}
40168c2ecf20Sopenharmony_ci	}
40178c2ecf20Sopenharmony_ci	rcu_read_unlock();
40188c2ecf20Sopenharmony_ci
40198c2ecf20Sopenharmony_ci	return err;
40208c2ecf20Sopenharmony_ci}
40218c2ecf20Sopenharmony_ci
40228c2ecf20Sopenharmony_cistatic void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
40238c2ecf20Sopenharmony_ci{
40248c2ecf20Sopenharmony_ci	struct netevent_redirect netevent;
40258c2ecf20Sopenharmony_ci	struct rt6_info *rt, *nrt = NULL;
40268c2ecf20Sopenharmony_ci	struct fib6_result res = {};
40278c2ecf20Sopenharmony_ci	struct ndisc_options ndopts;
40288c2ecf20Sopenharmony_ci	struct inet6_dev *in6_dev;
40298c2ecf20Sopenharmony_ci	struct neighbour *neigh;
40308c2ecf20Sopenharmony_ci	struct rd_msg *msg;
40318c2ecf20Sopenharmony_ci	int optlen, on_link;
40328c2ecf20Sopenharmony_ci	u8 *lladdr;
40338c2ecf20Sopenharmony_ci
40348c2ecf20Sopenharmony_ci	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
40358c2ecf20Sopenharmony_ci	optlen -= sizeof(*msg);
40368c2ecf20Sopenharmony_ci
40378c2ecf20Sopenharmony_ci	if (optlen < 0) {
40388c2ecf20Sopenharmony_ci		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
40398c2ecf20Sopenharmony_ci		return;
40408c2ecf20Sopenharmony_ci	}
40418c2ecf20Sopenharmony_ci
40428c2ecf20Sopenharmony_ci	msg = (struct rd_msg *)icmp6_hdr(skb);
40438c2ecf20Sopenharmony_ci
40448c2ecf20Sopenharmony_ci	if (ipv6_addr_is_multicast(&msg->dest)) {
40458c2ecf20Sopenharmony_ci		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
40468c2ecf20Sopenharmony_ci		return;
40478c2ecf20Sopenharmony_ci	}
40488c2ecf20Sopenharmony_ci
40498c2ecf20Sopenharmony_ci	on_link = 0;
40508c2ecf20Sopenharmony_ci	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
40518c2ecf20Sopenharmony_ci		on_link = 1;
40528c2ecf20Sopenharmony_ci	} else if (ipv6_addr_type(&msg->target) !=
40538c2ecf20Sopenharmony_ci		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
40548c2ecf20Sopenharmony_ci		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
40558c2ecf20Sopenharmony_ci		return;
40568c2ecf20Sopenharmony_ci	}
40578c2ecf20Sopenharmony_ci
40588c2ecf20Sopenharmony_ci	in6_dev = __in6_dev_get(skb->dev);
40598c2ecf20Sopenharmony_ci	if (!in6_dev)
40608c2ecf20Sopenharmony_ci		return;
40618c2ecf20Sopenharmony_ci	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
40628c2ecf20Sopenharmony_ci		return;
40638c2ecf20Sopenharmony_ci
40648c2ecf20Sopenharmony_ci	/* RFC2461 8.1:
40658c2ecf20Sopenharmony_ci	 *	The IP source address of the Redirect MUST be the same as the current
40668c2ecf20Sopenharmony_ci	 *	first-hop router for the specified ICMP Destination Address.
40678c2ecf20Sopenharmony_ci	 */
40688c2ecf20Sopenharmony_ci
40698c2ecf20Sopenharmony_ci	if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
40708c2ecf20Sopenharmony_ci		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
40718c2ecf20Sopenharmony_ci		return;
40728c2ecf20Sopenharmony_ci	}
40738c2ecf20Sopenharmony_ci
40748c2ecf20Sopenharmony_ci	lladdr = NULL;
40758c2ecf20Sopenharmony_ci	if (ndopts.nd_opts_tgt_lladdr) {
40768c2ecf20Sopenharmony_ci		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
40778c2ecf20Sopenharmony_ci					     skb->dev);
40788c2ecf20Sopenharmony_ci		if (!lladdr) {
40798c2ecf20Sopenharmony_ci			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
40808c2ecf20Sopenharmony_ci			return;
40818c2ecf20Sopenharmony_ci		}
40828c2ecf20Sopenharmony_ci	}
40838c2ecf20Sopenharmony_ci
40848c2ecf20Sopenharmony_ci	rt = (struct rt6_info *) dst;
40858c2ecf20Sopenharmony_ci	if (rt->rt6i_flags & RTF_REJECT) {
40868c2ecf20Sopenharmony_ci		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
40878c2ecf20Sopenharmony_ci		return;
40888c2ecf20Sopenharmony_ci	}
40898c2ecf20Sopenharmony_ci
40908c2ecf20Sopenharmony_ci	/* Redirect received -> path was valid.
40918c2ecf20Sopenharmony_ci	 * Look, redirects are sent only in response to data packets,
40928c2ecf20Sopenharmony_ci	 * so that this nexthop apparently is reachable. --ANK
40938c2ecf20Sopenharmony_ci	 */
40948c2ecf20Sopenharmony_ci	dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
40958c2ecf20Sopenharmony_ci
40968c2ecf20Sopenharmony_ci	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
40978c2ecf20Sopenharmony_ci	if (!neigh)
40988c2ecf20Sopenharmony_ci		return;
40998c2ecf20Sopenharmony_ci
41008c2ecf20Sopenharmony_ci	/*
41018c2ecf20Sopenharmony_ci	 *	We have finally decided to accept it.
41028c2ecf20Sopenharmony_ci	 */
41038c2ecf20Sopenharmony_ci
41048c2ecf20Sopenharmony_ci	ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
41058c2ecf20Sopenharmony_ci		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
41068c2ecf20Sopenharmony_ci		     NEIGH_UPDATE_F_OVERRIDE|
41078c2ecf20Sopenharmony_ci		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
41088c2ecf20Sopenharmony_ci				     NEIGH_UPDATE_F_ISROUTER)),
41098c2ecf20Sopenharmony_ci		     NDISC_REDIRECT, &ndopts);
41108c2ecf20Sopenharmony_ci
41118c2ecf20Sopenharmony_ci	rcu_read_lock();
41128c2ecf20Sopenharmony_ci	res.f6i = rcu_dereference(rt->from);
41138c2ecf20Sopenharmony_ci	if (!res.f6i)
41148c2ecf20Sopenharmony_ci		goto out;
41158c2ecf20Sopenharmony_ci
41168c2ecf20Sopenharmony_ci	if (res.f6i->nh) {
41178c2ecf20Sopenharmony_ci		struct fib6_nh_match_arg arg = {
41188c2ecf20Sopenharmony_ci			.dev = dst->dev,
41198c2ecf20Sopenharmony_ci			.gw = &rt->rt6i_gateway,
41208c2ecf20Sopenharmony_ci		};
41218c2ecf20Sopenharmony_ci
41228c2ecf20Sopenharmony_ci		nexthop_for_each_fib6_nh(res.f6i->nh,
41238c2ecf20Sopenharmony_ci					 fib6_nh_find_match, &arg);
41248c2ecf20Sopenharmony_ci
41258c2ecf20Sopenharmony_ci		/* fib6_info uses a nexthop that does not have fib6_nh
41268c2ecf20Sopenharmony_ci		 * using the dst->dev. Should be impossible
41278c2ecf20Sopenharmony_ci		 */
41288c2ecf20Sopenharmony_ci		if (!arg.match)
41298c2ecf20Sopenharmony_ci			goto out;
41308c2ecf20Sopenharmony_ci		res.nh = arg.match;
41318c2ecf20Sopenharmony_ci	} else {
41328c2ecf20Sopenharmony_ci		res.nh = res.f6i->fib6_nh;
41338c2ecf20Sopenharmony_ci	}
41348c2ecf20Sopenharmony_ci
41358c2ecf20Sopenharmony_ci	res.fib6_flags = res.f6i->fib6_flags;
41368c2ecf20Sopenharmony_ci	res.fib6_type = res.f6i->fib6_type;
41378c2ecf20Sopenharmony_ci	nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
41388c2ecf20Sopenharmony_ci	if (!nrt)
41398c2ecf20Sopenharmony_ci		goto out;
41408c2ecf20Sopenharmony_ci
41418c2ecf20Sopenharmony_ci	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
41428c2ecf20Sopenharmony_ci	if (on_link)
41438c2ecf20Sopenharmony_ci		nrt->rt6i_flags &= ~RTF_GATEWAY;
41448c2ecf20Sopenharmony_ci
41458c2ecf20Sopenharmony_ci	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
41468c2ecf20Sopenharmony_ci
41478c2ecf20Sopenharmony_ci	/* rt6_insert_exception() will take care of duplicated exceptions */
41488c2ecf20Sopenharmony_ci	if (rt6_insert_exception(nrt, &res)) {
41498c2ecf20Sopenharmony_ci		dst_release_immediate(&nrt->dst);
41508c2ecf20Sopenharmony_ci		goto out;
41518c2ecf20Sopenharmony_ci	}
41528c2ecf20Sopenharmony_ci
41538c2ecf20Sopenharmony_ci	netevent.old = &rt->dst;
41548c2ecf20Sopenharmony_ci	netevent.new = &nrt->dst;
41558c2ecf20Sopenharmony_ci	netevent.daddr = &msg->dest;
41568c2ecf20Sopenharmony_ci	netevent.neigh = neigh;
41578c2ecf20Sopenharmony_ci	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
41588c2ecf20Sopenharmony_ci
41598c2ecf20Sopenharmony_ciout:
41608c2ecf20Sopenharmony_ci	rcu_read_unlock();
41618c2ecf20Sopenharmony_ci	neigh_release(neigh);
41628c2ecf20Sopenharmony_ci}
41638c2ecf20Sopenharmony_ci
41648c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
41658c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net,
41668c2ecf20Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
41678c2ecf20Sopenharmony_ci					   const struct in6_addr *gwaddr,
41688c2ecf20Sopenharmony_ci					   struct net_device *dev)
41698c2ecf20Sopenharmony_ci{
41708c2ecf20Sopenharmony_ci	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
41718c2ecf20Sopenharmony_ci	int ifindex = dev->ifindex;
41728c2ecf20Sopenharmony_ci	struct fib6_node *fn;
41738c2ecf20Sopenharmony_ci	struct fib6_info *rt = NULL;
41748c2ecf20Sopenharmony_ci	struct fib6_table *table;
41758c2ecf20Sopenharmony_ci
41768c2ecf20Sopenharmony_ci	table = fib6_get_table(net, tb_id);
41778c2ecf20Sopenharmony_ci	if (!table)
41788c2ecf20Sopenharmony_ci		return NULL;
41798c2ecf20Sopenharmony_ci
41808c2ecf20Sopenharmony_ci	rcu_read_lock();
41818c2ecf20Sopenharmony_ci	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
41828c2ecf20Sopenharmony_ci	if (!fn)
41838c2ecf20Sopenharmony_ci		goto out;
41848c2ecf20Sopenharmony_ci
41858c2ecf20Sopenharmony_ci	for_each_fib6_node_rt_rcu(fn) {
41868c2ecf20Sopenharmony_ci		/* these routes do not use nexthops */
41878c2ecf20Sopenharmony_ci		if (rt->nh)
41888c2ecf20Sopenharmony_ci			continue;
41898c2ecf20Sopenharmony_ci		if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex)
41908c2ecf20Sopenharmony_ci			continue;
41918c2ecf20Sopenharmony_ci		if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
41928c2ecf20Sopenharmony_ci		    !rt->fib6_nh->fib_nh_gw_family)
41938c2ecf20Sopenharmony_ci			continue;
41948c2ecf20Sopenharmony_ci		if (!ipv6_addr_equal(&rt->fib6_nh->fib_nh_gw6, gwaddr))
41958c2ecf20Sopenharmony_ci			continue;
41968c2ecf20Sopenharmony_ci		if (!fib6_info_hold_safe(rt))
41978c2ecf20Sopenharmony_ci			continue;
41988c2ecf20Sopenharmony_ci		break;
41998c2ecf20Sopenharmony_ci	}
42008c2ecf20Sopenharmony_ciout:
42018c2ecf20Sopenharmony_ci	rcu_read_unlock();
42028c2ecf20Sopenharmony_ci	return rt;
42038c2ecf20Sopenharmony_ci}
42048c2ecf20Sopenharmony_ci
42058c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net,
42068c2ecf20Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
42078c2ecf20Sopenharmony_ci					   const struct in6_addr *gwaddr,
42088c2ecf20Sopenharmony_ci					   struct net_device *dev,
42098c2ecf20Sopenharmony_ci					   unsigned int pref)
42108c2ecf20Sopenharmony_ci{
42118c2ecf20Sopenharmony_ci	struct fib6_config cfg = {
42128c2ecf20Sopenharmony_ci		.fc_metric	= IP6_RT_PRIO_USER,
42138c2ecf20Sopenharmony_ci		.fc_ifindex	= dev->ifindex,
42148c2ecf20Sopenharmony_ci		.fc_dst_len	= prefixlen,
42158c2ecf20Sopenharmony_ci		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
42168c2ecf20Sopenharmony_ci				  RTF_UP | RTF_PREF(pref),
42178c2ecf20Sopenharmony_ci		.fc_protocol = RTPROT_RA,
42188c2ecf20Sopenharmony_ci		.fc_type = RTN_UNICAST,
42198c2ecf20Sopenharmony_ci		.fc_nlinfo.portid = 0,
42208c2ecf20Sopenharmony_ci		.fc_nlinfo.nlh = NULL,
42218c2ecf20Sopenharmony_ci		.fc_nlinfo.nl_net = net,
42228c2ecf20Sopenharmony_ci	};
42238c2ecf20Sopenharmony_ci
42248c2ecf20Sopenharmony_ci	cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
42258c2ecf20Sopenharmony_ci	cfg.fc_dst = *prefix;
42268c2ecf20Sopenharmony_ci	cfg.fc_gateway = *gwaddr;
42278c2ecf20Sopenharmony_ci
42288c2ecf20Sopenharmony_ci	/* We should treat it as a default route if prefix length is 0. */
42298c2ecf20Sopenharmony_ci	if (!prefixlen)
42308c2ecf20Sopenharmony_ci		cfg.fc_flags |= RTF_DEFAULT;
42318c2ecf20Sopenharmony_ci
42328c2ecf20Sopenharmony_ci	ip6_route_add(&cfg, GFP_ATOMIC, NULL);
42338c2ecf20Sopenharmony_ci
42348c2ecf20Sopenharmony_ci	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
42358c2ecf20Sopenharmony_ci}
42368c2ecf20Sopenharmony_ci#endif
42378c2ecf20Sopenharmony_ci
42388c2ecf20Sopenharmony_cistruct fib6_info *rt6_get_dflt_router(struct net *net,
42398c2ecf20Sopenharmony_ci				     const struct in6_addr *addr,
42408c2ecf20Sopenharmony_ci				     struct net_device *dev)
42418c2ecf20Sopenharmony_ci{
42428c2ecf20Sopenharmony_ci	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
42438c2ecf20Sopenharmony_ci	struct fib6_info *rt;
42448c2ecf20Sopenharmony_ci	struct fib6_table *table;
42458c2ecf20Sopenharmony_ci
42468c2ecf20Sopenharmony_ci	table = fib6_get_table(net, tb_id);
42478c2ecf20Sopenharmony_ci	if (!table)
42488c2ecf20Sopenharmony_ci		return NULL;
42498c2ecf20Sopenharmony_ci
42508c2ecf20Sopenharmony_ci	rcu_read_lock();
42518c2ecf20Sopenharmony_ci	for_each_fib6_node_rt_rcu(&table->tb6_root) {
42528c2ecf20Sopenharmony_ci		struct fib6_nh *nh;
42538c2ecf20Sopenharmony_ci
42548c2ecf20Sopenharmony_ci		/* RA routes do not use nexthops */
42558c2ecf20Sopenharmony_ci		if (rt->nh)
42568c2ecf20Sopenharmony_ci			continue;
42578c2ecf20Sopenharmony_ci
42588c2ecf20Sopenharmony_ci		nh = rt->fib6_nh;
42598c2ecf20Sopenharmony_ci		if (dev == nh->fib_nh_dev &&
42608c2ecf20Sopenharmony_ci		    ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
42618c2ecf20Sopenharmony_ci		    ipv6_addr_equal(&nh->fib_nh_gw6, addr))
42628c2ecf20Sopenharmony_ci			break;
42638c2ecf20Sopenharmony_ci	}
42648c2ecf20Sopenharmony_ci	if (rt && !fib6_info_hold_safe(rt))
42658c2ecf20Sopenharmony_ci		rt = NULL;
42668c2ecf20Sopenharmony_ci	rcu_read_unlock();
42678c2ecf20Sopenharmony_ci	return rt;
42688c2ecf20Sopenharmony_ci}
42698c2ecf20Sopenharmony_ci
42708c2ecf20Sopenharmony_cistruct fib6_info *rt6_add_dflt_router(struct net *net,
42718c2ecf20Sopenharmony_ci				     const struct in6_addr *gwaddr,
42728c2ecf20Sopenharmony_ci				     struct net_device *dev,
42738c2ecf20Sopenharmony_ci				     unsigned int pref)
42748c2ecf20Sopenharmony_ci{
42758c2ecf20Sopenharmony_ci	struct fib6_config cfg = {
42768c2ecf20Sopenharmony_ci		.fc_table	= l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
42778c2ecf20Sopenharmony_ci		.fc_metric	= IP6_RT_PRIO_USER,
42788c2ecf20Sopenharmony_ci		.fc_ifindex	= dev->ifindex,
42798c2ecf20Sopenharmony_ci		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
42808c2ecf20Sopenharmony_ci				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
42818c2ecf20Sopenharmony_ci		.fc_protocol = RTPROT_RA,
42828c2ecf20Sopenharmony_ci		.fc_type = RTN_UNICAST,
42838c2ecf20Sopenharmony_ci		.fc_nlinfo.portid = 0,
42848c2ecf20Sopenharmony_ci		.fc_nlinfo.nlh = NULL,
42858c2ecf20Sopenharmony_ci		.fc_nlinfo.nl_net = net,
42868c2ecf20Sopenharmony_ci	};
42878c2ecf20Sopenharmony_ci
42888c2ecf20Sopenharmony_ci	cfg.fc_gateway = *gwaddr;
42898c2ecf20Sopenharmony_ci
42908c2ecf20Sopenharmony_ci	if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
42918c2ecf20Sopenharmony_ci		struct fib6_table *table;
42928c2ecf20Sopenharmony_ci
42938c2ecf20Sopenharmony_ci		table = fib6_get_table(dev_net(dev), cfg.fc_table);
42948c2ecf20Sopenharmony_ci		if (table)
42958c2ecf20Sopenharmony_ci			table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
42968c2ecf20Sopenharmony_ci	}
42978c2ecf20Sopenharmony_ci
42988c2ecf20Sopenharmony_ci	return rt6_get_dflt_router(net, gwaddr, dev);
42998c2ecf20Sopenharmony_ci}
43008c2ecf20Sopenharmony_ci
43018c2ecf20Sopenharmony_cistatic void __rt6_purge_dflt_routers(struct net *net,
43028c2ecf20Sopenharmony_ci				     struct fib6_table *table)
43038c2ecf20Sopenharmony_ci{
43048c2ecf20Sopenharmony_ci	struct fib6_info *rt;
43058c2ecf20Sopenharmony_ci
43068c2ecf20Sopenharmony_cirestart:
43078c2ecf20Sopenharmony_ci	rcu_read_lock();
43088c2ecf20Sopenharmony_ci	for_each_fib6_node_rt_rcu(&table->tb6_root) {
43098c2ecf20Sopenharmony_ci		struct net_device *dev = fib6_info_nh_dev(rt);
43108c2ecf20Sopenharmony_ci		struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
43118c2ecf20Sopenharmony_ci
43128c2ecf20Sopenharmony_ci		if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
43138c2ecf20Sopenharmony_ci		    (!idev || idev->cnf.accept_ra != 2) &&
43148c2ecf20Sopenharmony_ci		    fib6_info_hold_safe(rt)) {
43158c2ecf20Sopenharmony_ci			rcu_read_unlock();
43168c2ecf20Sopenharmony_ci			ip6_del_rt(net, rt, false);
43178c2ecf20Sopenharmony_ci			goto restart;
43188c2ecf20Sopenharmony_ci		}
43198c2ecf20Sopenharmony_ci	}
43208c2ecf20Sopenharmony_ci	rcu_read_unlock();
43218c2ecf20Sopenharmony_ci
43228c2ecf20Sopenharmony_ci	table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
43238c2ecf20Sopenharmony_ci}
43248c2ecf20Sopenharmony_ci
43258c2ecf20Sopenharmony_civoid rt6_purge_dflt_routers(struct net *net)
43268c2ecf20Sopenharmony_ci{
43278c2ecf20Sopenharmony_ci	struct fib6_table *table;
43288c2ecf20Sopenharmony_ci	struct hlist_head *head;
43298c2ecf20Sopenharmony_ci	unsigned int h;
43308c2ecf20Sopenharmony_ci
43318c2ecf20Sopenharmony_ci	rcu_read_lock();
43328c2ecf20Sopenharmony_ci
43338c2ecf20Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
43348c2ecf20Sopenharmony_ci		head = &net->ipv6.fib_table_hash[h];
43358c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(table, head, tb6_hlist) {
43368c2ecf20Sopenharmony_ci			if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
43378c2ecf20Sopenharmony_ci				__rt6_purge_dflt_routers(net, table);
43388c2ecf20Sopenharmony_ci		}
43398c2ecf20Sopenharmony_ci	}
43408c2ecf20Sopenharmony_ci
43418c2ecf20Sopenharmony_ci	rcu_read_unlock();
43428c2ecf20Sopenharmony_ci}
43438c2ecf20Sopenharmony_ci
43448c2ecf20Sopenharmony_cistatic void rtmsg_to_fib6_config(struct net *net,
43458c2ecf20Sopenharmony_ci				 struct in6_rtmsg *rtmsg,
43468c2ecf20Sopenharmony_ci				 struct fib6_config *cfg)
43478c2ecf20Sopenharmony_ci{
43488c2ecf20Sopenharmony_ci	*cfg = (struct fib6_config){
43498c2ecf20Sopenharmony_ci		.fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
43508c2ecf20Sopenharmony_ci			 : RT6_TABLE_MAIN,
43518c2ecf20Sopenharmony_ci		.fc_ifindex = rtmsg->rtmsg_ifindex,
43528c2ecf20Sopenharmony_ci		.fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
43538c2ecf20Sopenharmony_ci		.fc_expires = rtmsg->rtmsg_info,
43548c2ecf20Sopenharmony_ci		.fc_dst_len = rtmsg->rtmsg_dst_len,
43558c2ecf20Sopenharmony_ci		.fc_src_len = rtmsg->rtmsg_src_len,
43568c2ecf20Sopenharmony_ci		.fc_flags = rtmsg->rtmsg_flags,
43578c2ecf20Sopenharmony_ci		.fc_type = rtmsg->rtmsg_type,
43588c2ecf20Sopenharmony_ci
43598c2ecf20Sopenharmony_ci		.fc_nlinfo.nl_net = net,
43608c2ecf20Sopenharmony_ci
43618c2ecf20Sopenharmony_ci		.fc_dst = rtmsg->rtmsg_dst,
43628c2ecf20Sopenharmony_ci		.fc_src = rtmsg->rtmsg_src,
43638c2ecf20Sopenharmony_ci		.fc_gateway = rtmsg->rtmsg_gateway,
43648c2ecf20Sopenharmony_ci	};
43658c2ecf20Sopenharmony_ci}
43668c2ecf20Sopenharmony_ci
43678c2ecf20Sopenharmony_ciint ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg)
43688c2ecf20Sopenharmony_ci{
43698c2ecf20Sopenharmony_ci	struct fib6_config cfg;
43708c2ecf20Sopenharmony_ci	int err;
43718c2ecf20Sopenharmony_ci
43728c2ecf20Sopenharmony_ci	if (cmd != SIOCADDRT && cmd != SIOCDELRT)
43738c2ecf20Sopenharmony_ci		return -EINVAL;
43748c2ecf20Sopenharmony_ci	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
43758c2ecf20Sopenharmony_ci		return -EPERM;
43768c2ecf20Sopenharmony_ci
43778c2ecf20Sopenharmony_ci	rtmsg_to_fib6_config(net, rtmsg, &cfg);
43788c2ecf20Sopenharmony_ci
43798c2ecf20Sopenharmony_ci	rtnl_lock();
43808c2ecf20Sopenharmony_ci	switch (cmd) {
43818c2ecf20Sopenharmony_ci	case SIOCADDRT:
43828c2ecf20Sopenharmony_ci		err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
43838c2ecf20Sopenharmony_ci		break;
43848c2ecf20Sopenharmony_ci	case SIOCDELRT:
43858c2ecf20Sopenharmony_ci		err = ip6_route_del(&cfg, NULL);
43868c2ecf20Sopenharmony_ci		break;
43878c2ecf20Sopenharmony_ci	}
43888c2ecf20Sopenharmony_ci	rtnl_unlock();
43898c2ecf20Sopenharmony_ci	return err;
43908c2ecf20Sopenharmony_ci}
43918c2ecf20Sopenharmony_ci
43928c2ecf20Sopenharmony_ci/*
43938c2ecf20Sopenharmony_ci *	Drop the packet on the floor
43948c2ecf20Sopenharmony_ci */
43958c2ecf20Sopenharmony_ci
43968c2ecf20Sopenharmony_cistatic int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
43978c2ecf20Sopenharmony_ci{
43988c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
43998c2ecf20Sopenharmony_ci	struct net *net = dev_net(dst->dev);
44008c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
44018c2ecf20Sopenharmony_ci	int type;
44028c2ecf20Sopenharmony_ci
44038c2ecf20Sopenharmony_ci	if (netif_is_l3_master(skb->dev) ||
44048c2ecf20Sopenharmony_ci	    dst->dev == net->loopback_dev)
44058c2ecf20Sopenharmony_ci		idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
44068c2ecf20Sopenharmony_ci	else
44078c2ecf20Sopenharmony_ci		idev = ip6_dst_idev(dst);
44088c2ecf20Sopenharmony_ci
44098c2ecf20Sopenharmony_ci	switch (ipstats_mib_noroutes) {
44108c2ecf20Sopenharmony_ci	case IPSTATS_MIB_INNOROUTES:
44118c2ecf20Sopenharmony_ci		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
44128c2ecf20Sopenharmony_ci		if (type == IPV6_ADDR_ANY) {
44138c2ecf20Sopenharmony_ci			IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
44148c2ecf20Sopenharmony_ci			break;
44158c2ecf20Sopenharmony_ci		}
44168c2ecf20Sopenharmony_ci		fallthrough;
44178c2ecf20Sopenharmony_ci	case IPSTATS_MIB_OUTNOROUTES:
44188c2ecf20Sopenharmony_ci		IP6_INC_STATS(net, idev, ipstats_mib_noroutes);
44198c2ecf20Sopenharmony_ci		break;
44208c2ecf20Sopenharmony_ci	}
44218c2ecf20Sopenharmony_ci
44228c2ecf20Sopenharmony_ci	/* Start over by dropping the dst for l3mdev case */
44238c2ecf20Sopenharmony_ci	if (netif_is_l3_master(skb->dev))
44248c2ecf20Sopenharmony_ci		skb_dst_drop(skb);
44258c2ecf20Sopenharmony_ci
44268c2ecf20Sopenharmony_ci	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
44278c2ecf20Sopenharmony_ci	kfree_skb(skb);
44288c2ecf20Sopenharmony_ci	return 0;
44298c2ecf20Sopenharmony_ci}
44308c2ecf20Sopenharmony_ci
44318c2ecf20Sopenharmony_cistatic int ip6_pkt_discard(struct sk_buff *skb)
44328c2ecf20Sopenharmony_ci{
44338c2ecf20Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
44348c2ecf20Sopenharmony_ci}
44358c2ecf20Sopenharmony_ci
44368c2ecf20Sopenharmony_cistatic int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
44378c2ecf20Sopenharmony_ci{
44388c2ecf20Sopenharmony_ci	skb->dev = skb_dst(skb)->dev;
44398c2ecf20Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
44408c2ecf20Sopenharmony_ci}
44418c2ecf20Sopenharmony_ci
44428c2ecf20Sopenharmony_cistatic int ip6_pkt_prohibit(struct sk_buff *skb)
44438c2ecf20Sopenharmony_ci{
44448c2ecf20Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
44458c2ecf20Sopenharmony_ci}
44468c2ecf20Sopenharmony_ci
44478c2ecf20Sopenharmony_cistatic int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
44488c2ecf20Sopenharmony_ci{
44498c2ecf20Sopenharmony_ci	skb->dev = skb_dst(skb)->dev;
44508c2ecf20Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
44518c2ecf20Sopenharmony_ci}
44528c2ecf20Sopenharmony_ci
44538c2ecf20Sopenharmony_ci/*
44548c2ecf20Sopenharmony_ci *	Allocate a dst for local (unicast / anycast) address.
44558c2ecf20Sopenharmony_ci */
44568c2ecf20Sopenharmony_ci
44578c2ecf20Sopenharmony_cistruct fib6_info *addrconf_f6i_alloc(struct net *net,
44588c2ecf20Sopenharmony_ci				     struct inet6_dev *idev,
44598c2ecf20Sopenharmony_ci				     const struct in6_addr *addr,
44608c2ecf20Sopenharmony_ci				     bool anycast, gfp_t gfp_flags)
44618c2ecf20Sopenharmony_ci{
44628c2ecf20Sopenharmony_ci	struct fib6_config cfg = {
44638c2ecf20Sopenharmony_ci		.fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
44648c2ecf20Sopenharmony_ci		.fc_ifindex = idev->dev->ifindex,
44658c2ecf20Sopenharmony_ci		.fc_flags = RTF_UP | RTF_NONEXTHOP,
44668c2ecf20Sopenharmony_ci		.fc_dst = *addr,
44678c2ecf20Sopenharmony_ci		.fc_dst_len = 128,
44688c2ecf20Sopenharmony_ci		.fc_protocol = RTPROT_KERNEL,
44698c2ecf20Sopenharmony_ci		.fc_nlinfo.nl_net = net,
44708c2ecf20Sopenharmony_ci		.fc_ignore_dev_down = true,
44718c2ecf20Sopenharmony_ci	};
44728c2ecf20Sopenharmony_ci	struct fib6_info *f6i;
44738c2ecf20Sopenharmony_ci
44748c2ecf20Sopenharmony_ci	if (anycast) {
44758c2ecf20Sopenharmony_ci		cfg.fc_type = RTN_ANYCAST;
44768c2ecf20Sopenharmony_ci		cfg.fc_flags |= RTF_ANYCAST;
44778c2ecf20Sopenharmony_ci	} else {
44788c2ecf20Sopenharmony_ci		cfg.fc_type = RTN_LOCAL;
44798c2ecf20Sopenharmony_ci		cfg.fc_flags |= RTF_LOCAL;
44808c2ecf20Sopenharmony_ci	}
44818c2ecf20Sopenharmony_ci
44828c2ecf20Sopenharmony_ci	f6i = ip6_route_info_create(&cfg, gfp_flags, NULL);
44838c2ecf20Sopenharmony_ci	if (!IS_ERR(f6i)) {
44848c2ecf20Sopenharmony_ci		f6i->dst_nocount = true;
44858c2ecf20Sopenharmony_ci
44868c2ecf20Sopenharmony_ci		if (!anycast &&
44878c2ecf20Sopenharmony_ci		    (net->ipv6.devconf_all->disable_policy ||
44888c2ecf20Sopenharmony_ci		     idev->cnf.disable_policy))
44898c2ecf20Sopenharmony_ci			f6i->dst_nopolicy = true;
44908c2ecf20Sopenharmony_ci	}
44918c2ecf20Sopenharmony_ci
44928c2ecf20Sopenharmony_ci	return f6i;
44938c2ecf20Sopenharmony_ci}
44948c2ecf20Sopenharmony_ci
44958c2ecf20Sopenharmony_ci/* remove deleted ip from prefsrc entries */
44968c2ecf20Sopenharmony_cistruct arg_dev_net_ip {
44978c2ecf20Sopenharmony_ci	struct net_device *dev;
44988c2ecf20Sopenharmony_ci	struct net *net;
44998c2ecf20Sopenharmony_ci	struct in6_addr *addr;
45008c2ecf20Sopenharmony_ci};
45018c2ecf20Sopenharmony_ci
45028c2ecf20Sopenharmony_cistatic int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
45038c2ecf20Sopenharmony_ci{
45048c2ecf20Sopenharmony_ci	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
45058c2ecf20Sopenharmony_ci	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
45068c2ecf20Sopenharmony_ci	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
45078c2ecf20Sopenharmony_ci
45088c2ecf20Sopenharmony_ci	if (!rt->nh &&
45098c2ecf20Sopenharmony_ci	    ((void *)rt->fib6_nh->fib_nh_dev == dev || !dev) &&
45108c2ecf20Sopenharmony_ci	    rt != net->ipv6.fib6_null_entry &&
45118c2ecf20Sopenharmony_ci	    ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
45128c2ecf20Sopenharmony_ci		spin_lock_bh(&rt6_exception_lock);
45138c2ecf20Sopenharmony_ci		/* remove prefsrc entry */
45148c2ecf20Sopenharmony_ci		rt->fib6_prefsrc.plen = 0;
45158c2ecf20Sopenharmony_ci		spin_unlock_bh(&rt6_exception_lock);
45168c2ecf20Sopenharmony_ci	}
45178c2ecf20Sopenharmony_ci	return 0;
45188c2ecf20Sopenharmony_ci}
45198c2ecf20Sopenharmony_ci
45208c2ecf20Sopenharmony_civoid rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
45218c2ecf20Sopenharmony_ci{
45228c2ecf20Sopenharmony_ci	struct net *net = dev_net(ifp->idev->dev);
45238c2ecf20Sopenharmony_ci	struct arg_dev_net_ip adni = {
45248c2ecf20Sopenharmony_ci		.dev = ifp->idev->dev,
45258c2ecf20Sopenharmony_ci		.net = net,
45268c2ecf20Sopenharmony_ci		.addr = &ifp->addr,
45278c2ecf20Sopenharmony_ci	};
45288c2ecf20Sopenharmony_ci	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
45298c2ecf20Sopenharmony_ci}
45308c2ecf20Sopenharmony_ci
45318c2ecf20Sopenharmony_ci#define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT)
45328c2ecf20Sopenharmony_ci
45338c2ecf20Sopenharmony_ci/* Remove routers and update dst entries when gateway turn into host. */
45348c2ecf20Sopenharmony_cistatic int fib6_clean_tohost(struct fib6_info *rt, void *arg)
45358c2ecf20Sopenharmony_ci{
45368c2ecf20Sopenharmony_ci	struct in6_addr *gateway = (struct in6_addr *)arg;
45378c2ecf20Sopenharmony_ci	struct fib6_nh *nh;
45388c2ecf20Sopenharmony_ci
45398c2ecf20Sopenharmony_ci	/* RA routes do not use nexthops */
45408c2ecf20Sopenharmony_ci	if (rt->nh)
45418c2ecf20Sopenharmony_ci		return 0;
45428c2ecf20Sopenharmony_ci
45438c2ecf20Sopenharmony_ci	nh = rt->fib6_nh;
45448c2ecf20Sopenharmony_ci	if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
45458c2ecf20Sopenharmony_ci	    nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6))
45468c2ecf20Sopenharmony_ci		return -1;
45478c2ecf20Sopenharmony_ci
45488c2ecf20Sopenharmony_ci	/* Further clean up cached routes in exception table.
45498c2ecf20Sopenharmony_ci	 * This is needed because cached route may have a different
45508c2ecf20Sopenharmony_ci	 * gateway than its 'parent' in the case of an ip redirect.
45518c2ecf20Sopenharmony_ci	 */
45528c2ecf20Sopenharmony_ci	fib6_nh_exceptions_clean_tohost(nh, gateway);
45538c2ecf20Sopenharmony_ci
45548c2ecf20Sopenharmony_ci	return 0;
45558c2ecf20Sopenharmony_ci}
45568c2ecf20Sopenharmony_ci
45578c2ecf20Sopenharmony_civoid rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
45588c2ecf20Sopenharmony_ci{
45598c2ecf20Sopenharmony_ci	fib6_clean_all(net, fib6_clean_tohost, gateway);
45608c2ecf20Sopenharmony_ci}
45618c2ecf20Sopenharmony_ci
45628c2ecf20Sopenharmony_cistruct arg_netdev_event {
45638c2ecf20Sopenharmony_ci	const struct net_device *dev;
45648c2ecf20Sopenharmony_ci	union {
45658c2ecf20Sopenharmony_ci		unsigned char nh_flags;
45668c2ecf20Sopenharmony_ci		unsigned long event;
45678c2ecf20Sopenharmony_ci	};
45688c2ecf20Sopenharmony_ci};
45698c2ecf20Sopenharmony_ci
45708c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
45718c2ecf20Sopenharmony_ci{
45728c2ecf20Sopenharmony_ci	struct fib6_info *iter;
45738c2ecf20Sopenharmony_ci	struct fib6_node *fn;
45748c2ecf20Sopenharmony_ci
45758c2ecf20Sopenharmony_ci	fn = rcu_dereference_protected(rt->fib6_node,
45768c2ecf20Sopenharmony_ci			lockdep_is_held(&rt->fib6_table->tb6_lock));
45778c2ecf20Sopenharmony_ci	iter = rcu_dereference_protected(fn->leaf,
45788c2ecf20Sopenharmony_ci			lockdep_is_held(&rt->fib6_table->tb6_lock));
45798c2ecf20Sopenharmony_ci	while (iter) {
45808c2ecf20Sopenharmony_ci		if (iter->fib6_metric == rt->fib6_metric &&
45818c2ecf20Sopenharmony_ci		    rt6_qualify_for_ecmp(iter))
45828c2ecf20Sopenharmony_ci			return iter;
45838c2ecf20Sopenharmony_ci		iter = rcu_dereference_protected(iter->fib6_next,
45848c2ecf20Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock));
45858c2ecf20Sopenharmony_ci	}
45868c2ecf20Sopenharmony_ci
45878c2ecf20Sopenharmony_ci	return NULL;
45888c2ecf20Sopenharmony_ci}
45898c2ecf20Sopenharmony_ci
45908c2ecf20Sopenharmony_ci/* only called for fib entries with builtin fib6_nh */
45918c2ecf20Sopenharmony_cistatic bool rt6_is_dead(const struct fib6_info *rt)
45928c2ecf20Sopenharmony_ci{
45938c2ecf20Sopenharmony_ci	if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD ||
45948c2ecf20Sopenharmony_ci	    (rt->fib6_nh->fib_nh_flags & RTNH_F_LINKDOWN &&
45958c2ecf20Sopenharmony_ci	     ip6_ignore_linkdown(rt->fib6_nh->fib_nh_dev)))
45968c2ecf20Sopenharmony_ci		return true;
45978c2ecf20Sopenharmony_ci
45988c2ecf20Sopenharmony_ci	return false;
45998c2ecf20Sopenharmony_ci}
46008c2ecf20Sopenharmony_ci
46018c2ecf20Sopenharmony_cistatic int rt6_multipath_total_weight(const struct fib6_info *rt)
46028c2ecf20Sopenharmony_ci{
46038c2ecf20Sopenharmony_ci	struct fib6_info *iter;
46048c2ecf20Sopenharmony_ci	int total = 0;
46058c2ecf20Sopenharmony_ci
46068c2ecf20Sopenharmony_ci	if (!rt6_is_dead(rt))
46078c2ecf20Sopenharmony_ci		total += rt->fib6_nh->fib_nh_weight;
46088c2ecf20Sopenharmony_ci
46098c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
46108c2ecf20Sopenharmony_ci		if (!rt6_is_dead(iter))
46118c2ecf20Sopenharmony_ci			total += iter->fib6_nh->fib_nh_weight;
46128c2ecf20Sopenharmony_ci	}
46138c2ecf20Sopenharmony_ci
46148c2ecf20Sopenharmony_ci	return total;
46158c2ecf20Sopenharmony_ci}
46168c2ecf20Sopenharmony_ci
46178c2ecf20Sopenharmony_cistatic void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
46188c2ecf20Sopenharmony_ci{
46198c2ecf20Sopenharmony_ci	int upper_bound = -1;
46208c2ecf20Sopenharmony_ci
46218c2ecf20Sopenharmony_ci	if (!rt6_is_dead(rt)) {
46228c2ecf20Sopenharmony_ci		*weight += rt->fib6_nh->fib_nh_weight;
46238c2ecf20Sopenharmony_ci		upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
46248c2ecf20Sopenharmony_ci						    total) - 1;
46258c2ecf20Sopenharmony_ci	}
46268c2ecf20Sopenharmony_ci	atomic_set(&rt->fib6_nh->fib_nh_upper_bound, upper_bound);
46278c2ecf20Sopenharmony_ci}
46288c2ecf20Sopenharmony_ci
46298c2ecf20Sopenharmony_cistatic void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
46308c2ecf20Sopenharmony_ci{
46318c2ecf20Sopenharmony_ci	struct fib6_info *iter;
46328c2ecf20Sopenharmony_ci	int weight = 0;
46338c2ecf20Sopenharmony_ci
46348c2ecf20Sopenharmony_ci	rt6_upper_bound_set(rt, &weight, total);
46358c2ecf20Sopenharmony_ci
46368c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
46378c2ecf20Sopenharmony_ci		rt6_upper_bound_set(iter, &weight, total);
46388c2ecf20Sopenharmony_ci}
46398c2ecf20Sopenharmony_ci
46408c2ecf20Sopenharmony_civoid rt6_multipath_rebalance(struct fib6_info *rt)
46418c2ecf20Sopenharmony_ci{
46428c2ecf20Sopenharmony_ci	struct fib6_info *first;
46438c2ecf20Sopenharmony_ci	int total;
46448c2ecf20Sopenharmony_ci
46458c2ecf20Sopenharmony_ci	/* In case the entire multipath route was marked for flushing,
46468c2ecf20Sopenharmony_ci	 * then there is no need to rebalance upon the removal of every
46478c2ecf20Sopenharmony_ci	 * sibling route.
46488c2ecf20Sopenharmony_ci	 */
46498c2ecf20Sopenharmony_ci	if (!rt->fib6_nsiblings || rt->should_flush)
46508c2ecf20Sopenharmony_ci		return;
46518c2ecf20Sopenharmony_ci
46528c2ecf20Sopenharmony_ci	/* During lookup routes are evaluated in order, so we need to
46538c2ecf20Sopenharmony_ci	 * make sure upper bounds are assigned from the first sibling
46548c2ecf20Sopenharmony_ci	 * onwards.
46558c2ecf20Sopenharmony_ci	 */
46568c2ecf20Sopenharmony_ci	first = rt6_multipath_first_sibling(rt);
46578c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!first))
46588c2ecf20Sopenharmony_ci		return;
46598c2ecf20Sopenharmony_ci
46608c2ecf20Sopenharmony_ci	total = rt6_multipath_total_weight(first);
46618c2ecf20Sopenharmony_ci	rt6_multipath_upper_bound_set(first, total);
46628c2ecf20Sopenharmony_ci}
46638c2ecf20Sopenharmony_ci
46648c2ecf20Sopenharmony_cistatic int fib6_ifup(struct fib6_info *rt, void *p_arg)
46658c2ecf20Sopenharmony_ci{
46668c2ecf20Sopenharmony_ci	const struct arg_netdev_event *arg = p_arg;
46678c2ecf20Sopenharmony_ci	struct net *net = dev_net(arg->dev);
46688c2ecf20Sopenharmony_ci
46698c2ecf20Sopenharmony_ci	if (rt != net->ipv6.fib6_null_entry && !rt->nh &&
46708c2ecf20Sopenharmony_ci	    rt->fib6_nh->fib_nh_dev == arg->dev) {
46718c2ecf20Sopenharmony_ci		rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags;
46728c2ecf20Sopenharmony_ci		fib6_update_sernum_upto_root(net, rt);
46738c2ecf20Sopenharmony_ci		rt6_multipath_rebalance(rt);
46748c2ecf20Sopenharmony_ci	}
46758c2ecf20Sopenharmony_ci
46768c2ecf20Sopenharmony_ci	return 0;
46778c2ecf20Sopenharmony_ci}
46788c2ecf20Sopenharmony_ci
46798c2ecf20Sopenharmony_civoid rt6_sync_up(struct net_device *dev, unsigned char nh_flags)
46808c2ecf20Sopenharmony_ci{
46818c2ecf20Sopenharmony_ci	struct arg_netdev_event arg = {
46828c2ecf20Sopenharmony_ci		.dev = dev,
46838c2ecf20Sopenharmony_ci		{
46848c2ecf20Sopenharmony_ci			.nh_flags = nh_flags,
46858c2ecf20Sopenharmony_ci		},
46868c2ecf20Sopenharmony_ci	};
46878c2ecf20Sopenharmony_ci
46888c2ecf20Sopenharmony_ci	if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
46898c2ecf20Sopenharmony_ci		arg.nh_flags |= RTNH_F_LINKDOWN;
46908c2ecf20Sopenharmony_ci
46918c2ecf20Sopenharmony_ci	fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
46928c2ecf20Sopenharmony_ci}
46938c2ecf20Sopenharmony_ci
46948c2ecf20Sopenharmony_ci/* only called for fib entries with inline fib6_nh */
46958c2ecf20Sopenharmony_cistatic bool rt6_multipath_uses_dev(const struct fib6_info *rt,
46968c2ecf20Sopenharmony_ci				   const struct net_device *dev)
46978c2ecf20Sopenharmony_ci{
46988c2ecf20Sopenharmony_ci	struct fib6_info *iter;
46998c2ecf20Sopenharmony_ci
47008c2ecf20Sopenharmony_ci	if (rt->fib6_nh->fib_nh_dev == dev)
47018c2ecf20Sopenharmony_ci		return true;
47028c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
47038c2ecf20Sopenharmony_ci		if (iter->fib6_nh->fib_nh_dev == dev)
47048c2ecf20Sopenharmony_ci			return true;
47058c2ecf20Sopenharmony_ci
47068c2ecf20Sopenharmony_ci	return false;
47078c2ecf20Sopenharmony_ci}
47088c2ecf20Sopenharmony_ci
47098c2ecf20Sopenharmony_cistatic void rt6_multipath_flush(struct fib6_info *rt)
47108c2ecf20Sopenharmony_ci{
47118c2ecf20Sopenharmony_ci	struct fib6_info *iter;
47128c2ecf20Sopenharmony_ci
47138c2ecf20Sopenharmony_ci	rt->should_flush = 1;
47148c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
47158c2ecf20Sopenharmony_ci		iter->should_flush = 1;
47168c2ecf20Sopenharmony_ci}
47178c2ecf20Sopenharmony_ci
47188c2ecf20Sopenharmony_cistatic unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
47198c2ecf20Sopenharmony_ci					     const struct net_device *down_dev)
47208c2ecf20Sopenharmony_ci{
47218c2ecf20Sopenharmony_ci	struct fib6_info *iter;
47228c2ecf20Sopenharmony_ci	unsigned int dead = 0;
47238c2ecf20Sopenharmony_ci
47248c2ecf20Sopenharmony_ci	if (rt->fib6_nh->fib_nh_dev == down_dev ||
47258c2ecf20Sopenharmony_ci	    rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
47268c2ecf20Sopenharmony_ci		dead++;
47278c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
47288c2ecf20Sopenharmony_ci		if (iter->fib6_nh->fib_nh_dev == down_dev ||
47298c2ecf20Sopenharmony_ci		    iter->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
47308c2ecf20Sopenharmony_ci			dead++;
47318c2ecf20Sopenharmony_ci
47328c2ecf20Sopenharmony_ci	return dead;
47338c2ecf20Sopenharmony_ci}
47348c2ecf20Sopenharmony_ci
47358c2ecf20Sopenharmony_cistatic void rt6_multipath_nh_flags_set(struct fib6_info *rt,
47368c2ecf20Sopenharmony_ci				       const struct net_device *dev,
47378c2ecf20Sopenharmony_ci				       unsigned char nh_flags)
47388c2ecf20Sopenharmony_ci{
47398c2ecf20Sopenharmony_ci	struct fib6_info *iter;
47408c2ecf20Sopenharmony_ci
47418c2ecf20Sopenharmony_ci	if (rt->fib6_nh->fib_nh_dev == dev)
47428c2ecf20Sopenharmony_ci		rt->fib6_nh->fib_nh_flags |= nh_flags;
47438c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
47448c2ecf20Sopenharmony_ci		if (iter->fib6_nh->fib_nh_dev == dev)
47458c2ecf20Sopenharmony_ci			iter->fib6_nh->fib_nh_flags |= nh_flags;
47468c2ecf20Sopenharmony_ci}
47478c2ecf20Sopenharmony_ci
47488c2ecf20Sopenharmony_ci/* called with write lock held for table with rt */
47498c2ecf20Sopenharmony_cistatic int fib6_ifdown(struct fib6_info *rt, void *p_arg)
47508c2ecf20Sopenharmony_ci{
47518c2ecf20Sopenharmony_ci	const struct arg_netdev_event *arg = p_arg;
47528c2ecf20Sopenharmony_ci	const struct net_device *dev = arg->dev;
47538c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
47548c2ecf20Sopenharmony_ci
47558c2ecf20Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry || rt->nh)
47568c2ecf20Sopenharmony_ci		return 0;
47578c2ecf20Sopenharmony_ci
47588c2ecf20Sopenharmony_ci	switch (arg->event) {
47598c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
47608c2ecf20Sopenharmony_ci		return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
47618c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
47628c2ecf20Sopenharmony_ci		if (rt->should_flush)
47638c2ecf20Sopenharmony_ci			return -1;
47648c2ecf20Sopenharmony_ci		if (!rt->fib6_nsiblings)
47658c2ecf20Sopenharmony_ci			return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
47668c2ecf20Sopenharmony_ci		if (rt6_multipath_uses_dev(rt, dev)) {
47678c2ecf20Sopenharmony_ci			unsigned int count;
47688c2ecf20Sopenharmony_ci
47698c2ecf20Sopenharmony_ci			count = rt6_multipath_dead_count(rt, dev);
47708c2ecf20Sopenharmony_ci			if (rt->fib6_nsiblings + 1 == count) {
47718c2ecf20Sopenharmony_ci				rt6_multipath_flush(rt);
47728c2ecf20Sopenharmony_ci				return -1;
47738c2ecf20Sopenharmony_ci			}
47748c2ecf20Sopenharmony_ci			rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
47758c2ecf20Sopenharmony_ci						   RTNH_F_LINKDOWN);
47768c2ecf20Sopenharmony_ci			fib6_update_sernum(net, rt);
47778c2ecf20Sopenharmony_ci			rt6_multipath_rebalance(rt);
47788c2ecf20Sopenharmony_ci		}
47798c2ecf20Sopenharmony_ci		return -2;
47808c2ecf20Sopenharmony_ci	case NETDEV_CHANGE:
47818c2ecf20Sopenharmony_ci		if (rt->fib6_nh->fib_nh_dev != dev ||
47828c2ecf20Sopenharmony_ci		    rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
47838c2ecf20Sopenharmony_ci			break;
47848c2ecf20Sopenharmony_ci		rt->fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
47858c2ecf20Sopenharmony_ci		rt6_multipath_rebalance(rt);
47868c2ecf20Sopenharmony_ci		break;
47878c2ecf20Sopenharmony_ci	}
47888c2ecf20Sopenharmony_ci
47898c2ecf20Sopenharmony_ci	return 0;
47908c2ecf20Sopenharmony_ci}
47918c2ecf20Sopenharmony_ci
47928c2ecf20Sopenharmony_civoid rt6_sync_down_dev(struct net_device *dev, unsigned long event)
47938c2ecf20Sopenharmony_ci{
47948c2ecf20Sopenharmony_ci	struct arg_netdev_event arg = {
47958c2ecf20Sopenharmony_ci		.dev = dev,
47968c2ecf20Sopenharmony_ci		{
47978c2ecf20Sopenharmony_ci			.event = event,
47988c2ecf20Sopenharmony_ci		},
47998c2ecf20Sopenharmony_ci	};
48008c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
48018c2ecf20Sopenharmony_ci
48028c2ecf20Sopenharmony_ci	if (net->ipv6.sysctl.skip_notify_on_dev_down)
48038c2ecf20Sopenharmony_ci		fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
48048c2ecf20Sopenharmony_ci	else
48058c2ecf20Sopenharmony_ci		fib6_clean_all(net, fib6_ifdown, &arg);
48068c2ecf20Sopenharmony_ci}
48078c2ecf20Sopenharmony_ci
48088c2ecf20Sopenharmony_civoid rt6_disable_ip(struct net_device *dev, unsigned long event)
48098c2ecf20Sopenharmony_ci{
48108c2ecf20Sopenharmony_ci	rt6_sync_down_dev(dev, event);
48118c2ecf20Sopenharmony_ci	rt6_uncached_list_flush_dev(dev_net(dev), dev);
48128c2ecf20Sopenharmony_ci	neigh_ifdown(&nd_tbl, dev);
48138c2ecf20Sopenharmony_ci}
48148c2ecf20Sopenharmony_ci
48158c2ecf20Sopenharmony_cistruct rt6_mtu_change_arg {
48168c2ecf20Sopenharmony_ci	struct net_device *dev;
48178c2ecf20Sopenharmony_ci	unsigned int mtu;
48188c2ecf20Sopenharmony_ci	struct fib6_info *f6i;
48198c2ecf20Sopenharmony_ci};
48208c2ecf20Sopenharmony_ci
48218c2ecf20Sopenharmony_cistatic int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg)
48228c2ecf20Sopenharmony_ci{
48238c2ecf20Sopenharmony_ci	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *)_arg;
48248c2ecf20Sopenharmony_ci	struct fib6_info *f6i = arg->f6i;
48258c2ecf20Sopenharmony_ci
48268c2ecf20Sopenharmony_ci	/* For administrative MTU increase, there is no way to discover
48278c2ecf20Sopenharmony_ci	 * IPv6 PMTU increase, so PMTU increase should be updated here.
48288c2ecf20Sopenharmony_ci	 * Since RFC 1981 doesn't include administrative MTU increase
48298c2ecf20Sopenharmony_ci	 * update PMTU increase is a MUST. (i.e. jumbo frame)
48308c2ecf20Sopenharmony_ci	 */
48318c2ecf20Sopenharmony_ci	if (nh->fib_nh_dev == arg->dev) {
48328c2ecf20Sopenharmony_ci		struct inet6_dev *idev = __in6_dev_get(arg->dev);
48338c2ecf20Sopenharmony_ci		u32 mtu = f6i->fib6_pmtu;
48348c2ecf20Sopenharmony_ci
48358c2ecf20Sopenharmony_ci		if (mtu >= arg->mtu ||
48368c2ecf20Sopenharmony_ci		    (mtu < arg->mtu && mtu == idev->cnf.mtu6))
48378c2ecf20Sopenharmony_ci			fib6_metric_set(f6i, RTAX_MTU, arg->mtu);
48388c2ecf20Sopenharmony_ci
48398c2ecf20Sopenharmony_ci		spin_lock_bh(&rt6_exception_lock);
48408c2ecf20Sopenharmony_ci		rt6_exceptions_update_pmtu(idev, nh, arg->mtu);
48418c2ecf20Sopenharmony_ci		spin_unlock_bh(&rt6_exception_lock);
48428c2ecf20Sopenharmony_ci	}
48438c2ecf20Sopenharmony_ci
48448c2ecf20Sopenharmony_ci	return 0;
48458c2ecf20Sopenharmony_ci}
48468c2ecf20Sopenharmony_ci
48478c2ecf20Sopenharmony_cistatic int rt6_mtu_change_route(struct fib6_info *f6i, void *p_arg)
48488c2ecf20Sopenharmony_ci{
48498c2ecf20Sopenharmony_ci	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
48508c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
48518c2ecf20Sopenharmony_ci
48528c2ecf20Sopenharmony_ci	/* In IPv6 pmtu discovery is not optional,
48538c2ecf20Sopenharmony_ci	   so that RTAX_MTU lock cannot disable it.
48548c2ecf20Sopenharmony_ci	   We still use this lock to block changes
48558c2ecf20Sopenharmony_ci	   caused by addrconf/ndisc.
48568c2ecf20Sopenharmony_ci	*/
48578c2ecf20Sopenharmony_ci
48588c2ecf20Sopenharmony_ci	idev = __in6_dev_get(arg->dev);
48598c2ecf20Sopenharmony_ci	if (!idev)
48608c2ecf20Sopenharmony_ci		return 0;
48618c2ecf20Sopenharmony_ci
48628c2ecf20Sopenharmony_ci	if (fib6_metric_locked(f6i, RTAX_MTU))
48638c2ecf20Sopenharmony_ci		return 0;
48648c2ecf20Sopenharmony_ci
48658c2ecf20Sopenharmony_ci	arg->f6i = f6i;
48668c2ecf20Sopenharmony_ci	if (f6i->nh) {
48678c2ecf20Sopenharmony_ci		/* fib6_nh_mtu_change only returns 0, so this is safe */
48688c2ecf20Sopenharmony_ci		return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_mtu_change,
48698c2ecf20Sopenharmony_ci						arg);
48708c2ecf20Sopenharmony_ci	}
48718c2ecf20Sopenharmony_ci
48728c2ecf20Sopenharmony_ci	return fib6_nh_mtu_change(f6i->fib6_nh, arg);
48738c2ecf20Sopenharmony_ci}
48748c2ecf20Sopenharmony_ci
48758c2ecf20Sopenharmony_civoid rt6_mtu_change(struct net_device *dev, unsigned int mtu)
48768c2ecf20Sopenharmony_ci{
48778c2ecf20Sopenharmony_ci	struct rt6_mtu_change_arg arg = {
48788c2ecf20Sopenharmony_ci		.dev = dev,
48798c2ecf20Sopenharmony_ci		.mtu = mtu,
48808c2ecf20Sopenharmony_ci	};
48818c2ecf20Sopenharmony_ci
48828c2ecf20Sopenharmony_ci	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
48838c2ecf20Sopenharmony_ci}
48848c2ecf20Sopenharmony_ci
48858c2ecf20Sopenharmony_cistatic const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
48868c2ecf20Sopenharmony_ci	[RTA_UNSPEC]		= { .strict_start_type = RTA_DPORT + 1 },
48878c2ecf20Sopenharmony_ci	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
48888c2ecf20Sopenharmony_ci	[RTA_PREFSRC]		= { .len = sizeof(struct in6_addr) },
48898c2ecf20Sopenharmony_ci	[RTA_OIF]               = { .type = NLA_U32 },
48908c2ecf20Sopenharmony_ci	[RTA_IIF]		= { .type = NLA_U32 },
48918c2ecf20Sopenharmony_ci	[RTA_PRIORITY]          = { .type = NLA_U32 },
48928c2ecf20Sopenharmony_ci	[RTA_METRICS]           = { .type = NLA_NESTED },
48938c2ecf20Sopenharmony_ci	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
48948c2ecf20Sopenharmony_ci	[RTA_PREF]              = { .type = NLA_U8 },
48958c2ecf20Sopenharmony_ci	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
48968c2ecf20Sopenharmony_ci	[RTA_ENCAP]		= { .type = NLA_NESTED },
48978c2ecf20Sopenharmony_ci	[RTA_EXPIRES]		= { .type = NLA_U32 },
48988c2ecf20Sopenharmony_ci	[RTA_UID]		= { .type = NLA_U32 },
48998c2ecf20Sopenharmony_ci	[RTA_MARK]		= { .type = NLA_U32 },
49008c2ecf20Sopenharmony_ci	[RTA_TABLE]		= { .type = NLA_U32 },
49018c2ecf20Sopenharmony_ci	[RTA_IP_PROTO]		= { .type = NLA_U8 },
49028c2ecf20Sopenharmony_ci	[RTA_SPORT]		= { .type = NLA_U16 },
49038c2ecf20Sopenharmony_ci	[RTA_DPORT]		= { .type = NLA_U16 },
49048c2ecf20Sopenharmony_ci	[RTA_NH_ID]		= { .type = NLA_U32 },
49058c2ecf20Sopenharmony_ci};
49068c2ecf20Sopenharmony_ci
49078c2ecf20Sopenharmony_cistatic int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
49088c2ecf20Sopenharmony_ci			      struct fib6_config *cfg,
49098c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
49108c2ecf20Sopenharmony_ci{
49118c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
49128c2ecf20Sopenharmony_ci	struct nlattr *tb[RTA_MAX+1];
49138c2ecf20Sopenharmony_ci	unsigned int pref;
49148c2ecf20Sopenharmony_ci	int err;
49158c2ecf20Sopenharmony_ci
49168c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
49178c2ecf20Sopenharmony_ci				     rtm_ipv6_policy, extack);
49188c2ecf20Sopenharmony_ci	if (err < 0)
49198c2ecf20Sopenharmony_ci		goto errout;
49208c2ecf20Sopenharmony_ci
49218c2ecf20Sopenharmony_ci	err = -EINVAL;
49228c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
49238c2ecf20Sopenharmony_ci
49248c2ecf20Sopenharmony_ci	*cfg = (struct fib6_config){
49258c2ecf20Sopenharmony_ci		.fc_table = rtm->rtm_table,
49268c2ecf20Sopenharmony_ci		.fc_dst_len = rtm->rtm_dst_len,
49278c2ecf20Sopenharmony_ci		.fc_src_len = rtm->rtm_src_len,
49288c2ecf20Sopenharmony_ci		.fc_flags = RTF_UP,
49298c2ecf20Sopenharmony_ci		.fc_protocol = rtm->rtm_protocol,
49308c2ecf20Sopenharmony_ci		.fc_type = rtm->rtm_type,
49318c2ecf20Sopenharmony_ci
49328c2ecf20Sopenharmony_ci		.fc_nlinfo.portid = NETLINK_CB(skb).portid,
49338c2ecf20Sopenharmony_ci		.fc_nlinfo.nlh = nlh,
49348c2ecf20Sopenharmony_ci		.fc_nlinfo.nl_net = sock_net(skb->sk),
49358c2ecf20Sopenharmony_ci	};
49368c2ecf20Sopenharmony_ci
49378c2ecf20Sopenharmony_ci	if (rtm->rtm_type == RTN_UNREACHABLE ||
49388c2ecf20Sopenharmony_ci	    rtm->rtm_type == RTN_BLACKHOLE ||
49398c2ecf20Sopenharmony_ci	    rtm->rtm_type == RTN_PROHIBIT ||
49408c2ecf20Sopenharmony_ci	    rtm->rtm_type == RTN_THROW)
49418c2ecf20Sopenharmony_ci		cfg->fc_flags |= RTF_REJECT;
49428c2ecf20Sopenharmony_ci
49438c2ecf20Sopenharmony_ci	if (rtm->rtm_type == RTN_LOCAL)
49448c2ecf20Sopenharmony_ci		cfg->fc_flags |= RTF_LOCAL;
49458c2ecf20Sopenharmony_ci
49468c2ecf20Sopenharmony_ci	if (rtm->rtm_flags & RTM_F_CLONED)
49478c2ecf20Sopenharmony_ci		cfg->fc_flags |= RTF_CACHE;
49488c2ecf20Sopenharmony_ci
49498c2ecf20Sopenharmony_ci	cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
49508c2ecf20Sopenharmony_ci
49518c2ecf20Sopenharmony_ci	if (tb[RTA_NH_ID]) {
49528c2ecf20Sopenharmony_ci		if (tb[RTA_GATEWAY]   || tb[RTA_OIF] ||
49538c2ecf20Sopenharmony_ci		    tb[RTA_MULTIPATH] || tb[RTA_ENCAP]) {
49548c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack,
49558c2ecf20Sopenharmony_ci				       "Nexthop specification and nexthop id are mutually exclusive");
49568c2ecf20Sopenharmony_ci			goto errout;
49578c2ecf20Sopenharmony_ci		}
49588c2ecf20Sopenharmony_ci		cfg->fc_nh_id = nla_get_u32(tb[RTA_NH_ID]);
49598c2ecf20Sopenharmony_ci	}
49608c2ecf20Sopenharmony_ci
49618c2ecf20Sopenharmony_ci	if (tb[RTA_GATEWAY]) {
49628c2ecf20Sopenharmony_ci		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
49638c2ecf20Sopenharmony_ci		cfg->fc_flags |= RTF_GATEWAY;
49648c2ecf20Sopenharmony_ci	}
49658c2ecf20Sopenharmony_ci	if (tb[RTA_VIA]) {
49668c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
49678c2ecf20Sopenharmony_ci		goto errout;
49688c2ecf20Sopenharmony_ci	}
49698c2ecf20Sopenharmony_ci
49708c2ecf20Sopenharmony_ci	if (tb[RTA_DST]) {
49718c2ecf20Sopenharmony_ci		int plen = (rtm->rtm_dst_len + 7) >> 3;
49728c2ecf20Sopenharmony_ci
49738c2ecf20Sopenharmony_ci		if (nla_len(tb[RTA_DST]) < plen)
49748c2ecf20Sopenharmony_ci			goto errout;
49758c2ecf20Sopenharmony_ci
49768c2ecf20Sopenharmony_ci		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
49778c2ecf20Sopenharmony_ci	}
49788c2ecf20Sopenharmony_ci
49798c2ecf20Sopenharmony_ci	if (tb[RTA_SRC]) {
49808c2ecf20Sopenharmony_ci		int plen = (rtm->rtm_src_len + 7) >> 3;
49818c2ecf20Sopenharmony_ci
49828c2ecf20Sopenharmony_ci		if (nla_len(tb[RTA_SRC]) < plen)
49838c2ecf20Sopenharmony_ci			goto errout;
49848c2ecf20Sopenharmony_ci
49858c2ecf20Sopenharmony_ci		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
49868c2ecf20Sopenharmony_ci	}
49878c2ecf20Sopenharmony_ci
49888c2ecf20Sopenharmony_ci	if (tb[RTA_PREFSRC])
49898c2ecf20Sopenharmony_ci		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
49908c2ecf20Sopenharmony_ci
49918c2ecf20Sopenharmony_ci	if (tb[RTA_OIF])
49928c2ecf20Sopenharmony_ci		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
49938c2ecf20Sopenharmony_ci
49948c2ecf20Sopenharmony_ci	if (tb[RTA_PRIORITY])
49958c2ecf20Sopenharmony_ci		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
49968c2ecf20Sopenharmony_ci
49978c2ecf20Sopenharmony_ci	if (tb[RTA_METRICS]) {
49988c2ecf20Sopenharmony_ci		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
49998c2ecf20Sopenharmony_ci		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
50008c2ecf20Sopenharmony_ci	}
50018c2ecf20Sopenharmony_ci
50028c2ecf20Sopenharmony_ci	if (tb[RTA_TABLE])
50038c2ecf20Sopenharmony_ci		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
50048c2ecf20Sopenharmony_ci
50058c2ecf20Sopenharmony_ci	if (tb[RTA_MULTIPATH]) {
50068c2ecf20Sopenharmony_ci		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
50078c2ecf20Sopenharmony_ci		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
50088c2ecf20Sopenharmony_ci
50098c2ecf20Sopenharmony_ci		err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
50108c2ecf20Sopenharmony_ci						     cfg->fc_mp_len, extack);
50118c2ecf20Sopenharmony_ci		if (err < 0)
50128c2ecf20Sopenharmony_ci			goto errout;
50138c2ecf20Sopenharmony_ci	}
50148c2ecf20Sopenharmony_ci
50158c2ecf20Sopenharmony_ci	if (tb[RTA_PREF]) {
50168c2ecf20Sopenharmony_ci		pref = nla_get_u8(tb[RTA_PREF]);
50178c2ecf20Sopenharmony_ci		if (pref != ICMPV6_ROUTER_PREF_LOW &&
50188c2ecf20Sopenharmony_ci		    pref != ICMPV6_ROUTER_PREF_HIGH)
50198c2ecf20Sopenharmony_ci			pref = ICMPV6_ROUTER_PREF_MEDIUM;
50208c2ecf20Sopenharmony_ci		cfg->fc_flags |= RTF_PREF(pref);
50218c2ecf20Sopenharmony_ci	}
50228c2ecf20Sopenharmony_ci
50238c2ecf20Sopenharmony_ci	if (tb[RTA_ENCAP])
50248c2ecf20Sopenharmony_ci		cfg->fc_encap = tb[RTA_ENCAP];
50258c2ecf20Sopenharmony_ci
50268c2ecf20Sopenharmony_ci	if (tb[RTA_ENCAP_TYPE]) {
50278c2ecf20Sopenharmony_ci		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
50288c2ecf20Sopenharmony_ci
50298c2ecf20Sopenharmony_ci		err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
50308c2ecf20Sopenharmony_ci		if (err < 0)
50318c2ecf20Sopenharmony_ci			goto errout;
50328c2ecf20Sopenharmony_ci	}
50338c2ecf20Sopenharmony_ci
50348c2ecf20Sopenharmony_ci	if (tb[RTA_EXPIRES]) {
50358c2ecf20Sopenharmony_ci		unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
50368c2ecf20Sopenharmony_ci
50378c2ecf20Sopenharmony_ci		if (addrconf_finite_timeout(timeout)) {
50388c2ecf20Sopenharmony_ci			cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
50398c2ecf20Sopenharmony_ci			cfg->fc_flags |= RTF_EXPIRES;
50408c2ecf20Sopenharmony_ci		}
50418c2ecf20Sopenharmony_ci	}
50428c2ecf20Sopenharmony_ci
50438c2ecf20Sopenharmony_ci	err = 0;
50448c2ecf20Sopenharmony_cierrout:
50458c2ecf20Sopenharmony_ci	return err;
50468c2ecf20Sopenharmony_ci}
50478c2ecf20Sopenharmony_ci
50488c2ecf20Sopenharmony_cistruct rt6_nh {
50498c2ecf20Sopenharmony_ci	struct fib6_info *fib6_info;
50508c2ecf20Sopenharmony_ci	struct fib6_config r_cfg;
50518c2ecf20Sopenharmony_ci	struct list_head next;
50528c2ecf20Sopenharmony_ci};
50538c2ecf20Sopenharmony_ci
50548c2ecf20Sopenharmony_cistatic int ip6_route_info_append(struct net *net,
50558c2ecf20Sopenharmony_ci				 struct list_head *rt6_nh_list,
50568c2ecf20Sopenharmony_ci				 struct fib6_info *rt,
50578c2ecf20Sopenharmony_ci				 struct fib6_config *r_cfg)
50588c2ecf20Sopenharmony_ci{
50598c2ecf20Sopenharmony_ci	struct rt6_nh *nh;
50608c2ecf20Sopenharmony_ci	int err = -EEXIST;
50618c2ecf20Sopenharmony_ci
50628c2ecf20Sopenharmony_ci	list_for_each_entry(nh, rt6_nh_list, next) {
50638c2ecf20Sopenharmony_ci		/* check if fib6_info already exists */
50648c2ecf20Sopenharmony_ci		if (rt6_duplicate_nexthop(nh->fib6_info, rt))
50658c2ecf20Sopenharmony_ci			return err;
50668c2ecf20Sopenharmony_ci	}
50678c2ecf20Sopenharmony_ci
50688c2ecf20Sopenharmony_ci	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
50698c2ecf20Sopenharmony_ci	if (!nh)
50708c2ecf20Sopenharmony_ci		return -ENOMEM;
50718c2ecf20Sopenharmony_ci	nh->fib6_info = rt;
50728c2ecf20Sopenharmony_ci	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
50738c2ecf20Sopenharmony_ci	list_add_tail(&nh->next, rt6_nh_list);
50748c2ecf20Sopenharmony_ci
50758c2ecf20Sopenharmony_ci	return 0;
50768c2ecf20Sopenharmony_ci}
50778c2ecf20Sopenharmony_ci
50788c2ecf20Sopenharmony_cistatic void ip6_route_mpath_notify(struct fib6_info *rt,
50798c2ecf20Sopenharmony_ci				   struct fib6_info *rt_last,
50808c2ecf20Sopenharmony_ci				   struct nl_info *info,
50818c2ecf20Sopenharmony_ci				   __u16 nlflags)
50828c2ecf20Sopenharmony_ci{
50838c2ecf20Sopenharmony_ci	/* if this is an APPEND route, then rt points to the first route
50848c2ecf20Sopenharmony_ci	 * inserted and rt_last points to last route inserted. Userspace
50858c2ecf20Sopenharmony_ci	 * wants a consistent dump of the route which starts at the first
50868c2ecf20Sopenharmony_ci	 * nexthop. Since sibling routes are always added at the end of
50878c2ecf20Sopenharmony_ci	 * the list, find the first sibling of the last route appended
50888c2ecf20Sopenharmony_ci	 */
50898c2ecf20Sopenharmony_ci	if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
50908c2ecf20Sopenharmony_ci		rt = list_first_entry(&rt_last->fib6_siblings,
50918c2ecf20Sopenharmony_ci				      struct fib6_info,
50928c2ecf20Sopenharmony_ci				      fib6_siblings);
50938c2ecf20Sopenharmony_ci	}
50948c2ecf20Sopenharmony_ci
50958c2ecf20Sopenharmony_ci	if (rt)
50968c2ecf20Sopenharmony_ci		inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
50978c2ecf20Sopenharmony_ci}
50988c2ecf20Sopenharmony_ci
50998c2ecf20Sopenharmony_cistatic bool ip6_route_mpath_should_notify(const struct fib6_info *rt)
51008c2ecf20Sopenharmony_ci{
51018c2ecf20Sopenharmony_ci	bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
51028c2ecf20Sopenharmony_ci	bool should_notify = false;
51038c2ecf20Sopenharmony_ci	struct fib6_info *leaf;
51048c2ecf20Sopenharmony_ci	struct fib6_node *fn;
51058c2ecf20Sopenharmony_ci
51068c2ecf20Sopenharmony_ci	rcu_read_lock();
51078c2ecf20Sopenharmony_ci	fn = rcu_dereference(rt->fib6_node);
51088c2ecf20Sopenharmony_ci	if (!fn)
51098c2ecf20Sopenharmony_ci		goto out;
51108c2ecf20Sopenharmony_ci
51118c2ecf20Sopenharmony_ci	leaf = rcu_dereference(fn->leaf);
51128c2ecf20Sopenharmony_ci	if (!leaf)
51138c2ecf20Sopenharmony_ci		goto out;
51148c2ecf20Sopenharmony_ci
51158c2ecf20Sopenharmony_ci	if (rt == leaf ||
51168c2ecf20Sopenharmony_ci	    (rt_can_ecmp && rt->fib6_metric == leaf->fib6_metric &&
51178c2ecf20Sopenharmony_ci	     rt6_qualify_for_ecmp(leaf)))
51188c2ecf20Sopenharmony_ci		should_notify = true;
51198c2ecf20Sopenharmony_ciout:
51208c2ecf20Sopenharmony_ci	rcu_read_unlock();
51218c2ecf20Sopenharmony_ci
51228c2ecf20Sopenharmony_ci	return should_notify;
51238c2ecf20Sopenharmony_ci}
51248c2ecf20Sopenharmony_ci
51258c2ecf20Sopenharmony_cistatic int fib6_gw_from_attr(struct in6_addr *gw, struct nlattr *nla,
51268c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
51278c2ecf20Sopenharmony_ci{
51288c2ecf20Sopenharmony_ci	if (nla_len(nla) < sizeof(*gw)) {
51298c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_GATEWAY");
51308c2ecf20Sopenharmony_ci		return -EINVAL;
51318c2ecf20Sopenharmony_ci	}
51328c2ecf20Sopenharmony_ci
51338c2ecf20Sopenharmony_ci	*gw = nla_get_in6_addr(nla);
51348c2ecf20Sopenharmony_ci
51358c2ecf20Sopenharmony_ci	return 0;
51368c2ecf20Sopenharmony_ci}
51378c2ecf20Sopenharmony_ci
51388c2ecf20Sopenharmony_cistatic int ip6_route_multipath_add(struct fib6_config *cfg,
51398c2ecf20Sopenharmony_ci				   struct netlink_ext_ack *extack)
51408c2ecf20Sopenharmony_ci{
51418c2ecf20Sopenharmony_ci	struct fib6_info *rt_notif = NULL, *rt_last = NULL;
51428c2ecf20Sopenharmony_ci	struct nl_info *info = &cfg->fc_nlinfo;
51438c2ecf20Sopenharmony_ci	struct fib6_config r_cfg;
51448c2ecf20Sopenharmony_ci	struct rtnexthop *rtnh;
51458c2ecf20Sopenharmony_ci	struct fib6_info *rt;
51468c2ecf20Sopenharmony_ci	struct rt6_nh *err_nh;
51478c2ecf20Sopenharmony_ci	struct rt6_nh *nh, *nh_safe;
51488c2ecf20Sopenharmony_ci	__u16 nlflags;
51498c2ecf20Sopenharmony_ci	int remaining;
51508c2ecf20Sopenharmony_ci	int attrlen;
51518c2ecf20Sopenharmony_ci	int err = 1;
51528c2ecf20Sopenharmony_ci	int nhn = 0;
51538c2ecf20Sopenharmony_ci	int replace = (cfg->fc_nlinfo.nlh &&
51548c2ecf20Sopenharmony_ci		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
51558c2ecf20Sopenharmony_ci	LIST_HEAD(rt6_nh_list);
51568c2ecf20Sopenharmony_ci
51578c2ecf20Sopenharmony_ci	nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
51588c2ecf20Sopenharmony_ci	if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
51598c2ecf20Sopenharmony_ci		nlflags |= NLM_F_APPEND;
51608c2ecf20Sopenharmony_ci
51618c2ecf20Sopenharmony_ci	remaining = cfg->fc_mp_len;
51628c2ecf20Sopenharmony_ci	rtnh = (struct rtnexthop *)cfg->fc_mp;
51638c2ecf20Sopenharmony_ci
51648c2ecf20Sopenharmony_ci	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
51658c2ecf20Sopenharmony_ci	 * fib6_info structs per nexthop
51668c2ecf20Sopenharmony_ci	 */
51678c2ecf20Sopenharmony_ci	while (rtnh_ok(rtnh, remaining)) {
51688c2ecf20Sopenharmony_ci		memcpy(&r_cfg, cfg, sizeof(*cfg));
51698c2ecf20Sopenharmony_ci		if (rtnh->rtnh_ifindex)
51708c2ecf20Sopenharmony_ci			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
51718c2ecf20Sopenharmony_ci
51728c2ecf20Sopenharmony_ci		attrlen = rtnh_attrlen(rtnh);
51738c2ecf20Sopenharmony_ci		if (attrlen > 0) {
51748c2ecf20Sopenharmony_ci			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
51758c2ecf20Sopenharmony_ci
51768c2ecf20Sopenharmony_ci			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
51778c2ecf20Sopenharmony_ci			if (nla) {
51788c2ecf20Sopenharmony_ci				err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
51798c2ecf20Sopenharmony_ci							extack);
51808c2ecf20Sopenharmony_ci				if (err)
51818c2ecf20Sopenharmony_ci					goto cleanup;
51828c2ecf20Sopenharmony_ci
51838c2ecf20Sopenharmony_ci				r_cfg.fc_flags |= RTF_GATEWAY;
51848c2ecf20Sopenharmony_ci			}
51858c2ecf20Sopenharmony_ci			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
51868c2ecf20Sopenharmony_ci
51878c2ecf20Sopenharmony_ci			/* RTA_ENCAP_TYPE length checked in
51888c2ecf20Sopenharmony_ci			 * lwtunnel_valid_encap_type_attr
51898c2ecf20Sopenharmony_ci			 */
51908c2ecf20Sopenharmony_ci			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
51918c2ecf20Sopenharmony_ci			if (nla)
51928c2ecf20Sopenharmony_ci				r_cfg.fc_encap_type = nla_get_u16(nla);
51938c2ecf20Sopenharmony_ci		}
51948c2ecf20Sopenharmony_ci
51958c2ecf20Sopenharmony_ci		r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
51968c2ecf20Sopenharmony_ci		rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
51978c2ecf20Sopenharmony_ci		if (IS_ERR(rt)) {
51988c2ecf20Sopenharmony_ci			err = PTR_ERR(rt);
51998c2ecf20Sopenharmony_ci			rt = NULL;
52008c2ecf20Sopenharmony_ci			goto cleanup;
52018c2ecf20Sopenharmony_ci		}
52028c2ecf20Sopenharmony_ci		if (!rt6_qualify_for_ecmp(rt)) {
52038c2ecf20Sopenharmony_ci			err = -EINVAL;
52048c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack,
52058c2ecf20Sopenharmony_ci				       "Device only routes can not be added for IPv6 using the multipath API.");
52068c2ecf20Sopenharmony_ci			fib6_info_release(rt);
52078c2ecf20Sopenharmony_ci			goto cleanup;
52088c2ecf20Sopenharmony_ci		}
52098c2ecf20Sopenharmony_ci
52108c2ecf20Sopenharmony_ci		rt->fib6_nh->fib_nh_weight = rtnh->rtnh_hops + 1;
52118c2ecf20Sopenharmony_ci
52128c2ecf20Sopenharmony_ci		err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
52138c2ecf20Sopenharmony_ci					    rt, &r_cfg);
52148c2ecf20Sopenharmony_ci		if (err) {
52158c2ecf20Sopenharmony_ci			fib6_info_release(rt);
52168c2ecf20Sopenharmony_ci			goto cleanup;
52178c2ecf20Sopenharmony_ci		}
52188c2ecf20Sopenharmony_ci
52198c2ecf20Sopenharmony_ci		rtnh = rtnh_next(rtnh, &remaining);
52208c2ecf20Sopenharmony_ci	}
52218c2ecf20Sopenharmony_ci
52228c2ecf20Sopenharmony_ci	if (list_empty(&rt6_nh_list)) {
52238c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
52248c2ecf20Sopenharmony_ci			       "Invalid nexthop configuration - no valid nexthops");
52258c2ecf20Sopenharmony_ci		return -EINVAL;
52268c2ecf20Sopenharmony_ci	}
52278c2ecf20Sopenharmony_ci
52288c2ecf20Sopenharmony_ci	/* for add and replace send one notification with all nexthops.
52298c2ecf20Sopenharmony_ci	 * Skip the notification in fib6_add_rt2node and send one with
52308c2ecf20Sopenharmony_ci	 * the full route when done
52318c2ecf20Sopenharmony_ci	 */
52328c2ecf20Sopenharmony_ci	info->skip_notify = 1;
52338c2ecf20Sopenharmony_ci
52348c2ecf20Sopenharmony_ci	/* For add and replace, send one notification with all nexthops. For
52358c2ecf20Sopenharmony_ci	 * append, send one notification with all appended nexthops.
52368c2ecf20Sopenharmony_ci	 */
52378c2ecf20Sopenharmony_ci	info->skip_notify_kernel = 1;
52388c2ecf20Sopenharmony_ci
52398c2ecf20Sopenharmony_ci	err_nh = NULL;
52408c2ecf20Sopenharmony_ci	list_for_each_entry(nh, &rt6_nh_list, next) {
52418c2ecf20Sopenharmony_ci		err = __ip6_ins_rt(nh->fib6_info, info, extack);
52428c2ecf20Sopenharmony_ci
52438c2ecf20Sopenharmony_ci		if (err) {
52448c2ecf20Sopenharmony_ci			if (replace && nhn)
52458c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
52468c2ecf20Sopenharmony_ci						   "multipath route replace failed (check consistency of installed routes)");
52478c2ecf20Sopenharmony_ci			err_nh = nh;
52488c2ecf20Sopenharmony_ci			goto add_errout;
52498c2ecf20Sopenharmony_ci		}
52508c2ecf20Sopenharmony_ci		/* save reference to last route successfully inserted */
52518c2ecf20Sopenharmony_ci		rt_last = nh->fib6_info;
52528c2ecf20Sopenharmony_ci
52538c2ecf20Sopenharmony_ci		/* save reference to first route for notification */
52548c2ecf20Sopenharmony_ci		if (!rt_notif)
52558c2ecf20Sopenharmony_ci			rt_notif = nh->fib6_info;
52568c2ecf20Sopenharmony_ci
52578c2ecf20Sopenharmony_ci		/* Because each route is added like a single route we remove
52588c2ecf20Sopenharmony_ci		 * these flags after the first nexthop: if there is a collision,
52598c2ecf20Sopenharmony_ci		 * we have already failed to add the first nexthop:
52608c2ecf20Sopenharmony_ci		 * fib6_add_rt2node() has rejected it; when replacing, old
52618c2ecf20Sopenharmony_ci		 * nexthops have been replaced by first new, the rest should
52628c2ecf20Sopenharmony_ci		 * be added to it.
52638c2ecf20Sopenharmony_ci		 */
52648c2ecf20Sopenharmony_ci		if (cfg->fc_nlinfo.nlh) {
52658c2ecf20Sopenharmony_ci			cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
52668c2ecf20Sopenharmony_ci							     NLM_F_REPLACE);
52678c2ecf20Sopenharmony_ci			cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_CREATE;
52688c2ecf20Sopenharmony_ci		}
52698c2ecf20Sopenharmony_ci		nhn++;
52708c2ecf20Sopenharmony_ci	}
52718c2ecf20Sopenharmony_ci
52728c2ecf20Sopenharmony_ci	/* An in-kernel notification should only be sent in case the new
52738c2ecf20Sopenharmony_ci	 * multipath route is added as the first route in the node, or if
52748c2ecf20Sopenharmony_ci	 * it was appended to it. We pass 'rt_notif' since it is the first
52758c2ecf20Sopenharmony_ci	 * sibling and might allow us to skip some checks in the replace case.
52768c2ecf20Sopenharmony_ci	 */
52778c2ecf20Sopenharmony_ci	if (ip6_route_mpath_should_notify(rt_notif)) {
52788c2ecf20Sopenharmony_ci		enum fib_event_type fib_event;
52798c2ecf20Sopenharmony_ci
52808c2ecf20Sopenharmony_ci		if (rt_notif->fib6_nsiblings != nhn - 1)
52818c2ecf20Sopenharmony_ci			fib_event = FIB_EVENT_ENTRY_APPEND;
52828c2ecf20Sopenharmony_ci		else
52838c2ecf20Sopenharmony_ci			fib_event = FIB_EVENT_ENTRY_REPLACE;
52848c2ecf20Sopenharmony_ci
52858c2ecf20Sopenharmony_ci		err = call_fib6_multipath_entry_notifiers(info->nl_net,
52868c2ecf20Sopenharmony_ci							  fib_event, rt_notif,
52878c2ecf20Sopenharmony_ci							  nhn - 1, extack);
52888c2ecf20Sopenharmony_ci		if (err) {
52898c2ecf20Sopenharmony_ci			/* Delete all the siblings that were just added */
52908c2ecf20Sopenharmony_ci			err_nh = NULL;
52918c2ecf20Sopenharmony_ci			goto add_errout;
52928c2ecf20Sopenharmony_ci		}
52938c2ecf20Sopenharmony_ci	}
52948c2ecf20Sopenharmony_ci
52958c2ecf20Sopenharmony_ci	/* success ... tell user about new route */
52968c2ecf20Sopenharmony_ci	ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
52978c2ecf20Sopenharmony_ci	goto cleanup;
52988c2ecf20Sopenharmony_ci
52998c2ecf20Sopenharmony_ciadd_errout:
53008c2ecf20Sopenharmony_ci	/* send notification for routes that were added so that
53018c2ecf20Sopenharmony_ci	 * the delete notifications sent by ip6_route_del are
53028c2ecf20Sopenharmony_ci	 * coherent
53038c2ecf20Sopenharmony_ci	 */
53048c2ecf20Sopenharmony_ci	if (rt_notif)
53058c2ecf20Sopenharmony_ci		ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
53068c2ecf20Sopenharmony_ci
53078c2ecf20Sopenharmony_ci	/* Delete routes that were already added */
53088c2ecf20Sopenharmony_ci	list_for_each_entry(nh, &rt6_nh_list, next) {
53098c2ecf20Sopenharmony_ci		if (err_nh == nh)
53108c2ecf20Sopenharmony_ci			break;
53118c2ecf20Sopenharmony_ci		ip6_route_del(&nh->r_cfg, extack);
53128c2ecf20Sopenharmony_ci	}
53138c2ecf20Sopenharmony_ci
53148c2ecf20Sopenharmony_cicleanup:
53158c2ecf20Sopenharmony_ci	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
53168c2ecf20Sopenharmony_ci		fib6_info_release(nh->fib6_info);
53178c2ecf20Sopenharmony_ci		list_del(&nh->next);
53188c2ecf20Sopenharmony_ci		kfree(nh);
53198c2ecf20Sopenharmony_ci	}
53208c2ecf20Sopenharmony_ci
53218c2ecf20Sopenharmony_ci	return err;
53228c2ecf20Sopenharmony_ci}
53238c2ecf20Sopenharmony_ci
53248c2ecf20Sopenharmony_cistatic int ip6_route_multipath_del(struct fib6_config *cfg,
53258c2ecf20Sopenharmony_ci				   struct netlink_ext_ack *extack)
53268c2ecf20Sopenharmony_ci{
53278c2ecf20Sopenharmony_ci	struct fib6_config r_cfg;
53288c2ecf20Sopenharmony_ci	struct rtnexthop *rtnh;
53298c2ecf20Sopenharmony_ci	int last_err = 0;
53308c2ecf20Sopenharmony_ci	int remaining;
53318c2ecf20Sopenharmony_ci	int attrlen;
53328c2ecf20Sopenharmony_ci	int err;
53338c2ecf20Sopenharmony_ci
53348c2ecf20Sopenharmony_ci	remaining = cfg->fc_mp_len;
53358c2ecf20Sopenharmony_ci	rtnh = (struct rtnexthop *)cfg->fc_mp;
53368c2ecf20Sopenharmony_ci
53378c2ecf20Sopenharmony_ci	/* Parse a Multipath Entry */
53388c2ecf20Sopenharmony_ci	while (rtnh_ok(rtnh, remaining)) {
53398c2ecf20Sopenharmony_ci		memcpy(&r_cfg, cfg, sizeof(*cfg));
53408c2ecf20Sopenharmony_ci		if (rtnh->rtnh_ifindex)
53418c2ecf20Sopenharmony_ci			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
53428c2ecf20Sopenharmony_ci
53438c2ecf20Sopenharmony_ci		attrlen = rtnh_attrlen(rtnh);
53448c2ecf20Sopenharmony_ci		if (attrlen > 0) {
53458c2ecf20Sopenharmony_ci			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
53468c2ecf20Sopenharmony_ci
53478c2ecf20Sopenharmony_ci			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
53488c2ecf20Sopenharmony_ci			if (nla) {
53498c2ecf20Sopenharmony_ci				err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
53508c2ecf20Sopenharmony_ci							extack);
53518c2ecf20Sopenharmony_ci				if (err) {
53528c2ecf20Sopenharmony_ci					last_err = err;
53538c2ecf20Sopenharmony_ci					goto next_rtnh;
53548c2ecf20Sopenharmony_ci				}
53558c2ecf20Sopenharmony_ci
53568c2ecf20Sopenharmony_ci				r_cfg.fc_flags |= RTF_GATEWAY;
53578c2ecf20Sopenharmony_ci			}
53588c2ecf20Sopenharmony_ci		}
53598c2ecf20Sopenharmony_ci		err = ip6_route_del(&r_cfg, extack);
53608c2ecf20Sopenharmony_ci		if (err)
53618c2ecf20Sopenharmony_ci			last_err = err;
53628c2ecf20Sopenharmony_ci
53638c2ecf20Sopenharmony_cinext_rtnh:
53648c2ecf20Sopenharmony_ci		rtnh = rtnh_next(rtnh, &remaining);
53658c2ecf20Sopenharmony_ci	}
53668c2ecf20Sopenharmony_ci
53678c2ecf20Sopenharmony_ci	return last_err;
53688c2ecf20Sopenharmony_ci}
53698c2ecf20Sopenharmony_ci
53708c2ecf20Sopenharmony_cistatic int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
53718c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
53728c2ecf20Sopenharmony_ci{
53738c2ecf20Sopenharmony_ci	struct fib6_config cfg;
53748c2ecf20Sopenharmony_ci	int err;
53758c2ecf20Sopenharmony_ci
53768c2ecf20Sopenharmony_ci	err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
53778c2ecf20Sopenharmony_ci	if (err < 0)
53788c2ecf20Sopenharmony_ci		return err;
53798c2ecf20Sopenharmony_ci
53808c2ecf20Sopenharmony_ci	if (cfg.fc_nh_id &&
53818c2ecf20Sopenharmony_ci	    !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) {
53828c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
53838c2ecf20Sopenharmony_ci		return -EINVAL;
53848c2ecf20Sopenharmony_ci	}
53858c2ecf20Sopenharmony_ci
53868c2ecf20Sopenharmony_ci	if (cfg.fc_mp)
53878c2ecf20Sopenharmony_ci		return ip6_route_multipath_del(&cfg, extack);
53888c2ecf20Sopenharmony_ci	else {
53898c2ecf20Sopenharmony_ci		cfg.fc_delete_all_nh = 1;
53908c2ecf20Sopenharmony_ci		return ip6_route_del(&cfg, extack);
53918c2ecf20Sopenharmony_ci	}
53928c2ecf20Sopenharmony_ci}
53938c2ecf20Sopenharmony_ci
53948c2ecf20Sopenharmony_cistatic int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
53958c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
53968c2ecf20Sopenharmony_ci{
53978c2ecf20Sopenharmony_ci	struct fib6_config cfg;
53988c2ecf20Sopenharmony_ci	int err;
53998c2ecf20Sopenharmony_ci
54008c2ecf20Sopenharmony_ci	err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
54018c2ecf20Sopenharmony_ci	if (err < 0)
54028c2ecf20Sopenharmony_ci		return err;
54038c2ecf20Sopenharmony_ci
54048c2ecf20Sopenharmony_ci	if (cfg.fc_metric == 0)
54058c2ecf20Sopenharmony_ci		cfg.fc_metric = IP6_RT_PRIO_USER;
54068c2ecf20Sopenharmony_ci
54078c2ecf20Sopenharmony_ci	if (cfg.fc_mp)
54088c2ecf20Sopenharmony_ci		return ip6_route_multipath_add(&cfg, extack);
54098c2ecf20Sopenharmony_ci	else
54108c2ecf20Sopenharmony_ci		return ip6_route_add(&cfg, GFP_KERNEL, extack);
54118c2ecf20Sopenharmony_ci}
54128c2ecf20Sopenharmony_ci
54138c2ecf20Sopenharmony_ci/* add the overhead of this fib6_nh to nexthop_len */
54148c2ecf20Sopenharmony_cistatic int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg)
54158c2ecf20Sopenharmony_ci{
54168c2ecf20Sopenharmony_ci	int *nexthop_len = arg;
54178c2ecf20Sopenharmony_ci
54188c2ecf20Sopenharmony_ci	*nexthop_len += nla_total_size(0)	 /* RTA_MULTIPATH */
54198c2ecf20Sopenharmony_ci		     + NLA_ALIGN(sizeof(struct rtnexthop))
54208c2ecf20Sopenharmony_ci		     + nla_total_size(16); /* RTA_GATEWAY */
54218c2ecf20Sopenharmony_ci
54228c2ecf20Sopenharmony_ci	if (nh->fib_nh_lws) {
54238c2ecf20Sopenharmony_ci		/* RTA_ENCAP_TYPE */
54248c2ecf20Sopenharmony_ci		*nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
54258c2ecf20Sopenharmony_ci		/* RTA_ENCAP */
54268c2ecf20Sopenharmony_ci		*nexthop_len += nla_total_size(2);
54278c2ecf20Sopenharmony_ci	}
54288c2ecf20Sopenharmony_ci
54298c2ecf20Sopenharmony_ci	return 0;
54308c2ecf20Sopenharmony_ci}
54318c2ecf20Sopenharmony_ci
54328c2ecf20Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i)
54338c2ecf20Sopenharmony_ci{
54348c2ecf20Sopenharmony_ci	int nexthop_len;
54358c2ecf20Sopenharmony_ci
54368c2ecf20Sopenharmony_ci	if (f6i->nh) {
54378c2ecf20Sopenharmony_ci		nexthop_len = nla_total_size(4); /* RTA_NH_ID */
54388c2ecf20Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size,
54398c2ecf20Sopenharmony_ci					 &nexthop_len);
54408c2ecf20Sopenharmony_ci	} else {
54418c2ecf20Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
54428c2ecf20Sopenharmony_ci		struct fib6_nh *nh = f6i->fib6_nh;
54438c2ecf20Sopenharmony_ci
54448c2ecf20Sopenharmony_ci		nexthop_len = 0;
54458c2ecf20Sopenharmony_ci		if (f6i->fib6_nsiblings) {
54468c2ecf20Sopenharmony_ci			rt6_nh_nlmsg_size(nh, &nexthop_len);
54478c2ecf20Sopenharmony_ci
54488c2ecf20Sopenharmony_ci			list_for_each_entry_safe(sibling, next_sibling,
54498c2ecf20Sopenharmony_ci						 &f6i->fib6_siblings, fib6_siblings) {
54508c2ecf20Sopenharmony_ci				rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len);
54518c2ecf20Sopenharmony_ci			}
54528c2ecf20Sopenharmony_ci		}
54538c2ecf20Sopenharmony_ci		nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
54548c2ecf20Sopenharmony_ci	}
54558c2ecf20Sopenharmony_ci
54568c2ecf20Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct rtmsg))
54578c2ecf20Sopenharmony_ci	       + nla_total_size(16) /* RTA_SRC */
54588c2ecf20Sopenharmony_ci	       + nla_total_size(16) /* RTA_DST */
54598c2ecf20Sopenharmony_ci	       + nla_total_size(16) /* RTA_GATEWAY */
54608c2ecf20Sopenharmony_ci	       + nla_total_size(16) /* RTA_PREFSRC */
54618c2ecf20Sopenharmony_ci	       + nla_total_size(4) /* RTA_TABLE */
54628c2ecf20Sopenharmony_ci	       + nla_total_size(4) /* RTA_IIF */
54638c2ecf20Sopenharmony_ci	       + nla_total_size(4) /* RTA_OIF */
54648c2ecf20Sopenharmony_ci	       + nla_total_size(4) /* RTA_PRIORITY */
54658c2ecf20Sopenharmony_ci	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
54668c2ecf20Sopenharmony_ci	       + nla_total_size(sizeof(struct rta_cacheinfo))
54678c2ecf20Sopenharmony_ci	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
54688c2ecf20Sopenharmony_ci	       + nla_total_size(1) /* RTA_PREF */
54698c2ecf20Sopenharmony_ci	       + nexthop_len;
54708c2ecf20Sopenharmony_ci}
54718c2ecf20Sopenharmony_ci
54728c2ecf20Sopenharmony_cistatic int rt6_fill_node_nexthop(struct sk_buff *skb, struct nexthop *nh,
54738c2ecf20Sopenharmony_ci				 unsigned char *flags)
54748c2ecf20Sopenharmony_ci{
54758c2ecf20Sopenharmony_ci	if (nexthop_is_multipath(nh)) {
54768c2ecf20Sopenharmony_ci		struct nlattr *mp;
54778c2ecf20Sopenharmony_ci
54788c2ecf20Sopenharmony_ci		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
54798c2ecf20Sopenharmony_ci		if (!mp)
54808c2ecf20Sopenharmony_ci			goto nla_put_failure;
54818c2ecf20Sopenharmony_ci
54828c2ecf20Sopenharmony_ci		if (nexthop_mpath_fill_node(skb, nh, AF_INET6))
54838c2ecf20Sopenharmony_ci			goto nla_put_failure;
54848c2ecf20Sopenharmony_ci
54858c2ecf20Sopenharmony_ci		nla_nest_end(skb, mp);
54868c2ecf20Sopenharmony_ci	} else {
54878c2ecf20Sopenharmony_ci		struct fib6_nh *fib6_nh;
54888c2ecf20Sopenharmony_ci
54898c2ecf20Sopenharmony_ci		fib6_nh = nexthop_fib6_nh(nh);
54908c2ecf20Sopenharmony_ci		if (fib_nexthop_info(skb, &fib6_nh->nh_common, AF_INET6,
54918c2ecf20Sopenharmony_ci				     flags, false) < 0)
54928c2ecf20Sopenharmony_ci			goto nla_put_failure;
54938c2ecf20Sopenharmony_ci	}
54948c2ecf20Sopenharmony_ci
54958c2ecf20Sopenharmony_ci	return 0;
54968c2ecf20Sopenharmony_ci
54978c2ecf20Sopenharmony_cinla_put_failure:
54988c2ecf20Sopenharmony_ci	return -EMSGSIZE;
54998c2ecf20Sopenharmony_ci}
55008c2ecf20Sopenharmony_ci
55018c2ecf20Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb,
55028c2ecf20Sopenharmony_ci			 struct fib6_info *rt, struct dst_entry *dst,
55038c2ecf20Sopenharmony_ci			 struct in6_addr *dest, struct in6_addr *src,
55048c2ecf20Sopenharmony_ci			 int iif, int type, u32 portid, u32 seq,
55058c2ecf20Sopenharmony_ci			 unsigned int flags)
55068c2ecf20Sopenharmony_ci{
55078c2ecf20Sopenharmony_ci	struct rt6_info *rt6 = (struct rt6_info *)dst;
55088c2ecf20Sopenharmony_ci	struct rt6key *rt6_dst, *rt6_src;
55098c2ecf20Sopenharmony_ci	u32 *pmetrics, table, rt6_flags;
55108c2ecf20Sopenharmony_ci	unsigned char nh_flags = 0;
55118c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
55128c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
55138c2ecf20Sopenharmony_ci	long expires = 0;
55148c2ecf20Sopenharmony_ci
55158c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
55168c2ecf20Sopenharmony_ci	if (!nlh)
55178c2ecf20Sopenharmony_ci		return -EMSGSIZE;
55188c2ecf20Sopenharmony_ci
55198c2ecf20Sopenharmony_ci	if (rt6) {
55208c2ecf20Sopenharmony_ci		rt6_dst = &rt6->rt6i_dst;
55218c2ecf20Sopenharmony_ci		rt6_src = &rt6->rt6i_src;
55228c2ecf20Sopenharmony_ci		rt6_flags = rt6->rt6i_flags;
55238c2ecf20Sopenharmony_ci	} else {
55248c2ecf20Sopenharmony_ci		rt6_dst = &rt->fib6_dst;
55258c2ecf20Sopenharmony_ci		rt6_src = &rt->fib6_src;
55268c2ecf20Sopenharmony_ci		rt6_flags = rt->fib6_flags;
55278c2ecf20Sopenharmony_ci	}
55288c2ecf20Sopenharmony_ci
55298c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
55308c2ecf20Sopenharmony_ci	rtm->rtm_family = AF_INET6;
55318c2ecf20Sopenharmony_ci	rtm->rtm_dst_len = rt6_dst->plen;
55328c2ecf20Sopenharmony_ci	rtm->rtm_src_len = rt6_src->plen;
55338c2ecf20Sopenharmony_ci	rtm->rtm_tos = 0;
55348c2ecf20Sopenharmony_ci	if (rt->fib6_table)
55358c2ecf20Sopenharmony_ci		table = rt->fib6_table->tb6_id;
55368c2ecf20Sopenharmony_ci	else
55378c2ecf20Sopenharmony_ci		table = RT6_TABLE_UNSPEC;
55388c2ecf20Sopenharmony_ci	rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
55398c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, RTA_TABLE, table))
55408c2ecf20Sopenharmony_ci		goto nla_put_failure;
55418c2ecf20Sopenharmony_ci
55428c2ecf20Sopenharmony_ci	rtm->rtm_type = rt->fib6_type;
55438c2ecf20Sopenharmony_ci	rtm->rtm_flags = 0;
55448c2ecf20Sopenharmony_ci	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
55458c2ecf20Sopenharmony_ci	rtm->rtm_protocol = rt->fib6_protocol;
55468c2ecf20Sopenharmony_ci
55478c2ecf20Sopenharmony_ci	if (rt6_flags & RTF_CACHE)
55488c2ecf20Sopenharmony_ci		rtm->rtm_flags |= RTM_F_CLONED;
55498c2ecf20Sopenharmony_ci
55508c2ecf20Sopenharmony_ci	if (dest) {
55518c2ecf20Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_DST, dest))
55528c2ecf20Sopenharmony_ci			goto nla_put_failure;
55538c2ecf20Sopenharmony_ci		rtm->rtm_dst_len = 128;
55548c2ecf20Sopenharmony_ci	} else if (rtm->rtm_dst_len)
55558c2ecf20Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
55568c2ecf20Sopenharmony_ci			goto nla_put_failure;
55578c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
55588c2ecf20Sopenharmony_ci	if (src) {
55598c2ecf20Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_SRC, src))
55608c2ecf20Sopenharmony_ci			goto nla_put_failure;
55618c2ecf20Sopenharmony_ci		rtm->rtm_src_len = 128;
55628c2ecf20Sopenharmony_ci	} else if (rtm->rtm_src_len &&
55638c2ecf20Sopenharmony_ci		   nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
55648c2ecf20Sopenharmony_ci		goto nla_put_failure;
55658c2ecf20Sopenharmony_ci#endif
55668c2ecf20Sopenharmony_ci	if (iif) {
55678c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE
55688c2ecf20Sopenharmony_ci		if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
55698c2ecf20Sopenharmony_ci			int err = ip6mr_get_route(net, skb, rtm, portid);
55708c2ecf20Sopenharmony_ci
55718c2ecf20Sopenharmony_ci			if (err == 0)
55728c2ecf20Sopenharmony_ci				return 0;
55738c2ecf20Sopenharmony_ci			if (err < 0)
55748c2ecf20Sopenharmony_ci				goto nla_put_failure;
55758c2ecf20Sopenharmony_ci		} else
55768c2ecf20Sopenharmony_ci#endif
55778c2ecf20Sopenharmony_ci			if (nla_put_u32(skb, RTA_IIF, iif))
55788c2ecf20Sopenharmony_ci				goto nla_put_failure;
55798c2ecf20Sopenharmony_ci	} else if (dest) {
55808c2ecf20Sopenharmony_ci		struct in6_addr saddr_buf;
55818c2ecf20Sopenharmony_ci		if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
55828c2ecf20Sopenharmony_ci		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
55838c2ecf20Sopenharmony_ci			goto nla_put_failure;
55848c2ecf20Sopenharmony_ci	}
55858c2ecf20Sopenharmony_ci
55868c2ecf20Sopenharmony_ci	if (rt->fib6_prefsrc.plen) {
55878c2ecf20Sopenharmony_ci		struct in6_addr saddr_buf;
55888c2ecf20Sopenharmony_ci		saddr_buf = rt->fib6_prefsrc.addr;
55898c2ecf20Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
55908c2ecf20Sopenharmony_ci			goto nla_put_failure;
55918c2ecf20Sopenharmony_ci	}
55928c2ecf20Sopenharmony_ci
55938c2ecf20Sopenharmony_ci	pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
55948c2ecf20Sopenharmony_ci	if (rtnetlink_put_metrics(skb, pmetrics) < 0)
55958c2ecf20Sopenharmony_ci		goto nla_put_failure;
55968c2ecf20Sopenharmony_ci
55978c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
55988c2ecf20Sopenharmony_ci		goto nla_put_failure;
55998c2ecf20Sopenharmony_ci
56008c2ecf20Sopenharmony_ci	/* For multipath routes, walk the siblings list and add
56018c2ecf20Sopenharmony_ci	 * each as a nexthop within RTA_MULTIPATH.
56028c2ecf20Sopenharmony_ci	 */
56038c2ecf20Sopenharmony_ci	if (rt6) {
56048c2ecf20Sopenharmony_ci		if (rt6_flags & RTF_GATEWAY &&
56058c2ecf20Sopenharmony_ci		    nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
56068c2ecf20Sopenharmony_ci			goto nla_put_failure;
56078c2ecf20Sopenharmony_ci
56088c2ecf20Sopenharmony_ci		if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
56098c2ecf20Sopenharmony_ci			goto nla_put_failure;
56108c2ecf20Sopenharmony_ci	} else if (rt->fib6_nsiblings) {
56118c2ecf20Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
56128c2ecf20Sopenharmony_ci		struct nlattr *mp;
56138c2ecf20Sopenharmony_ci
56148c2ecf20Sopenharmony_ci		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
56158c2ecf20Sopenharmony_ci		if (!mp)
56168c2ecf20Sopenharmony_ci			goto nla_put_failure;
56178c2ecf20Sopenharmony_ci
56188c2ecf20Sopenharmony_ci		if (fib_add_nexthop(skb, &rt->fib6_nh->nh_common,
56198c2ecf20Sopenharmony_ci				    rt->fib6_nh->fib_nh_weight, AF_INET6,
56208c2ecf20Sopenharmony_ci				    0) < 0)
56218c2ecf20Sopenharmony_ci			goto nla_put_failure;
56228c2ecf20Sopenharmony_ci
56238c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
56248c2ecf20Sopenharmony_ci					 &rt->fib6_siblings, fib6_siblings) {
56258c2ecf20Sopenharmony_ci			if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common,
56268c2ecf20Sopenharmony_ci					    sibling->fib6_nh->fib_nh_weight,
56278c2ecf20Sopenharmony_ci					    AF_INET6, 0) < 0)
56288c2ecf20Sopenharmony_ci				goto nla_put_failure;
56298c2ecf20Sopenharmony_ci		}
56308c2ecf20Sopenharmony_ci
56318c2ecf20Sopenharmony_ci		nla_nest_end(skb, mp);
56328c2ecf20Sopenharmony_ci	} else if (rt->nh) {
56338c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id))
56348c2ecf20Sopenharmony_ci			goto nla_put_failure;
56358c2ecf20Sopenharmony_ci
56368c2ecf20Sopenharmony_ci		if (nexthop_is_blackhole(rt->nh))
56378c2ecf20Sopenharmony_ci			rtm->rtm_type = RTN_BLACKHOLE;
56388c2ecf20Sopenharmony_ci
56398c2ecf20Sopenharmony_ci		if (READ_ONCE(net->ipv4.sysctl_nexthop_compat_mode) &&
56408c2ecf20Sopenharmony_ci		    rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0)
56418c2ecf20Sopenharmony_ci			goto nla_put_failure;
56428c2ecf20Sopenharmony_ci
56438c2ecf20Sopenharmony_ci		rtm->rtm_flags |= nh_flags;
56448c2ecf20Sopenharmony_ci	} else {
56458c2ecf20Sopenharmony_ci		if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, AF_INET6,
56468c2ecf20Sopenharmony_ci				     &nh_flags, false) < 0)
56478c2ecf20Sopenharmony_ci			goto nla_put_failure;
56488c2ecf20Sopenharmony_ci
56498c2ecf20Sopenharmony_ci		rtm->rtm_flags |= nh_flags;
56508c2ecf20Sopenharmony_ci	}
56518c2ecf20Sopenharmony_ci
56528c2ecf20Sopenharmony_ci	if (rt6_flags & RTF_EXPIRES) {
56538c2ecf20Sopenharmony_ci		expires = dst ? dst->expires : rt->expires;
56548c2ecf20Sopenharmony_ci		expires -= jiffies;
56558c2ecf20Sopenharmony_ci	}
56568c2ecf20Sopenharmony_ci
56578c2ecf20Sopenharmony_ci	if (!dst) {
56588c2ecf20Sopenharmony_ci		if (rt->offload)
56598c2ecf20Sopenharmony_ci			rtm->rtm_flags |= RTM_F_OFFLOAD;
56608c2ecf20Sopenharmony_ci		if (rt->trap)
56618c2ecf20Sopenharmony_ci			rtm->rtm_flags |= RTM_F_TRAP;
56628c2ecf20Sopenharmony_ci	}
56638c2ecf20Sopenharmony_ci
56648c2ecf20Sopenharmony_ci	if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
56658c2ecf20Sopenharmony_ci		goto nla_put_failure;
56668c2ecf20Sopenharmony_ci
56678c2ecf20Sopenharmony_ci	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
56688c2ecf20Sopenharmony_ci		goto nla_put_failure;
56698c2ecf20Sopenharmony_ci
56708c2ecf20Sopenharmony_ci
56718c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
56728c2ecf20Sopenharmony_ci	return 0;
56738c2ecf20Sopenharmony_ci
56748c2ecf20Sopenharmony_cinla_put_failure:
56758c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
56768c2ecf20Sopenharmony_ci	return -EMSGSIZE;
56778c2ecf20Sopenharmony_ci}
56788c2ecf20Sopenharmony_ci
56798c2ecf20Sopenharmony_cistatic int fib6_info_nh_uses_dev(struct fib6_nh *nh, void *arg)
56808c2ecf20Sopenharmony_ci{
56818c2ecf20Sopenharmony_ci	const struct net_device *dev = arg;
56828c2ecf20Sopenharmony_ci
56838c2ecf20Sopenharmony_ci	if (nh->fib_nh_dev == dev)
56848c2ecf20Sopenharmony_ci		return 1;
56858c2ecf20Sopenharmony_ci
56868c2ecf20Sopenharmony_ci	return 0;
56878c2ecf20Sopenharmony_ci}
56888c2ecf20Sopenharmony_ci
56898c2ecf20Sopenharmony_cistatic bool fib6_info_uses_dev(const struct fib6_info *f6i,
56908c2ecf20Sopenharmony_ci			       const struct net_device *dev)
56918c2ecf20Sopenharmony_ci{
56928c2ecf20Sopenharmony_ci	if (f6i->nh) {
56938c2ecf20Sopenharmony_ci		struct net_device *_dev = (struct net_device *)dev;
56948c2ecf20Sopenharmony_ci
56958c2ecf20Sopenharmony_ci		return !!nexthop_for_each_fib6_nh(f6i->nh,
56968c2ecf20Sopenharmony_ci						  fib6_info_nh_uses_dev,
56978c2ecf20Sopenharmony_ci						  _dev);
56988c2ecf20Sopenharmony_ci	}
56998c2ecf20Sopenharmony_ci
57008c2ecf20Sopenharmony_ci	if (f6i->fib6_nh->fib_nh_dev == dev)
57018c2ecf20Sopenharmony_ci		return true;
57028c2ecf20Sopenharmony_ci
57038c2ecf20Sopenharmony_ci	if (f6i->fib6_nsiblings) {
57048c2ecf20Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
57058c2ecf20Sopenharmony_ci
57068c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
57078c2ecf20Sopenharmony_ci					 &f6i->fib6_siblings, fib6_siblings) {
57088c2ecf20Sopenharmony_ci			if (sibling->fib6_nh->fib_nh_dev == dev)
57098c2ecf20Sopenharmony_ci				return true;
57108c2ecf20Sopenharmony_ci		}
57118c2ecf20Sopenharmony_ci	}
57128c2ecf20Sopenharmony_ci
57138c2ecf20Sopenharmony_ci	return false;
57148c2ecf20Sopenharmony_ci}
57158c2ecf20Sopenharmony_ci
57168c2ecf20Sopenharmony_cistruct fib6_nh_exception_dump_walker {
57178c2ecf20Sopenharmony_ci	struct rt6_rtnl_dump_arg *dump;
57188c2ecf20Sopenharmony_ci	struct fib6_info *rt;
57198c2ecf20Sopenharmony_ci	unsigned int flags;
57208c2ecf20Sopenharmony_ci	unsigned int skip;
57218c2ecf20Sopenharmony_ci	unsigned int count;
57228c2ecf20Sopenharmony_ci};
57238c2ecf20Sopenharmony_ci
57248c2ecf20Sopenharmony_cistatic int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg)
57258c2ecf20Sopenharmony_ci{
57268c2ecf20Sopenharmony_ci	struct fib6_nh_exception_dump_walker *w = arg;
57278c2ecf20Sopenharmony_ci	struct rt6_rtnl_dump_arg *dump = w->dump;
57288c2ecf20Sopenharmony_ci	struct rt6_exception_bucket *bucket;
57298c2ecf20Sopenharmony_ci	struct rt6_exception *rt6_ex;
57308c2ecf20Sopenharmony_ci	int i, err;
57318c2ecf20Sopenharmony_ci
57328c2ecf20Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, NULL);
57338c2ecf20Sopenharmony_ci	if (!bucket)
57348c2ecf20Sopenharmony_ci		return 0;
57358c2ecf20Sopenharmony_ci
57368c2ecf20Sopenharmony_ci	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
57378c2ecf20Sopenharmony_ci		hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
57388c2ecf20Sopenharmony_ci			if (w->skip) {
57398c2ecf20Sopenharmony_ci				w->skip--;
57408c2ecf20Sopenharmony_ci				continue;
57418c2ecf20Sopenharmony_ci			}
57428c2ecf20Sopenharmony_ci
57438c2ecf20Sopenharmony_ci			/* Expiration of entries doesn't bump sernum, insertion
57448c2ecf20Sopenharmony_ci			 * does. Removal is triggered by insertion, so we can
57458c2ecf20Sopenharmony_ci			 * rely on the fact that if entries change between two
57468c2ecf20Sopenharmony_ci			 * partial dumps, this node is scanned again completely,
57478c2ecf20Sopenharmony_ci			 * see rt6_insert_exception() and fib6_dump_table().
57488c2ecf20Sopenharmony_ci			 *
57498c2ecf20Sopenharmony_ci			 * Count expired entries we go through as handled
57508c2ecf20Sopenharmony_ci			 * entries that we'll skip next time, in case of partial
57518c2ecf20Sopenharmony_ci			 * node dump. Otherwise, if entries expire meanwhile,
57528c2ecf20Sopenharmony_ci			 * we'll skip the wrong amount.
57538c2ecf20Sopenharmony_ci			 */
57548c2ecf20Sopenharmony_ci			if (rt6_check_expired(rt6_ex->rt6i)) {
57558c2ecf20Sopenharmony_ci				w->count++;
57568c2ecf20Sopenharmony_ci				continue;
57578c2ecf20Sopenharmony_ci			}
57588c2ecf20Sopenharmony_ci
57598c2ecf20Sopenharmony_ci			err = rt6_fill_node(dump->net, dump->skb, w->rt,
57608c2ecf20Sopenharmony_ci					    &rt6_ex->rt6i->dst, NULL, NULL, 0,
57618c2ecf20Sopenharmony_ci					    RTM_NEWROUTE,
57628c2ecf20Sopenharmony_ci					    NETLINK_CB(dump->cb->skb).portid,
57638c2ecf20Sopenharmony_ci					    dump->cb->nlh->nlmsg_seq, w->flags);
57648c2ecf20Sopenharmony_ci			if (err)
57658c2ecf20Sopenharmony_ci				return err;
57668c2ecf20Sopenharmony_ci
57678c2ecf20Sopenharmony_ci			w->count++;
57688c2ecf20Sopenharmony_ci		}
57698c2ecf20Sopenharmony_ci		bucket++;
57708c2ecf20Sopenharmony_ci	}
57718c2ecf20Sopenharmony_ci
57728c2ecf20Sopenharmony_ci	return 0;
57738c2ecf20Sopenharmony_ci}
57748c2ecf20Sopenharmony_ci
57758c2ecf20Sopenharmony_ci/* Return -1 if done with node, number of handled routes on partial dump */
57768c2ecf20Sopenharmony_ciint rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip)
57778c2ecf20Sopenharmony_ci{
57788c2ecf20Sopenharmony_ci	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
57798c2ecf20Sopenharmony_ci	struct fib_dump_filter *filter = &arg->filter;
57808c2ecf20Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
57818c2ecf20Sopenharmony_ci	struct net *net = arg->net;
57828c2ecf20Sopenharmony_ci	int count = 0;
57838c2ecf20Sopenharmony_ci
57848c2ecf20Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry)
57858c2ecf20Sopenharmony_ci		return -1;
57868c2ecf20Sopenharmony_ci
57878c2ecf20Sopenharmony_ci	if ((filter->flags & RTM_F_PREFIX) &&
57888c2ecf20Sopenharmony_ci	    !(rt->fib6_flags & RTF_PREFIX_RT)) {
57898c2ecf20Sopenharmony_ci		/* success since this is not a prefix route */
57908c2ecf20Sopenharmony_ci		return -1;
57918c2ecf20Sopenharmony_ci	}
57928c2ecf20Sopenharmony_ci	if (filter->filter_set &&
57938c2ecf20Sopenharmony_ci	    ((filter->rt_type  && rt->fib6_type != filter->rt_type) ||
57948c2ecf20Sopenharmony_ci	     (filter->dev      && !fib6_info_uses_dev(rt, filter->dev)) ||
57958c2ecf20Sopenharmony_ci	     (filter->protocol && rt->fib6_protocol != filter->protocol))) {
57968c2ecf20Sopenharmony_ci		return -1;
57978c2ecf20Sopenharmony_ci	}
57988c2ecf20Sopenharmony_ci
57998c2ecf20Sopenharmony_ci	if (filter->filter_set ||
58008c2ecf20Sopenharmony_ci	    !filter->dump_routes || !filter->dump_exceptions) {
58018c2ecf20Sopenharmony_ci		flags |= NLM_F_DUMP_FILTERED;
58028c2ecf20Sopenharmony_ci	}
58038c2ecf20Sopenharmony_ci
58048c2ecf20Sopenharmony_ci	if (filter->dump_routes) {
58058c2ecf20Sopenharmony_ci		if (skip) {
58068c2ecf20Sopenharmony_ci			skip--;
58078c2ecf20Sopenharmony_ci		} else {
58088c2ecf20Sopenharmony_ci			if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL,
58098c2ecf20Sopenharmony_ci					  0, RTM_NEWROUTE,
58108c2ecf20Sopenharmony_ci					  NETLINK_CB(arg->cb->skb).portid,
58118c2ecf20Sopenharmony_ci					  arg->cb->nlh->nlmsg_seq, flags)) {
58128c2ecf20Sopenharmony_ci				return 0;
58138c2ecf20Sopenharmony_ci			}
58148c2ecf20Sopenharmony_ci			count++;
58158c2ecf20Sopenharmony_ci		}
58168c2ecf20Sopenharmony_ci	}
58178c2ecf20Sopenharmony_ci
58188c2ecf20Sopenharmony_ci	if (filter->dump_exceptions) {
58198c2ecf20Sopenharmony_ci		struct fib6_nh_exception_dump_walker w = { .dump = arg,
58208c2ecf20Sopenharmony_ci							   .rt = rt,
58218c2ecf20Sopenharmony_ci							   .flags = flags,
58228c2ecf20Sopenharmony_ci							   .skip = skip,
58238c2ecf20Sopenharmony_ci							   .count = 0 };
58248c2ecf20Sopenharmony_ci		int err;
58258c2ecf20Sopenharmony_ci
58268c2ecf20Sopenharmony_ci		rcu_read_lock();
58278c2ecf20Sopenharmony_ci		if (rt->nh) {
58288c2ecf20Sopenharmony_ci			err = nexthop_for_each_fib6_nh(rt->nh,
58298c2ecf20Sopenharmony_ci						       rt6_nh_dump_exceptions,
58308c2ecf20Sopenharmony_ci						       &w);
58318c2ecf20Sopenharmony_ci		} else {
58328c2ecf20Sopenharmony_ci			err = rt6_nh_dump_exceptions(rt->fib6_nh, &w);
58338c2ecf20Sopenharmony_ci		}
58348c2ecf20Sopenharmony_ci		rcu_read_unlock();
58358c2ecf20Sopenharmony_ci
58368c2ecf20Sopenharmony_ci		if (err)
58378c2ecf20Sopenharmony_ci			return count += w.count;
58388c2ecf20Sopenharmony_ci	}
58398c2ecf20Sopenharmony_ci
58408c2ecf20Sopenharmony_ci	return -1;
58418c2ecf20Sopenharmony_ci}
58428c2ecf20Sopenharmony_ci
58438c2ecf20Sopenharmony_cistatic int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
58448c2ecf20Sopenharmony_ci					const struct nlmsghdr *nlh,
58458c2ecf20Sopenharmony_ci					struct nlattr **tb,
58468c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
58478c2ecf20Sopenharmony_ci{
58488c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
58498c2ecf20Sopenharmony_ci	int i, err;
58508c2ecf20Sopenharmony_ci
58518c2ecf20Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
58528c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
58538c2ecf20Sopenharmony_ci				   "Invalid header for get route request");
58548c2ecf20Sopenharmony_ci		return -EINVAL;
58558c2ecf20Sopenharmony_ci	}
58568c2ecf20Sopenharmony_ci
58578c2ecf20Sopenharmony_ci	if (!netlink_strict_get_check(skb))
58588c2ecf20Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
58598c2ecf20Sopenharmony_ci					      rtm_ipv6_policy, extack);
58608c2ecf20Sopenharmony_ci
58618c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
58628c2ecf20Sopenharmony_ci	if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
58638c2ecf20Sopenharmony_ci	    (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
58648c2ecf20Sopenharmony_ci	    rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
58658c2ecf20Sopenharmony_ci	    rtm->rtm_type) {
58668c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
58678c2ecf20Sopenharmony_ci		return -EINVAL;
58688c2ecf20Sopenharmony_ci	}
58698c2ecf20Sopenharmony_ci	if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
58708c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
58718c2ecf20Sopenharmony_ci				   "Invalid flags for get route request");
58728c2ecf20Sopenharmony_ci		return -EINVAL;
58738c2ecf20Sopenharmony_ci	}
58748c2ecf20Sopenharmony_ci
58758c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
58768c2ecf20Sopenharmony_ci					    rtm_ipv6_policy, extack);
58778c2ecf20Sopenharmony_ci	if (err)
58788c2ecf20Sopenharmony_ci		return err;
58798c2ecf20Sopenharmony_ci
58808c2ecf20Sopenharmony_ci	if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
58818c2ecf20Sopenharmony_ci	    (tb[RTA_DST] && !rtm->rtm_dst_len)) {
58828c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
58838c2ecf20Sopenharmony_ci		return -EINVAL;
58848c2ecf20Sopenharmony_ci	}
58858c2ecf20Sopenharmony_ci
58868c2ecf20Sopenharmony_ci	for (i = 0; i <= RTA_MAX; i++) {
58878c2ecf20Sopenharmony_ci		if (!tb[i])
58888c2ecf20Sopenharmony_ci			continue;
58898c2ecf20Sopenharmony_ci
58908c2ecf20Sopenharmony_ci		switch (i) {
58918c2ecf20Sopenharmony_ci		case RTA_SRC:
58928c2ecf20Sopenharmony_ci		case RTA_DST:
58938c2ecf20Sopenharmony_ci		case RTA_IIF:
58948c2ecf20Sopenharmony_ci		case RTA_OIF:
58958c2ecf20Sopenharmony_ci		case RTA_MARK:
58968c2ecf20Sopenharmony_ci		case RTA_UID:
58978c2ecf20Sopenharmony_ci		case RTA_SPORT:
58988c2ecf20Sopenharmony_ci		case RTA_DPORT:
58998c2ecf20Sopenharmony_ci		case RTA_IP_PROTO:
59008c2ecf20Sopenharmony_ci			break;
59018c2ecf20Sopenharmony_ci		default:
59028c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
59038c2ecf20Sopenharmony_ci			return -EINVAL;
59048c2ecf20Sopenharmony_ci		}
59058c2ecf20Sopenharmony_ci	}
59068c2ecf20Sopenharmony_ci
59078c2ecf20Sopenharmony_ci	return 0;
59088c2ecf20Sopenharmony_ci}
59098c2ecf20Sopenharmony_ci
59108c2ecf20Sopenharmony_cistatic int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
59118c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
59128c2ecf20Sopenharmony_ci{
59138c2ecf20Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
59148c2ecf20Sopenharmony_ci	struct nlattr *tb[RTA_MAX+1];
59158c2ecf20Sopenharmony_ci	int err, iif = 0, oif = 0;
59168c2ecf20Sopenharmony_ci	struct fib6_info *from;
59178c2ecf20Sopenharmony_ci	struct dst_entry *dst;
59188c2ecf20Sopenharmony_ci	struct rt6_info *rt;
59198c2ecf20Sopenharmony_ci	struct sk_buff *skb;
59208c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
59218c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {};
59228c2ecf20Sopenharmony_ci	bool fibmatch;
59238c2ecf20Sopenharmony_ci
59248c2ecf20Sopenharmony_ci	err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
59258c2ecf20Sopenharmony_ci	if (err < 0)
59268c2ecf20Sopenharmony_ci		goto errout;
59278c2ecf20Sopenharmony_ci
59288c2ecf20Sopenharmony_ci	err = -EINVAL;
59298c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
59308c2ecf20Sopenharmony_ci	fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
59318c2ecf20Sopenharmony_ci	fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
59328c2ecf20Sopenharmony_ci
59338c2ecf20Sopenharmony_ci	if (tb[RTA_SRC]) {
59348c2ecf20Sopenharmony_ci		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
59358c2ecf20Sopenharmony_ci			goto errout;
59368c2ecf20Sopenharmony_ci
59378c2ecf20Sopenharmony_ci		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
59388c2ecf20Sopenharmony_ci	}
59398c2ecf20Sopenharmony_ci
59408c2ecf20Sopenharmony_ci	if (tb[RTA_DST]) {
59418c2ecf20Sopenharmony_ci		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
59428c2ecf20Sopenharmony_ci			goto errout;
59438c2ecf20Sopenharmony_ci
59448c2ecf20Sopenharmony_ci		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
59458c2ecf20Sopenharmony_ci	}
59468c2ecf20Sopenharmony_ci
59478c2ecf20Sopenharmony_ci	if (tb[RTA_IIF])
59488c2ecf20Sopenharmony_ci		iif = nla_get_u32(tb[RTA_IIF]);
59498c2ecf20Sopenharmony_ci
59508c2ecf20Sopenharmony_ci	if (tb[RTA_OIF])
59518c2ecf20Sopenharmony_ci		oif = nla_get_u32(tb[RTA_OIF]);
59528c2ecf20Sopenharmony_ci
59538c2ecf20Sopenharmony_ci	if (tb[RTA_MARK])
59548c2ecf20Sopenharmony_ci		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
59558c2ecf20Sopenharmony_ci
59568c2ecf20Sopenharmony_ci	if (tb[RTA_UID])
59578c2ecf20Sopenharmony_ci		fl6.flowi6_uid = make_kuid(current_user_ns(),
59588c2ecf20Sopenharmony_ci					   nla_get_u32(tb[RTA_UID]));
59598c2ecf20Sopenharmony_ci	else
59608c2ecf20Sopenharmony_ci		fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
59618c2ecf20Sopenharmony_ci
59628c2ecf20Sopenharmony_ci	if (tb[RTA_SPORT])
59638c2ecf20Sopenharmony_ci		fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
59648c2ecf20Sopenharmony_ci
59658c2ecf20Sopenharmony_ci	if (tb[RTA_DPORT])
59668c2ecf20Sopenharmony_ci		fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
59678c2ecf20Sopenharmony_ci
59688c2ecf20Sopenharmony_ci	if (tb[RTA_IP_PROTO]) {
59698c2ecf20Sopenharmony_ci		err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
59708c2ecf20Sopenharmony_ci						  &fl6.flowi6_proto, AF_INET6,
59718c2ecf20Sopenharmony_ci						  extack);
59728c2ecf20Sopenharmony_ci		if (err)
59738c2ecf20Sopenharmony_ci			goto errout;
59748c2ecf20Sopenharmony_ci	}
59758c2ecf20Sopenharmony_ci
59768c2ecf20Sopenharmony_ci	if (iif) {
59778c2ecf20Sopenharmony_ci		struct net_device *dev;
59788c2ecf20Sopenharmony_ci		int flags = 0;
59798c2ecf20Sopenharmony_ci
59808c2ecf20Sopenharmony_ci		rcu_read_lock();
59818c2ecf20Sopenharmony_ci
59828c2ecf20Sopenharmony_ci		dev = dev_get_by_index_rcu(net, iif);
59838c2ecf20Sopenharmony_ci		if (!dev) {
59848c2ecf20Sopenharmony_ci			rcu_read_unlock();
59858c2ecf20Sopenharmony_ci			err = -ENODEV;
59868c2ecf20Sopenharmony_ci			goto errout;
59878c2ecf20Sopenharmony_ci		}
59888c2ecf20Sopenharmony_ci
59898c2ecf20Sopenharmony_ci		fl6.flowi6_iif = iif;
59908c2ecf20Sopenharmony_ci
59918c2ecf20Sopenharmony_ci		if (!ipv6_addr_any(&fl6.saddr))
59928c2ecf20Sopenharmony_ci			flags |= RT6_LOOKUP_F_HAS_SADDR;
59938c2ecf20Sopenharmony_ci
59948c2ecf20Sopenharmony_ci		dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
59958c2ecf20Sopenharmony_ci
59968c2ecf20Sopenharmony_ci		rcu_read_unlock();
59978c2ecf20Sopenharmony_ci	} else {
59988c2ecf20Sopenharmony_ci		fl6.flowi6_oif = oif;
59998c2ecf20Sopenharmony_ci
60008c2ecf20Sopenharmony_ci		dst = ip6_route_output(net, NULL, &fl6);
60018c2ecf20Sopenharmony_ci	}
60028c2ecf20Sopenharmony_ci
60038c2ecf20Sopenharmony_ci
60048c2ecf20Sopenharmony_ci	rt = container_of(dst, struct rt6_info, dst);
60058c2ecf20Sopenharmony_ci	if (rt->dst.error) {
60068c2ecf20Sopenharmony_ci		err = rt->dst.error;
60078c2ecf20Sopenharmony_ci		ip6_rt_put(rt);
60088c2ecf20Sopenharmony_ci		goto errout;
60098c2ecf20Sopenharmony_ci	}
60108c2ecf20Sopenharmony_ci
60118c2ecf20Sopenharmony_ci	if (rt == net->ipv6.ip6_null_entry) {
60128c2ecf20Sopenharmony_ci		err = rt->dst.error;
60138c2ecf20Sopenharmony_ci		ip6_rt_put(rt);
60148c2ecf20Sopenharmony_ci		goto errout;
60158c2ecf20Sopenharmony_ci	}
60168c2ecf20Sopenharmony_ci
60178c2ecf20Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
60188c2ecf20Sopenharmony_ci	if (!skb) {
60198c2ecf20Sopenharmony_ci		ip6_rt_put(rt);
60208c2ecf20Sopenharmony_ci		err = -ENOBUFS;
60218c2ecf20Sopenharmony_ci		goto errout;
60228c2ecf20Sopenharmony_ci	}
60238c2ecf20Sopenharmony_ci
60248c2ecf20Sopenharmony_ci	skb_dst_set(skb, &rt->dst);
60258c2ecf20Sopenharmony_ci
60268c2ecf20Sopenharmony_ci	rcu_read_lock();
60278c2ecf20Sopenharmony_ci	from = rcu_dereference(rt->from);
60288c2ecf20Sopenharmony_ci	if (from) {
60298c2ecf20Sopenharmony_ci		if (fibmatch)
60308c2ecf20Sopenharmony_ci			err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
60318c2ecf20Sopenharmony_ci					    iif, RTM_NEWROUTE,
60328c2ecf20Sopenharmony_ci					    NETLINK_CB(in_skb).portid,
60338c2ecf20Sopenharmony_ci					    nlh->nlmsg_seq, 0);
60348c2ecf20Sopenharmony_ci		else
60358c2ecf20Sopenharmony_ci			err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
60368c2ecf20Sopenharmony_ci					    &fl6.saddr, iif, RTM_NEWROUTE,
60378c2ecf20Sopenharmony_ci					    NETLINK_CB(in_skb).portid,
60388c2ecf20Sopenharmony_ci					    nlh->nlmsg_seq, 0);
60398c2ecf20Sopenharmony_ci	} else {
60408c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
60418c2ecf20Sopenharmony_ci	}
60428c2ecf20Sopenharmony_ci	rcu_read_unlock();
60438c2ecf20Sopenharmony_ci
60448c2ecf20Sopenharmony_ci	if (err < 0) {
60458c2ecf20Sopenharmony_ci		kfree_skb(skb);
60468c2ecf20Sopenharmony_ci		goto errout;
60478c2ecf20Sopenharmony_ci	}
60488c2ecf20Sopenharmony_ci
60498c2ecf20Sopenharmony_ci	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
60508c2ecf20Sopenharmony_cierrout:
60518c2ecf20Sopenharmony_ci	return err;
60528c2ecf20Sopenharmony_ci}
60538c2ecf20Sopenharmony_ci
60548c2ecf20Sopenharmony_civoid inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
60558c2ecf20Sopenharmony_ci		     unsigned int nlm_flags)
60568c2ecf20Sopenharmony_ci{
60578c2ecf20Sopenharmony_ci	struct sk_buff *skb;
60588c2ecf20Sopenharmony_ci	struct net *net = info->nl_net;
60598c2ecf20Sopenharmony_ci	u32 seq;
60608c2ecf20Sopenharmony_ci	int err;
60618c2ecf20Sopenharmony_ci
60628c2ecf20Sopenharmony_ci	err = -ENOBUFS;
60638c2ecf20Sopenharmony_ci	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
60648c2ecf20Sopenharmony_ci
60658c2ecf20Sopenharmony_ci	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
60668c2ecf20Sopenharmony_ci	if (!skb)
60678c2ecf20Sopenharmony_ci		goto errout;
60688c2ecf20Sopenharmony_ci
60698c2ecf20Sopenharmony_ci	err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
60708c2ecf20Sopenharmony_ci			    event, info->portid, seq, nlm_flags);
60718c2ecf20Sopenharmony_ci	if (err < 0) {
60728c2ecf20Sopenharmony_ci		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
60738c2ecf20Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
60748c2ecf20Sopenharmony_ci		kfree_skb(skb);
60758c2ecf20Sopenharmony_ci		goto errout;
60768c2ecf20Sopenharmony_ci	}
60778c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
60788c2ecf20Sopenharmony_ci		    info->nlh, gfp_any());
60798c2ecf20Sopenharmony_ci	return;
60808c2ecf20Sopenharmony_cierrout:
60818c2ecf20Sopenharmony_ci	if (err < 0)
60828c2ecf20Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
60838c2ecf20Sopenharmony_ci}
60848c2ecf20Sopenharmony_ci
60858c2ecf20Sopenharmony_civoid fib6_rt_update(struct net *net, struct fib6_info *rt,
60868c2ecf20Sopenharmony_ci		    struct nl_info *info)
60878c2ecf20Sopenharmony_ci{
60888c2ecf20Sopenharmony_ci	u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
60898c2ecf20Sopenharmony_ci	struct sk_buff *skb;
60908c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
60918c2ecf20Sopenharmony_ci
60928c2ecf20Sopenharmony_ci	/* call_fib6_entry_notifiers will be removed when in-kernel notifier
60938c2ecf20Sopenharmony_ci	 * is implemented and supported for nexthop objects
60948c2ecf20Sopenharmony_ci	 */
60958c2ecf20Sopenharmony_ci	call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, rt, NULL);
60968c2ecf20Sopenharmony_ci
60978c2ecf20Sopenharmony_ci	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
60988c2ecf20Sopenharmony_ci	if (!skb)
60998c2ecf20Sopenharmony_ci		goto errout;
61008c2ecf20Sopenharmony_ci
61018c2ecf20Sopenharmony_ci	err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
61028c2ecf20Sopenharmony_ci			    RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE);
61038c2ecf20Sopenharmony_ci	if (err < 0) {
61048c2ecf20Sopenharmony_ci		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
61058c2ecf20Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
61068c2ecf20Sopenharmony_ci		kfree_skb(skb);
61078c2ecf20Sopenharmony_ci		goto errout;
61088c2ecf20Sopenharmony_ci	}
61098c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
61108c2ecf20Sopenharmony_ci		    info->nlh, gfp_any());
61118c2ecf20Sopenharmony_ci	return;
61128c2ecf20Sopenharmony_cierrout:
61138c2ecf20Sopenharmony_ci	if (err < 0)
61148c2ecf20Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
61158c2ecf20Sopenharmony_ci}
61168c2ecf20Sopenharmony_ci
61178c2ecf20Sopenharmony_cistatic int ip6_route_dev_notify(struct notifier_block *this,
61188c2ecf20Sopenharmony_ci				unsigned long event, void *ptr)
61198c2ecf20Sopenharmony_ci{
61208c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
61218c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
61228c2ecf20Sopenharmony_ci
61238c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_LOOPBACK))
61248c2ecf20Sopenharmony_ci		return NOTIFY_OK;
61258c2ecf20Sopenharmony_ci
61268c2ecf20Sopenharmony_ci	if (event == NETDEV_REGISTER) {
61278c2ecf20Sopenharmony_ci		net->ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = dev;
61288c2ecf20Sopenharmony_ci		net->ipv6.ip6_null_entry->dst.dev = dev;
61298c2ecf20Sopenharmony_ci		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
61308c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
61318c2ecf20Sopenharmony_ci		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
61328c2ecf20Sopenharmony_ci		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
61338c2ecf20Sopenharmony_ci		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
61348c2ecf20Sopenharmony_ci		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
61358c2ecf20Sopenharmony_ci#endif
61368c2ecf20Sopenharmony_ci	 } else if (event == NETDEV_UNREGISTER &&
61378c2ecf20Sopenharmony_ci		    dev->reg_state != NETREG_UNREGISTERED) {
61388c2ecf20Sopenharmony_ci		/* NETDEV_UNREGISTER could be fired for multiple times by
61398c2ecf20Sopenharmony_ci		 * netdev_wait_allrefs(). Make sure we only call this once.
61408c2ecf20Sopenharmony_ci		 */
61418c2ecf20Sopenharmony_ci		in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
61428c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
61438c2ecf20Sopenharmony_ci		in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
61448c2ecf20Sopenharmony_ci		in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
61458c2ecf20Sopenharmony_ci#endif
61468c2ecf20Sopenharmony_ci	}
61478c2ecf20Sopenharmony_ci
61488c2ecf20Sopenharmony_ci	return NOTIFY_OK;
61498c2ecf20Sopenharmony_ci}
61508c2ecf20Sopenharmony_ci
61518c2ecf20Sopenharmony_ci/*
61528c2ecf20Sopenharmony_ci *	/proc
61538c2ecf20Sopenharmony_ci */
61548c2ecf20Sopenharmony_ci
61558c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
61568c2ecf20Sopenharmony_cistatic int rt6_stats_seq_show(struct seq_file *seq, void *v)
61578c2ecf20Sopenharmony_ci{
61588c2ecf20Sopenharmony_ci	struct net *net = (struct net *)seq->private;
61598c2ecf20Sopenharmony_ci	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
61608c2ecf20Sopenharmony_ci		   net->ipv6.rt6_stats->fib_nodes,
61618c2ecf20Sopenharmony_ci		   net->ipv6.rt6_stats->fib_route_nodes,
61628c2ecf20Sopenharmony_ci		   atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
61638c2ecf20Sopenharmony_ci		   net->ipv6.rt6_stats->fib_rt_entries,
61648c2ecf20Sopenharmony_ci		   net->ipv6.rt6_stats->fib_rt_cache,
61658c2ecf20Sopenharmony_ci		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
61668c2ecf20Sopenharmony_ci		   net->ipv6.rt6_stats->fib_discarded_routes);
61678c2ecf20Sopenharmony_ci
61688c2ecf20Sopenharmony_ci	return 0;
61698c2ecf20Sopenharmony_ci}
61708c2ecf20Sopenharmony_ci#endif	/* CONFIG_PROC_FS */
61718c2ecf20Sopenharmony_ci
61728c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
61738c2ecf20Sopenharmony_ci
61748c2ecf20Sopenharmony_cistatic int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
61758c2ecf20Sopenharmony_ci			      void *buffer, size_t *lenp, loff_t *ppos)
61768c2ecf20Sopenharmony_ci{
61778c2ecf20Sopenharmony_ci	struct net *net;
61788c2ecf20Sopenharmony_ci	int delay;
61798c2ecf20Sopenharmony_ci	int ret;
61808c2ecf20Sopenharmony_ci	if (!write)
61818c2ecf20Sopenharmony_ci		return -EINVAL;
61828c2ecf20Sopenharmony_ci
61838c2ecf20Sopenharmony_ci	net = (struct net *)ctl->extra1;
61848c2ecf20Sopenharmony_ci	delay = net->ipv6.sysctl.flush_delay;
61858c2ecf20Sopenharmony_ci	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
61868c2ecf20Sopenharmony_ci	if (ret)
61878c2ecf20Sopenharmony_ci		return ret;
61888c2ecf20Sopenharmony_ci
61898c2ecf20Sopenharmony_ci	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
61908c2ecf20Sopenharmony_ci	return 0;
61918c2ecf20Sopenharmony_ci}
61928c2ecf20Sopenharmony_ci
61938c2ecf20Sopenharmony_cistatic struct ctl_table ipv6_route_table_template[] = {
61948c2ecf20Sopenharmony_ci	{
61958c2ecf20Sopenharmony_ci		.procname	=	"flush",
61968c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.flush_delay,
61978c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
61988c2ecf20Sopenharmony_ci		.mode		=	0200,
61998c2ecf20Sopenharmony_ci		.proc_handler	=	ipv6_sysctl_rtcache_flush
62008c2ecf20Sopenharmony_ci	},
62018c2ecf20Sopenharmony_ci	{
62028c2ecf20Sopenharmony_ci		.procname	=	"gc_thresh",
62038c2ecf20Sopenharmony_ci		.data		=	&ip6_dst_ops_template.gc_thresh,
62048c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62058c2ecf20Sopenharmony_ci		.mode		=	0644,
62068c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec,
62078c2ecf20Sopenharmony_ci	},
62088c2ecf20Sopenharmony_ci	{
62098c2ecf20Sopenharmony_ci		.procname	=	"max_size",
62108c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
62118c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62128c2ecf20Sopenharmony_ci		.mode		=	0644,
62138c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec,
62148c2ecf20Sopenharmony_ci	},
62158c2ecf20Sopenharmony_ci	{
62168c2ecf20Sopenharmony_ci		.procname	=	"gc_min_interval",
62178c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
62188c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62198c2ecf20Sopenharmony_ci		.mode		=	0644,
62208c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
62218c2ecf20Sopenharmony_ci	},
62228c2ecf20Sopenharmony_ci	{
62238c2ecf20Sopenharmony_ci		.procname	=	"gc_timeout",
62248c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
62258c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62268c2ecf20Sopenharmony_ci		.mode		=	0644,
62278c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
62288c2ecf20Sopenharmony_ci	},
62298c2ecf20Sopenharmony_ci	{
62308c2ecf20Sopenharmony_ci		.procname	=	"gc_interval",
62318c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
62328c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62338c2ecf20Sopenharmony_ci		.mode		=	0644,
62348c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
62358c2ecf20Sopenharmony_ci	},
62368c2ecf20Sopenharmony_ci	{
62378c2ecf20Sopenharmony_ci		.procname	=	"gc_elasticity",
62388c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
62398c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62408c2ecf20Sopenharmony_ci		.mode		=	0644,
62418c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec,
62428c2ecf20Sopenharmony_ci	},
62438c2ecf20Sopenharmony_ci	{
62448c2ecf20Sopenharmony_ci		.procname	=	"mtu_expires",
62458c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
62468c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62478c2ecf20Sopenharmony_ci		.mode		=	0644,
62488c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
62498c2ecf20Sopenharmony_ci	},
62508c2ecf20Sopenharmony_ci	{
62518c2ecf20Sopenharmony_ci		.procname	=	"min_adv_mss",
62528c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
62538c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62548c2ecf20Sopenharmony_ci		.mode		=	0644,
62558c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec,
62568c2ecf20Sopenharmony_ci	},
62578c2ecf20Sopenharmony_ci	{
62588c2ecf20Sopenharmony_ci		.procname	=	"gc_min_interval_ms",
62598c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
62608c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62618c2ecf20Sopenharmony_ci		.mode		=	0644,
62628c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec_ms_jiffies,
62638c2ecf20Sopenharmony_ci	},
62648c2ecf20Sopenharmony_ci	{
62658c2ecf20Sopenharmony_ci		.procname	=	"skip_notify_on_dev_down",
62668c2ecf20Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.skip_notify_on_dev_down,
62678c2ecf20Sopenharmony_ci		.maxlen		=	sizeof(int),
62688c2ecf20Sopenharmony_ci		.mode		=	0644,
62698c2ecf20Sopenharmony_ci		.proc_handler	=	proc_dointvec_minmax,
62708c2ecf20Sopenharmony_ci		.extra1		=	SYSCTL_ZERO,
62718c2ecf20Sopenharmony_ci		.extra2		=	SYSCTL_ONE,
62728c2ecf20Sopenharmony_ci	},
62738c2ecf20Sopenharmony_ci	{ }
62748c2ecf20Sopenharmony_ci};
62758c2ecf20Sopenharmony_ci
62768c2ecf20Sopenharmony_cistruct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
62778c2ecf20Sopenharmony_ci{
62788c2ecf20Sopenharmony_ci	struct ctl_table *table;
62798c2ecf20Sopenharmony_ci
62808c2ecf20Sopenharmony_ci	table = kmemdup(ipv6_route_table_template,
62818c2ecf20Sopenharmony_ci			sizeof(ipv6_route_table_template),
62828c2ecf20Sopenharmony_ci			GFP_KERNEL);
62838c2ecf20Sopenharmony_ci
62848c2ecf20Sopenharmony_ci	if (table) {
62858c2ecf20Sopenharmony_ci		table[0].data = &net->ipv6.sysctl.flush_delay;
62868c2ecf20Sopenharmony_ci		table[0].extra1 = net;
62878c2ecf20Sopenharmony_ci		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
62888c2ecf20Sopenharmony_ci		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
62898c2ecf20Sopenharmony_ci		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
62908c2ecf20Sopenharmony_ci		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
62918c2ecf20Sopenharmony_ci		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
62928c2ecf20Sopenharmony_ci		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
62938c2ecf20Sopenharmony_ci		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
62948c2ecf20Sopenharmony_ci		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
62958c2ecf20Sopenharmony_ci		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
62968c2ecf20Sopenharmony_ci		table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
62978c2ecf20Sopenharmony_ci
62988c2ecf20Sopenharmony_ci		/* Don't export sysctls to unprivileged users */
62998c2ecf20Sopenharmony_ci		if (net->user_ns != &init_user_ns)
63008c2ecf20Sopenharmony_ci			table[0].procname = NULL;
63018c2ecf20Sopenharmony_ci	}
63028c2ecf20Sopenharmony_ci
63038c2ecf20Sopenharmony_ci	return table;
63048c2ecf20Sopenharmony_ci}
63058c2ecf20Sopenharmony_ci#endif
63068c2ecf20Sopenharmony_ci
63078c2ecf20Sopenharmony_cistatic int __net_init ip6_route_net_init(struct net *net)
63088c2ecf20Sopenharmony_ci{
63098c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
63108c2ecf20Sopenharmony_ci
63118c2ecf20Sopenharmony_ci	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
63128c2ecf20Sopenharmony_ci	       sizeof(net->ipv6.ip6_dst_ops));
63138c2ecf20Sopenharmony_ci
63148c2ecf20Sopenharmony_ci	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
63158c2ecf20Sopenharmony_ci		goto out_ip6_dst_ops;
63168c2ecf20Sopenharmony_ci
63178c2ecf20Sopenharmony_ci	net->ipv6.fib6_null_entry = fib6_info_alloc(GFP_KERNEL, true);
63188c2ecf20Sopenharmony_ci	if (!net->ipv6.fib6_null_entry)
63198c2ecf20Sopenharmony_ci		goto out_ip6_dst_entries;
63208c2ecf20Sopenharmony_ci	memcpy(net->ipv6.fib6_null_entry, &fib6_null_entry_template,
63218c2ecf20Sopenharmony_ci	       sizeof(*net->ipv6.fib6_null_entry));
63228c2ecf20Sopenharmony_ci
63238c2ecf20Sopenharmony_ci	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
63248c2ecf20Sopenharmony_ci					   sizeof(*net->ipv6.ip6_null_entry),
63258c2ecf20Sopenharmony_ci					   GFP_KERNEL);
63268c2ecf20Sopenharmony_ci	if (!net->ipv6.ip6_null_entry)
63278c2ecf20Sopenharmony_ci		goto out_fib6_null_entry;
63288c2ecf20Sopenharmony_ci	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
63298c2ecf20Sopenharmony_ci	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
63308c2ecf20Sopenharmony_ci			 ip6_template_metrics, true);
63318c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.ip6_null_entry->rt6i_uncached);
63328c2ecf20Sopenharmony_ci
63338c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
63348c2ecf20Sopenharmony_ci	net->ipv6.fib6_has_custom_rules = false;
63358c2ecf20Sopenharmony_ci	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
63368c2ecf20Sopenharmony_ci					       sizeof(*net->ipv6.ip6_prohibit_entry),
63378c2ecf20Sopenharmony_ci					       GFP_KERNEL);
63388c2ecf20Sopenharmony_ci	if (!net->ipv6.ip6_prohibit_entry)
63398c2ecf20Sopenharmony_ci		goto out_ip6_null_entry;
63408c2ecf20Sopenharmony_ci	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
63418c2ecf20Sopenharmony_ci	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
63428c2ecf20Sopenharmony_ci			 ip6_template_metrics, true);
63438c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached);
63448c2ecf20Sopenharmony_ci
63458c2ecf20Sopenharmony_ci	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
63468c2ecf20Sopenharmony_ci					       sizeof(*net->ipv6.ip6_blk_hole_entry),
63478c2ecf20Sopenharmony_ci					       GFP_KERNEL);
63488c2ecf20Sopenharmony_ci	if (!net->ipv6.ip6_blk_hole_entry)
63498c2ecf20Sopenharmony_ci		goto out_ip6_prohibit_entry;
63508c2ecf20Sopenharmony_ci	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
63518c2ecf20Sopenharmony_ci	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
63528c2ecf20Sopenharmony_ci			 ip6_template_metrics, true);
63538c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.ip6_blk_hole_entry->rt6i_uncached);
63548c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
63558c2ecf20Sopenharmony_ci	net->ipv6.fib6_routes_require_src = 0;
63568c2ecf20Sopenharmony_ci#endif
63578c2ecf20Sopenharmony_ci#endif
63588c2ecf20Sopenharmony_ci
63598c2ecf20Sopenharmony_ci	net->ipv6.sysctl.flush_delay = 0;
63608c2ecf20Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_max_size = INT_MAX;
63618c2ecf20Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
63628c2ecf20Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
63638c2ecf20Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
63648c2ecf20Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
63658c2ecf20Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
63668c2ecf20Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
63678c2ecf20Sopenharmony_ci	net->ipv6.sysctl.skip_notify_on_dev_down = 0;
63688c2ecf20Sopenharmony_ci
63698c2ecf20Sopenharmony_ci	atomic_set(&net->ipv6.ip6_rt_gc_expire, 30*HZ);
63708c2ecf20Sopenharmony_ci
63718c2ecf20Sopenharmony_ci	ret = 0;
63728c2ecf20Sopenharmony_ciout:
63738c2ecf20Sopenharmony_ci	return ret;
63748c2ecf20Sopenharmony_ci
63758c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
63768c2ecf20Sopenharmony_ciout_ip6_prohibit_entry:
63778c2ecf20Sopenharmony_ci	kfree(net->ipv6.ip6_prohibit_entry);
63788c2ecf20Sopenharmony_ciout_ip6_null_entry:
63798c2ecf20Sopenharmony_ci	kfree(net->ipv6.ip6_null_entry);
63808c2ecf20Sopenharmony_ci#endif
63818c2ecf20Sopenharmony_ciout_fib6_null_entry:
63828c2ecf20Sopenharmony_ci	kfree(net->ipv6.fib6_null_entry);
63838c2ecf20Sopenharmony_ciout_ip6_dst_entries:
63848c2ecf20Sopenharmony_ci	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
63858c2ecf20Sopenharmony_ciout_ip6_dst_ops:
63868c2ecf20Sopenharmony_ci	goto out;
63878c2ecf20Sopenharmony_ci}
63888c2ecf20Sopenharmony_ci
63898c2ecf20Sopenharmony_cistatic void __net_exit ip6_route_net_exit(struct net *net)
63908c2ecf20Sopenharmony_ci{
63918c2ecf20Sopenharmony_ci	kfree(net->ipv6.fib6_null_entry);
63928c2ecf20Sopenharmony_ci	kfree(net->ipv6.ip6_null_entry);
63938c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
63948c2ecf20Sopenharmony_ci	kfree(net->ipv6.ip6_prohibit_entry);
63958c2ecf20Sopenharmony_ci	kfree(net->ipv6.ip6_blk_hole_entry);
63968c2ecf20Sopenharmony_ci#endif
63978c2ecf20Sopenharmony_ci	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
63988c2ecf20Sopenharmony_ci}
63998c2ecf20Sopenharmony_ci
64008c2ecf20Sopenharmony_cistatic int __net_init ip6_route_net_init_late(struct net *net)
64018c2ecf20Sopenharmony_ci{
64028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
64038c2ecf20Sopenharmony_ci	if (!proc_create_net("ipv6_route", 0, net->proc_net,
64048c2ecf20Sopenharmony_ci			     &ipv6_route_seq_ops,
64058c2ecf20Sopenharmony_ci			     sizeof(struct ipv6_route_iter)))
64068c2ecf20Sopenharmony_ci		return -ENOMEM;
64078c2ecf20Sopenharmony_ci
64088c2ecf20Sopenharmony_ci	if (!proc_create_net_single("rt6_stats", 0444, net->proc_net,
64098c2ecf20Sopenharmony_ci				    rt6_stats_seq_show, NULL)) {
64108c2ecf20Sopenharmony_ci		remove_proc_entry("ipv6_route", net->proc_net);
64118c2ecf20Sopenharmony_ci		return -ENOMEM;
64128c2ecf20Sopenharmony_ci	}
64138c2ecf20Sopenharmony_ci#endif
64148c2ecf20Sopenharmony_ci	return 0;
64158c2ecf20Sopenharmony_ci}
64168c2ecf20Sopenharmony_ci
64178c2ecf20Sopenharmony_cistatic void __net_exit ip6_route_net_exit_late(struct net *net)
64188c2ecf20Sopenharmony_ci{
64198c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
64208c2ecf20Sopenharmony_ci	remove_proc_entry("ipv6_route", net->proc_net);
64218c2ecf20Sopenharmony_ci	remove_proc_entry("rt6_stats", net->proc_net);
64228c2ecf20Sopenharmony_ci#endif
64238c2ecf20Sopenharmony_ci}
64248c2ecf20Sopenharmony_ci
64258c2ecf20Sopenharmony_cistatic struct pernet_operations ip6_route_net_ops = {
64268c2ecf20Sopenharmony_ci	.init = ip6_route_net_init,
64278c2ecf20Sopenharmony_ci	.exit = ip6_route_net_exit,
64288c2ecf20Sopenharmony_ci};
64298c2ecf20Sopenharmony_ci
64308c2ecf20Sopenharmony_cistatic int __net_init ipv6_inetpeer_init(struct net *net)
64318c2ecf20Sopenharmony_ci{
64328c2ecf20Sopenharmony_ci	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
64338c2ecf20Sopenharmony_ci
64348c2ecf20Sopenharmony_ci	if (!bp)
64358c2ecf20Sopenharmony_ci		return -ENOMEM;
64368c2ecf20Sopenharmony_ci	inet_peer_base_init(bp);
64378c2ecf20Sopenharmony_ci	net->ipv6.peers = bp;
64388c2ecf20Sopenharmony_ci	return 0;
64398c2ecf20Sopenharmony_ci}
64408c2ecf20Sopenharmony_ci
64418c2ecf20Sopenharmony_cistatic void __net_exit ipv6_inetpeer_exit(struct net *net)
64428c2ecf20Sopenharmony_ci{
64438c2ecf20Sopenharmony_ci	struct inet_peer_base *bp = net->ipv6.peers;
64448c2ecf20Sopenharmony_ci
64458c2ecf20Sopenharmony_ci	net->ipv6.peers = NULL;
64468c2ecf20Sopenharmony_ci	inetpeer_invalidate_tree(bp);
64478c2ecf20Sopenharmony_ci	kfree(bp);
64488c2ecf20Sopenharmony_ci}
64498c2ecf20Sopenharmony_ci
64508c2ecf20Sopenharmony_cistatic struct pernet_operations ipv6_inetpeer_ops = {
64518c2ecf20Sopenharmony_ci	.init	=	ipv6_inetpeer_init,
64528c2ecf20Sopenharmony_ci	.exit	=	ipv6_inetpeer_exit,
64538c2ecf20Sopenharmony_ci};
64548c2ecf20Sopenharmony_ci
64558c2ecf20Sopenharmony_cistatic struct pernet_operations ip6_route_net_late_ops = {
64568c2ecf20Sopenharmony_ci	.init = ip6_route_net_init_late,
64578c2ecf20Sopenharmony_ci	.exit = ip6_route_net_exit_late,
64588c2ecf20Sopenharmony_ci};
64598c2ecf20Sopenharmony_ci
64608c2ecf20Sopenharmony_cistatic struct notifier_block ip6_route_dev_notifier = {
64618c2ecf20Sopenharmony_ci	.notifier_call = ip6_route_dev_notify,
64628c2ecf20Sopenharmony_ci	.priority = ADDRCONF_NOTIFY_PRIORITY - 10,
64638c2ecf20Sopenharmony_ci};
64648c2ecf20Sopenharmony_ci
64658c2ecf20Sopenharmony_civoid __init ip6_route_init_special_entries(void)
64668c2ecf20Sopenharmony_ci{
64678c2ecf20Sopenharmony_ci	/* Registering of the loopback is done before this portion of code,
64688c2ecf20Sopenharmony_ci	 * the loopback reference in rt6_info will not be taken, do it
64698c2ecf20Sopenharmony_ci	 * manually for init_net */
64708c2ecf20Sopenharmony_ci	init_net.ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = init_net.loopback_dev;
64718c2ecf20Sopenharmony_ci	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
64728c2ecf20Sopenharmony_ci	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
64738c2ecf20Sopenharmony_ci  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
64748c2ecf20Sopenharmony_ci	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
64758c2ecf20Sopenharmony_ci	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
64768c2ecf20Sopenharmony_ci	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
64778c2ecf20Sopenharmony_ci	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
64788c2ecf20Sopenharmony_ci  #endif
64798c2ecf20Sopenharmony_ci}
64808c2ecf20Sopenharmony_ci
64818c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6)
64828c2ecf20Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
64838c2ecf20Sopenharmony_ciDEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt)
64848c2ecf20Sopenharmony_ci
64858c2ecf20Sopenharmony_ciBTF_ID_LIST(btf_fib6_info_id)
64868c2ecf20Sopenharmony_ciBTF_ID(struct, fib6_info)
64878c2ecf20Sopenharmony_ci
64888c2ecf20Sopenharmony_cistatic const struct bpf_iter_seq_info ipv6_route_seq_info = {
64898c2ecf20Sopenharmony_ci	.seq_ops		= &ipv6_route_seq_ops,
64908c2ecf20Sopenharmony_ci	.init_seq_private	= bpf_iter_init_seq_net,
64918c2ecf20Sopenharmony_ci	.fini_seq_private	= bpf_iter_fini_seq_net,
64928c2ecf20Sopenharmony_ci	.seq_priv_size		= sizeof(struct ipv6_route_iter),
64938c2ecf20Sopenharmony_ci};
64948c2ecf20Sopenharmony_ci
64958c2ecf20Sopenharmony_cistatic struct bpf_iter_reg ipv6_route_reg_info = {
64968c2ecf20Sopenharmony_ci	.target			= "ipv6_route",
64978c2ecf20Sopenharmony_ci	.ctx_arg_info_size	= 1,
64988c2ecf20Sopenharmony_ci	.ctx_arg_info		= {
64998c2ecf20Sopenharmony_ci		{ offsetof(struct bpf_iter__ipv6_route, rt),
65008c2ecf20Sopenharmony_ci		  PTR_TO_BTF_ID_OR_NULL },
65018c2ecf20Sopenharmony_ci	},
65028c2ecf20Sopenharmony_ci	.seq_info		= &ipv6_route_seq_info,
65038c2ecf20Sopenharmony_ci};
65048c2ecf20Sopenharmony_ci
65058c2ecf20Sopenharmony_cistatic int __init bpf_iter_register(void)
65068c2ecf20Sopenharmony_ci{
65078c2ecf20Sopenharmony_ci	ipv6_route_reg_info.ctx_arg_info[0].btf_id = *btf_fib6_info_id;
65088c2ecf20Sopenharmony_ci	return bpf_iter_reg_target(&ipv6_route_reg_info);
65098c2ecf20Sopenharmony_ci}
65108c2ecf20Sopenharmony_ci
65118c2ecf20Sopenharmony_cistatic void bpf_iter_unregister(void)
65128c2ecf20Sopenharmony_ci{
65138c2ecf20Sopenharmony_ci	bpf_iter_unreg_target(&ipv6_route_reg_info);
65148c2ecf20Sopenharmony_ci}
65158c2ecf20Sopenharmony_ci#endif
65168c2ecf20Sopenharmony_ci#endif
65178c2ecf20Sopenharmony_ci
65188c2ecf20Sopenharmony_ciint __init ip6_route_init(void)
65198c2ecf20Sopenharmony_ci{
65208c2ecf20Sopenharmony_ci	int ret;
65218c2ecf20Sopenharmony_ci	int cpu;
65228c2ecf20Sopenharmony_ci
65238c2ecf20Sopenharmony_ci	ret = -ENOMEM;
65248c2ecf20Sopenharmony_ci	ip6_dst_ops_template.kmem_cachep =
65258c2ecf20Sopenharmony_ci		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
65268c2ecf20Sopenharmony_ci				  SLAB_HWCACHE_ALIGN, NULL);
65278c2ecf20Sopenharmony_ci	if (!ip6_dst_ops_template.kmem_cachep)
65288c2ecf20Sopenharmony_ci		goto out;
65298c2ecf20Sopenharmony_ci
65308c2ecf20Sopenharmony_ci	ret = dst_entries_init(&ip6_dst_blackhole_ops);
65318c2ecf20Sopenharmony_ci	if (ret)
65328c2ecf20Sopenharmony_ci		goto out_kmem_cache;
65338c2ecf20Sopenharmony_ci
65348c2ecf20Sopenharmony_ci	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
65358c2ecf20Sopenharmony_ci	if (ret)
65368c2ecf20Sopenharmony_ci		goto out_dst_entries;
65378c2ecf20Sopenharmony_ci
65388c2ecf20Sopenharmony_ci	ret = register_pernet_subsys(&ip6_route_net_ops);
65398c2ecf20Sopenharmony_ci	if (ret)
65408c2ecf20Sopenharmony_ci		goto out_register_inetpeer;
65418c2ecf20Sopenharmony_ci
65428c2ecf20Sopenharmony_ci	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
65438c2ecf20Sopenharmony_ci
65448c2ecf20Sopenharmony_ci	ret = fib6_init();
65458c2ecf20Sopenharmony_ci	if (ret)
65468c2ecf20Sopenharmony_ci		goto out_register_subsys;
65478c2ecf20Sopenharmony_ci
65488c2ecf20Sopenharmony_ci	ret = xfrm6_init();
65498c2ecf20Sopenharmony_ci	if (ret)
65508c2ecf20Sopenharmony_ci		goto out_fib6_init;
65518c2ecf20Sopenharmony_ci
65528c2ecf20Sopenharmony_ci	ret = fib6_rules_init();
65538c2ecf20Sopenharmony_ci	if (ret)
65548c2ecf20Sopenharmony_ci		goto xfrm6_init;
65558c2ecf20Sopenharmony_ci
65568c2ecf20Sopenharmony_ci	ret = register_pernet_subsys(&ip6_route_net_late_ops);
65578c2ecf20Sopenharmony_ci	if (ret)
65588c2ecf20Sopenharmony_ci		goto fib6_rules_init;
65598c2ecf20Sopenharmony_ci
65608c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
65618c2ecf20Sopenharmony_ci				   inet6_rtm_newroute, NULL, 0);
65628c2ecf20Sopenharmony_ci	if (ret < 0)
65638c2ecf20Sopenharmony_ci		goto out_register_late_subsys;
65648c2ecf20Sopenharmony_ci
65658c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
65668c2ecf20Sopenharmony_ci				   inet6_rtm_delroute, NULL, 0);
65678c2ecf20Sopenharmony_ci	if (ret < 0)
65688c2ecf20Sopenharmony_ci		goto out_register_late_subsys;
65698c2ecf20Sopenharmony_ci
65708c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
65718c2ecf20Sopenharmony_ci				   inet6_rtm_getroute, NULL,
65728c2ecf20Sopenharmony_ci				   RTNL_FLAG_DOIT_UNLOCKED);
65738c2ecf20Sopenharmony_ci	if (ret < 0)
65748c2ecf20Sopenharmony_ci		goto out_register_late_subsys;
65758c2ecf20Sopenharmony_ci
65768c2ecf20Sopenharmony_ci	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
65778c2ecf20Sopenharmony_ci	if (ret)
65788c2ecf20Sopenharmony_ci		goto out_register_late_subsys;
65798c2ecf20Sopenharmony_ci
65808c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6)
65818c2ecf20Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
65828c2ecf20Sopenharmony_ci	ret = bpf_iter_register();
65838c2ecf20Sopenharmony_ci	if (ret)
65848c2ecf20Sopenharmony_ci		goto out_register_late_subsys;
65858c2ecf20Sopenharmony_ci#endif
65868c2ecf20Sopenharmony_ci#endif
65878c2ecf20Sopenharmony_ci
65888c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
65898c2ecf20Sopenharmony_ci		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
65908c2ecf20Sopenharmony_ci
65918c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ul->head);
65928c2ecf20Sopenharmony_ci		spin_lock_init(&ul->lock);
65938c2ecf20Sopenharmony_ci	}
65948c2ecf20Sopenharmony_ci
65958c2ecf20Sopenharmony_ciout:
65968c2ecf20Sopenharmony_ci	return ret;
65978c2ecf20Sopenharmony_ci
65988c2ecf20Sopenharmony_ciout_register_late_subsys:
65998c2ecf20Sopenharmony_ci	rtnl_unregister_all(PF_INET6);
66008c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_late_ops);
66018c2ecf20Sopenharmony_cifib6_rules_init:
66028c2ecf20Sopenharmony_ci	fib6_rules_cleanup();
66038c2ecf20Sopenharmony_cixfrm6_init:
66048c2ecf20Sopenharmony_ci	xfrm6_fini();
66058c2ecf20Sopenharmony_ciout_fib6_init:
66068c2ecf20Sopenharmony_ci	fib6_gc_cleanup();
66078c2ecf20Sopenharmony_ciout_register_subsys:
66088c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_ops);
66098c2ecf20Sopenharmony_ciout_register_inetpeer:
66108c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ipv6_inetpeer_ops);
66118c2ecf20Sopenharmony_ciout_dst_entries:
66128c2ecf20Sopenharmony_ci	dst_entries_destroy(&ip6_dst_blackhole_ops);
66138c2ecf20Sopenharmony_ciout_kmem_cache:
66148c2ecf20Sopenharmony_ci	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
66158c2ecf20Sopenharmony_ci	goto out;
66168c2ecf20Sopenharmony_ci}
66178c2ecf20Sopenharmony_ci
66188c2ecf20Sopenharmony_civoid ip6_route_cleanup(void)
66198c2ecf20Sopenharmony_ci{
66208c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6)
66218c2ecf20Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
66228c2ecf20Sopenharmony_ci	bpf_iter_unregister();
66238c2ecf20Sopenharmony_ci#endif
66248c2ecf20Sopenharmony_ci#endif
66258c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&ip6_route_dev_notifier);
66268c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_late_ops);
66278c2ecf20Sopenharmony_ci	fib6_rules_cleanup();
66288c2ecf20Sopenharmony_ci	xfrm6_fini();
66298c2ecf20Sopenharmony_ci	fib6_gc_cleanup();
66308c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ipv6_inetpeer_ops);
66318c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_ops);
66328c2ecf20Sopenharmony_ci	dst_entries_destroy(&ip6_dst_blackhole_ops);
66338c2ecf20Sopenharmony_ci	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
66348c2ecf20Sopenharmony_ci}
6635