162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Linux INET6 implementation
462306a36Sopenharmony_ci *	FIB front-end.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	Authors:
762306a36Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*	Changes:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI
1362306a36Sopenharmony_ci *		reworked default router selection.
1462306a36Sopenharmony_ci *		- respect outgoing interface
1562306a36Sopenharmony_ci *		- select from (probably) reachable routers (i.e.
1662306a36Sopenharmony_ci *		routers in REACHABLE, STALE, DELAY or PROBE states).
1762306a36Sopenharmony_ci *		- always select the same router if it is (probably)
1862306a36Sopenharmony_ci *		reachable.  otherwise, round-robin the list.
1962306a36Sopenharmony_ci *	Ville Nuorvala
2062306a36Sopenharmony_ci *		Fixed routing subtrees.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/capability.h>
2662306a36Sopenharmony_ci#include <linux/errno.h>
2762306a36Sopenharmony_ci#include <linux/export.h>
2862306a36Sopenharmony_ci#include <linux/types.h>
2962306a36Sopenharmony_ci#include <linux/times.h>
3062306a36Sopenharmony_ci#include <linux/socket.h>
3162306a36Sopenharmony_ci#include <linux/sockios.h>
3262306a36Sopenharmony_ci#include <linux/net.h>
3362306a36Sopenharmony_ci#include <linux/route.h>
3462306a36Sopenharmony_ci#include <linux/netdevice.h>
3562306a36Sopenharmony_ci#include <linux/in6.h>
3662306a36Sopenharmony_ci#include <linux/mroute6.h>
3762306a36Sopenharmony_ci#include <linux/init.h>
3862306a36Sopenharmony_ci#include <linux/if_arp.h>
3962306a36Sopenharmony_ci#include <linux/proc_fs.h>
4062306a36Sopenharmony_ci#include <linux/seq_file.h>
4162306a36Sopenharmony_ci#include <linux/nsproxy.h>
4262306a36Sopenharmony_ci#include <linux/slab.h>
4362306a36Sopenharmony_ci#include <linux/jhash.h>
4462306a36Sopenharmony_ci#include <linux/siphash.h>
4562306a36Sopenharmony_ci#include <net/net_namespace.h>
4662306a36Sopenharmony_ci#include <net/snmp.h>
4762306a36Sopenharmony_ci#include <net/ipv6.h>
4862306a36Sopenharmony_ci#include <net/ip6_fib.h>
4962306a36Sopenharmony_ci#include <net/ip6_route.h>
5062306a36Sopenharmony_ci#include <net/ndisc.h>
5162306a36Sopenharmony_ci#include <net/addrconf.h>
5262306a36Sopenharmony_ci#include <net/tcp.h>
5362306a36Sopenharmony_ci#include <linux/rtnetlink.h>
5462306a36Sopenharmony_ci#include <net/dst.h>
5562306a36Sopenharmony_ci#include <net/dst_metadata.h>
5662306a36Sopenharmony_ci#include <net/xfrm.h>
5762306a36Sopenharmony_ci#include <net/netevent.h>
5862306a36Sopenharmony_ci#include <net/netlink.h>
5962306a36Sopenharmony_ci#include <net/rtnh.h>
6062306a36Sopenharmony_ci#include <net/lwtunnel.h>
6162306a36Sopenharmony_ci#include <net/ip_tunnels.h>
6262306a36Sopenharmony_ci#include <net/l3mdev.h>
6362306a36Sopenharmony_ci#include <net/ip.h>
6462306a36Sopenharmony_ci#include <linux/uaccess.h>
6562306a36Sopenharmony_ci#include <linux/btf_ids.h>
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
6862306a36Sopenharmony_ci#include <linux/sysctl.h>
6962306a36Sopenharmony_ci#endif
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
7462306a36Sopenharmony_ci#include <trace/events/fib6.h>
7562306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
7662306a36Sopenharmony_ci#undef CREATE_TRACE_POINTS
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cienum rt6_nud_state {
7962306a36Sopenharmony_ci	RT6_NUD_FAIL_HARD = -3,
8062306a36Sopenharmony_ci	RT6_NUD_FAIL_PROBE = -2,
8162306a36Sopenharmony_ci	RT6_NUD_FAIL_DO_RR = -1,
8262306a36Sopenharmony_ci	RT6_NUD_SUCCEED = 1
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE
8662306a36Sopenharmony_cistruct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
8762306a36Sopenharmony_cistatic unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
8862306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE
8962306a36Sopenharmony_ciunsigned int		ip6_mtu(const struct dst_entry *dst);
9062306a36Sopenharmony_cistatic struct dst_entry *ip6_negative_advice(struct dst_entry *);
9162306a36Sopenharmony_cistatic void		ip6_dst_destroy(struct dst_entry *);
9262306a36Sopenharmony_cistatic void		ip6_dst_ifdown(struct dst_entry *,
9362306a36Sopenharmony_ci				       struct net_device *dev);
9462306a36Sopenharmony_cistatic void		 ip6_dst_gc(struct dst_ops *ops);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int		ip6_pkt_discard(struct sk_buff *skb);
9762306a36Sopenharmony_cistatic int		ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
9862306a36Sopenharmony_cistatic int		ip6_pkt_prohibit(struct sk_buff *skb);
9962306a36Sopenharmony_cistatic int		ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
10062306a36Sopenharmony_cistatic void		ip6_link_failure(struct sk_buff *skb);
10162306a36Sopenharmony_cistatic void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
10262306a36Sopenharmony_ci					   struct sk_buff *skb, u32 mtu,
10362306a36Sopenharmony_ci					   bool confirm_neigh);
10462306a36Sopenharmony_cistatic void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
10562306a36Sopenharmony_ci					struct sk_buff *skb);
10662306a36Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
10762306a36Sopenharmony_ci			   int strict);
10862306a36Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i);
10962306a36Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb,
11062306a36Sopenharmony_ci			 struct fib6_info *rt, struct dst_entry *dst,
11162306a36Sopenharmony_ci			 struct in6_addr *dest, struct in6_addr *src,
11262306a36Sopenharmony_ci			 int iif, int type, u32 portid, u32 seq,
11362306a36Sopenharmony_ci			 unsigned int flags);
11462306a36Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
11562306a36Sopenharmony_ci					   const struct in6_addr *daddr,
11662306a36Sopenharmony_ci					   const struct in6_addr *saddr);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
11962306a36Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net,
12062306a36Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
12162306a36Sopenharmony_ci					   const struct in6_addr *gwaddr,
12262306a36Sopenharmony_ci					   struct net_device *dev,
12362306a36Sopenharmony_ci					   unsigned int pref);
12462306a36Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net,
12562306a36Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
12662306a36Sopenharmony_ci					   const struct in6_addr *gwaddr,
12762306a36Sopenharmony_ci					   struct net_device *dev);
12862306a36Sopenharmony_ci#endif
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistruct uncached_list {
13162306a36Sopenharmony_ci	spinlock_t		lock;
13262306a36Sopenharmony_ci	struct list_head	head;
13362306a36Sopenharmony_ci	struct list_head	quarantine;
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_civoid rt6_uncached_list_add(struct rt6_info *rt)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	rt->dst.rt_uncached_list = ul;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	spin_lock_bh(&ul->lock);
14562306a36Sopenharmony_ci	list_add_tail(&rt->dst.rt_uncached, &ul->head);
14662306a36Sopenharmony_ci	spin_unlock_bh(&ul->lock);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_civoid rt6_uncached_list_del(struct rt6_info *rt)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	if (!list_empty(&rt->dst.rt_uncached)) {
15262306a36Sopenharmony_ci		struct uncached_list *ul = rt->dst.rt_uncached_list;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		spin_lock_bh(&ul->lock);
15562306a36Sopenharmony_ci		list_del_init(&rt->dst.rt_uncached);
15662306a36Sopenharmony_ci		spin_unlock_bh(&ul->lock);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic void rt6_uncached_list_flush_dev(struct net_device *dev)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int cpu;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
16562306a36Sopenharmony_ci		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
16662306a36Sopenharmony_ci		struct rt6_info *rt, *safe;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		if (list_empty(&ul->head))
16962306a36Sopenharmony_ci			continue;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		spin_lock_bh(&ul->lock);
17262306a36Sopenharmony_ci		list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) {
17362306a36Sopenharmony_ci			struct inet6_dev *rt_idev = rt->rt6i_idev;
17462306a36Sopenharmony_ci			struct net_device *rt_dev = rt->dst.dev;
17562306a36Sopenharmony_ci			bool handled = false;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			if (rt_idev->dev == dev) {
17862306a36Sopenharmony_ci				rt->rt6i_idev = in6_dev_get(blackhole_netdev);
17962306a36Sopenharmony_ci				in6_dev_put(rt_idev);
18062306a36Sopenharmony_ci				handled = true;
18162306a36Sopenharmony_ci			}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci			if (rt_dev == dev) {
18462306a36Sopenharmony_ci				rt->dst.dev = blackhole_netdev;
18562306a36Sopenharmony_ci				netdev_ref_replace(rt_dev, blackhole_netdev,
18662306a36Sopenharmony_ci						   &rt->dst.dev_tracker,
18762306a36Sopenharmony_ci						   GFP_ATOMIC);
18862306a36Sopenharmony_ci				handled = true;
18962306a36Sopenharmony_ci			}
19062306a36Sopenharmony_ci			if (handled)
19162306a36Sopenharmony_ci				list_move(&rt->dst.rt_uncached,
19262306a36Sopenharmony_ci					  &ul->quarantine);
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci		spin_unlock_bh(&ul->lock);
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic inline const void *choose_neigh_daddr(const struct in6_addr *p,
19962306a36Sopenharmony_ci					     struct sk_buff *skb,
20062306a36Sopenharmony_ci					     const void *daddr)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	if (!ipv6_addr_any(p))
20362306a36Sopenharmony_ci		return (const void *) p;
20462306a36Sopenharmony_ci	else if (skb)
20562306a36Sopenharmony_ci		return &ipv6_hdr(skb)->daddr;
20662306a36Sopenharmony_ci	return daddr;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistruct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
21062306a36Sopenharmony_ci				   struct net_device *dev,
21162306a36Sopenharmony_ci				   struct sk_buff *skb,
21262306a36Sopenharmony_ci				   const void *daddr)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct neighbour *n;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	daddr = choose_neigh_daddr(gw, skb, daddr);
21762306a36Sopenharmony_ci	n = __ipv6_neigh_lookup(dev, daddr);
21862306a36Sopenharmony_ci	if (n)
21962306a36Sopenharmony_ci		return n;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	n = neigh_create(&nd_tbl, daddr, dev);
22262306a36Sopenharmony_ci	return IS_ERR(n) ? NULL : n;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
22662306a36Sopenharmony_ci					      struct sk_buff *skb,
22762306a36Sopenharmony_ci					      const void *daddr)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any),
23262306a36Sopenharmony_ci				dst->dev, skb, daddr);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct net_device *dev = dst->dev;
23862306a36Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)dst;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr);
24162306a36Sopenharmony_ci	if (!daddr)
24262306a36Sopenharmony_ci		return;
24362306a36Sopenharmony_ci	if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
24462306a36Sopenharmony_ci		return;
24562306a36Sopenharmony_ci	if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
24662306a36Sopenharmony_ci		return;
24762306a36Sopenharmony_ci	__ipv6_confirm_neigh(dev, daddr);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic struct dst_ops ip6_dst_ops_template = {
25162306a36Sopenharmony_ci	.family			=	AF_INET6,
25262306a36Sopenharmony_ci	.gc			=	ip6_dst_gc,
25362306a36Sopenharmony_ci	.gc_thresh		=	1024,
25462306a36Sopenharmony_ci	.check			=	ip6_dst_check,
25562306a36Sopenharmony_ci	.default_advmss		=	ip6_default_advmss,
25662306a36Sopenharmony_ci	.mtu			=	ip6_mtu,
25762306a36Sopenharmony_ci	.cow_metrics		=	dst_cow_metrics_generic,
25862306a36Sopenharmony_ci	.destroy		=	ip6_dst_destroy,
25962306a36Sopenharmony_ci	.ifdown			=	ip6_dst_ifdown,
26062306a36Sopenharmony_ci	.negative_advice	=	ip6_negative_advice,
26162306a36Sopenharmony_ci	.link_failure		=	ip6_link_failure,
26262306a36Sopenharmony_ci	.update_pmtu		=	ip6_rt_update_pmtu,
26362306a36Sopenharmony_ci	.redirect		=	rt6_do_redirect,
26462306a36Sopenharmony_ci	.local_out		=	__ip6_local_out,
26562306a36Sopenharmony_ci	.neigh_lookup		=	ip6_dst_neigh_lookup,
26662306a36Sopenharmony_ci	.confirm_neigh		=	ip6_confirm_neigh,
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic struct dst_ops ip6_dst_blackhole_ops = {
27062306a36Sopenharmony_ci	.family			= AF_INET6,
27162306a36Sopenharmony_ci	.default_advmss		= ip6_default_advmss,
27262306a36Sopenharmony_ci	.neigh_lookup		= ip6_dst_neigh_lookup,
27362306a36Sopenharmony_ci	.check			= ip6_dst_check,
27462306a36Sopenharmony_ci	.destroy		= ip6_dst_destroy,
27562306a36Sopenharmony_ci	.cow_metrics		= dst_cow_metrics_generic,
27662306a36Sopenharmony_ci	.update_pmtu		= dst_blackhole_update_pmtu,
27762306a36Sopenharmony_ci	.redirect		= dst_blackhole_redirect,
27862306a36Sopenharmony_ci	.mtu			= dst_blackhole_mtu,
27962306a36Sopenharmony_ci};
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic const u32 ip6_template_metrics[RTAX_MAX] = {
28262306a36Sopenharmony_ci	[RTAX_HOPLIMIT - 1] = 0,
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic const struct fib6_info fib6_null_entry_template = {
28662306a36Sopenharmony_ci	.fib6_flags	= (RTF_REJECT | RTF_NONEXTHOP),
28762306a36Sopenharmony_ci	.fib6_protocol  = RTPROT_KERNEL,
28862306a36Sopenharmony_ci	.fib6_metric	= ~(u32)0,
28962306a36Sopenharmony_ci	.fib6_ref	= REFCOUNT_INIT(1),
29062306a36Sopenharmony_ci	.fib6_type	= RTN_UNREACHABLE,
29162306a36Sopenharmony_ci	.fib6_metrics	= (struct dst_metrics *)&dst_default_metrics,
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic const struct rt6_info ip6_null_entry_template = {
29562306a36Sopenharmony_ci	.dst = {
29662306a36Sopenharmony_ci		.__rcuref	= RCUREF_INIT(1),
29762306a36Sopenharmony_ci		.__use		= 1,
29862306a36Sopenharmony_ci		.obsolete	= DST_OBSOLETE_FORCE_CHK,
29962306a36Sopenharmony_ci		.error		= -ENETUNREACH,
30062306a36Sopenharmony_ci		.input		= ip6_pkt_discard,
30162306a36Sopenharmony_ci		.output		= ip6_pkt_discard_out,
30262306a36Sopenharmony_ci	},
30362306a36Sopenharmony_ci	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
30462306a36Sopenharmony_ci};
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic const struct rt6_info ip6_prohibit_entry_template = {
30962306a36Sopenharmony_ci	.dst = {
31062306a36Sopenharmony_ci		.__rcuref	= RCUREF_INIT(1),
31162306a36Sopenharmony_ci		.__use		= 1,
31262306a36Sopenharmony_ci		.obsolete	= DST_OBSOLETE_FORCE_CHK,
31362306a36Sopenharmony_ci		.error		= -EACCES,
31462306a36Sopenharmony_ci		.input		= ip6_pkt_prohibit,
31562306a36Sopenharmony_ci		.output		= ip6_pkt_prohibit_out,
31662306a36Sopenharmony_ci	},
31762306a36Sopenharmony_ci	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic const struct rt6_info ip6_blk_hole_entry_template = {
32162306a36Sopenharmony_ci	.dst = {
32262306a36Sopenharmony_ci		.__rcuref	= RCUREF_INIT(1),
32362306a36Sopenharmony_ci		.__use		= 1,
32462306a36Sopenharmony_ci		.obsolete	= DST_OBSOLETE_FORCE_CHK,
32562306a36Sopenharmony_ci		.error		= -EINVAL,
32662306a36Sopenharmony_ci		.input		= dst_discard,
32762306a36Sopenharmony_ci		.output		= dst_discard_out,
32862306a36Sopenharmony_ci	},
32962306a36Sopenharmony_ci	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
33062306a36Sopenharmony_ci};
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci#endif
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void rt6_info_init(struct rt6_info *rt)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	memset_after(rt, 0, dst);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/* allocate dst with ip6_dst_ops */
34062306a36Sopenharmony_cistruct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
34162306a36Sopenharmony_ci			       int flags)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
34462306a36Sopenharmony_ci					1, DST_OBSOLETE_FORCE_CHK, flags);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (rt) {
34762306a36Sopenharmony_ci		rt6_info_init(rt);
34862306a36Sopenharmony_ci		atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return rt;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ciEXPORT_SYMBOL(ip6_dst_alloc);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void ip6_dst_destroy(struct dst_entry *dst)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)dst;
35862306a36Sopenharmony_ci	struct fib6_info *from;
35962306a36Sopenharmony_ci	struct inet6_dev *idev;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	ip_dst_metrics_put(dst);
36262306a36Sopenharmony_ci	rt6_uncached_list_del(rt);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	idev = rt->rt6i_idev;
36562306a36Sopenharmony_ci	if (idev) {
36662306a36Sopenharmony_ci		rt->rt6i_idev = NULL;
36762306a36Sopenharmony_ci		in6_dev_put(idev);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	from = xchg((__force struct fib6_info **)&rt->from, NULL);
37162306a36Sopenharmony_ci	fib6_info_release(from);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *)dst;
37762306a36Sopenharmony_ci	struct inet6_dev *idev = rt->rt6i_idev;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (idev && idev->dev != blackhole_netdev) {
38062306a36Sopenharmony_ci		struct inet6_dev *blackhole_idev = in6_dev_get(blackhole_netdev);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		if (blackhole_idev) {
38362306a36Sopenharmony_ci			rt->rt6i_idev = blackhole_idev;
38462306a36Sopenharmony_ci			in6_dev_put(idev);
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic bool __rt6_check_expired(const struct rt6_info *rt)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	if (rt->rt6i_flags & RTF_EXPIRES)
39262306a36Sopenharmony_ci		return time_after(jiffies, rt->dst.expires);
39362306a36Sopenharmony_ci	else
39462306a36Sopenharmony_ci		return false;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic bool rt6_check_expired(const struct rt6_info *rt)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct fib6_info *from;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	from = rcu_dereference(rt->from);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (rt->rt6i_flags & RTF_EXPIRES) {
40462306a36Sopenharmony_ci		if (time_after(jiffies, rt->dst.expires))
40562306a36Sopenharmony_ci			return true;
40662306a36Sopenharmony_ci	} else if (from) {
40762306a36Sopenharmony_ci		return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
40862306a36Sopenharmony_ci			fib6_check_expired(from);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	return false;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_civoid fib6_select_path(const struct net *net, struct fib6_result *res,
41462306a36Sopenharmony_ci		      struct flowi6 *fl6, int oif, bool have_oif_match,
41562306a36Sopenharmony_ci		      const struct sk_buff *skb, int strict)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct fib6_info *sibling, *next_sibling;
41862306a36Sopenharmony_ci	struct fib6_info *match = res->f6i;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (!match->nh && (!match->fib6_nsiblings || have_oif_match))
42162306a36Sopenharmony_ci		goto out;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (match->nh && have_oif_match && res->nh)
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (skb)
42762306a36Sopenharmony_ci		IP6CB(skb)->flags |= IP6SKB_MULTIPATH;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* We might have already computed the hash for ICMPv6 errors. In such
43062306a36Sopenharmony_ci	 * case it will always be non-zero. Otherwise now is the time to do it.
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	if (!fl6->mp_hash &&
43362306a36Sopenharmony_ci	    (!match->nh || nexthop_is_multipath(match->nh)))
43462306a36Sopenharmony_ci		fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (unlikely(match->nh)) {
43762306a36Sopenharmony_ci		nexthop_path_fib6_result(res, fl6->mp_hash);
43862306a36Sopenharmony_ci		return;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound))
44262306a36Sopenharmony_ci		goto out;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
44562306a36Sopenharmony_ci				 fib6_siblings) {
44662306a36Sopenharmony_ci		const struct fib6_nh *nh = sibling->fib6_nh;
44762306a36Sopenharmony_ci		int nh_upper_bound;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
45062306a36Sopenharmony_ci		if (fl6->mp_hash > nh_upper_bound)
45162306a36Sopenharmony_ci			continue;
45262306a36Sopenharmony_ci		if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		match = sibling;
45562306a36Sopenharmony_ci		break;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ciout:
45962306a36Sopenharmony_ci	res->f6i = match;
46062306a36Sopenharmony_ci	res->nh = match->fib6_nh;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci/*
46462306a36Sopenharmony_ci *	Route lookup. rcu_read_lock() should be held.
46562306a36Sopenharmony_ci */
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
46862306a36Sopenharmony_ci			       const struct in6_addr *saddr, int oif, int flags)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	const struct net_device *dev;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD)
47362306a36Sopenharmony_ci		return false;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	dev = nh->fib_nh_dev;
47662306a36Sopenharmony_ci	if (oif) {
47762306a36Sopenharmony_ci		if (dev->ifindex == oif)
47862306a36Sopenharmony_ci			return true;
47962306a36Sopenharmony_ci	} else {
48062306a36Sopenharmony_ci		if (ipv6_chk_addr(net, saddr, dev,
48162306a36Sopenharmony_ci				  flags & RT6_LOOKUP_F_IFACE))
48262306a36Sopenharmony_ci			return true;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return false;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistruct fib6_nh_dm_arg {
48962306a36Sopenharmony_ci	struct net		*net;
49062306a36Sopenharmony_ci	const struct in6_addr	*saddr;
49162306a36Sopenharmony_ci	int			oif;
49262306a36Sopenharmony_ci	int			flags;
49362306a36Sopenharmony_ci	struct fib6_nh		*nh;
49462306a36Sopenharmony_ci};
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic int __rt6_nh_dev_match(struct fib6_nh *nh, void *_arg)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct fib6_nh_dm_arg *arg = _arg;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	arg->nh = nh;
50162306a36Sopenharmony_ci	return __rt6_device_match(arg->net, nh, arg->saddr, arg->oif,
50262306a36Sopenharmony_ci				  arg->flags);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/* returns fib6_nh from nexthop or NULL */
50662306a36Sopenharmony_cistatic struct fib6_nh *rt6_nh_dev_match(struct net *net, struct nexthop *nh,
50762306a36Sopenharmony_ci					struct fib6_result *res,
50862306a36Sopenharmony_ci					const struct in6_addr *saddr,
50962306a36Sopenharmony_ci					int oif, int flags)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct fib6_nh_dm_arg arg = {
51262306a36Sopenharmony_ci		.net   = net,
51362306a36Sopenharmony_ci		.saddr = saddr,
51462306a36Sopenharmony_ci		.oif   = oif,
51562306a36Sopenharmony_ci		.flags = flags,
51662306a36Sopenharmony_ci	};
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (nexthop_is_blackhole(nh))
51962306a36Sopenharmony_ci		return NULL;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (nexthop_for_each_fib6_nh(nh, __rt6_nh_dev_match, &arg))
52262306a36Sopenharmony_ci		return arg.nh;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return NULL;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void rt6_device_match(struct net *net, struct fib6_result *res,
52862306a36Sopenharmony_ci			     const struct in6_addr *saddr, int oif, int flags)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
53162306a36Sopenharmony_ci	struct fib6_info *spf6i;
53262306a36Sopenharmony_ci	struct fib6_nh *nh;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (!oif && ipv6_addr_any(saddr)) {
53562306a36Sopenharmony_ci		if (unlikely(f6i->nh)) {
53662306a36Sopenharmony_ci			nh = nexthop_fib6_nh(f6i->nh);
53762306a36Sopenharmony_ci			if (nexthop_is_blackhole(f6i->nh))
53862306a36Sopenharmony_ci				goto out_blackhole;
53962306a36Sopenharmony_ci		} else {
54062306a36Sopenharmony_ci			nh = f6i->fib6_nh;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci		if (!(nh->fib_nh_flags & RTNH_F_DEAD))
54362306a36Sopenharmony_ci			goto out;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
54762306a36Sopenharmony_ci		bool matched = false;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		if (unlikely(spf6i->nh)) {
55062306a36Sopenharmony_ci			nh = rt6_nh_dev_match(net, spf6i->nh, res, saddr,
55162306a36Sopenharmony_ci					      oif, flags);
55262306a36Sopenharmony_ci			if (nh)
55362306a36Sopenharmony_ci				matched = true;
55462306a36Sopenharmony_ci		} else {
55562306a36Sopenharmony_ci			nh = spf6i->fib6_nh;
55662306a36Sopenharmony_ci			if (__rt6_device_match(net, nh, saddr, oif, flags))
55762306a36Sopenharmony_ci				matched = true;
55862306a36Sopenharmony_ci		}
55962306a36Sopenharmony_ci		if (matched) {
56062306a36Sopenharmony_ci			res->f6i = spf6i;
56162306a36Sopenharmony_ci			goto out;
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (oif && flags & RT6_LOOKUP_F_IFACE) {
56662306a36Sopenharmony_ci		res->f6i = net->ipv6.fib6_null_entry;
56762306a36Sopenharmony_ci		nh = res->f6i->fib6_nh;
56862306a36Sopenharmony_ci		goto out;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (unlikely(f6i->nh)) {
57262306a36Sopenharmony_ci		nh = nexthop_fib6_nh(f6i->nh);
57362306a36Sopenharmony_ci		if (nexthop_is_blackhole(f6i->nh))
57462306a36Sopenharmony_ci			goto out_blackhole;
57562306a36Sopenharmony_ci	} else {
57662306a36Sopenharmony_ci		nh = f6i->fib6_nh;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD) {
58062306a36Sopenharmony_ci		res->f6i = net->ipv6.fib6_null_entry;
58162306a36Sopenharmony_ci		nh = res->f6i->fib6_nh;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ciout:
58462306a36Sopenharmony_ci	res->nh = nh;
58562306a36Sopenharmony_ci	res->fib6_type = res->f6i->fib6_type;
58662306a36Sopenharmony_ci	res->fib6_flags = res->f6i->fib6_flags;
58762306a36Sopenharmony_ci	return;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ciout_blackhole:
59062306a36Sopenharmony_ci	res->fib6_flags |= RTF_REJECT;
59162306a36Sopenharmony_ci	res->fib6_type = RTN_BLACKHOLE;
59262306a36Sopenharmony_ci	res->nh = nh;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
59662306a36Sopenharmony_cistruct __rt6_probe_work {
59762306a36Sopenharmony_ci	struct work_struct work;
59862306a36Sopenharmony_ci	struct in6_addr target;
59962306a36Sopenharmony_ci	struct net_device *dev;
60062306a36Sopenharmony_ci	netdevice_tracker dev_tracker;
60162306a36Sopenharmony_ci};
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic void rt6_probe_deferred(struct work_struct *w)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct in6_addr mcaddr;
60662306a36Sopenharmony_ci	struct __rt6_probe_work *work =
60762306a36Sopenharmony_ci		container_of(w, struct __rt6_probe_work, work);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	addrconf_addr_solict_mult(&work->target, &mcaddr);
61062306a36Sopenharmony_ci	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
61162306a36Sopenharmony_ci	netdev_put(work->dev, &work->dev_tracker);
61262306a36Sopenharmony_ci	kfree(work);
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic void rt6_probe(struct fib6_nh *fib6_nh)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct __rt6_probe_work *work = NULL;
61862306a36Sopenharmony_ci	const struct in6_addr *nh_gw;
61962306a36Sopenharmony_ci	unsigned long last_probe;
62062306a36Sopenharmony_ci	struct neighbour *neigh;
62162306a36Sopenharmony_ci	struct net_device *dev;
62262306a36Sopenharmony_ci	struct inet6_dev *idev;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/*
62562306a36Sopenharmony_ci	 * Okay, this does not seem to be appropriate
62662306a36Sopenharmony_ci	 * for now, however, we need to check if it
62762306a36Sopenharmony_ci	 * is really so; aka Router Reachability Probing.
62862306a36Sopenharmony_ci	 *
62962306a36Sopenharmony_ci	 * Router Reachability Probe MUST be rate-limited
63062306a36Sopenharmony_ci	 * to no more than one per minute.
63162306a36Sopenharmony_ci	 */
63262306a36Sopenharmony_ci	if (!fib6_nh->fib_nh_gw_family)
63362306a36Sopenharmony_ci		return;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	nh_gw = &fib6_nh->fib_nh_gw6;
63662306a36Sopenharmony_ci	dev = fib6_nh->fib_nh_dev;
63762306a36Sopenharmony_ci	rcu_read_lock();
63862306a36Sopenharmony_ci	last_probe = READ_ONCE(fib6_nh->last_probe);
63962306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
64062306a36Sopenharmony_ci	neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
64162306a36Sopenharmony_ci	if (neigh) {
64262306a36Sopenharmony_ci		if (READ_ONCE(neigh->nud_state) & NUD_VALID)
64362306a36Sopenharmony_ci			goto out;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		write_lock_bh(&neigh->lock);
64662306a36Sopenharmony_ci		if (!(neigh->nud_state & NUD_VALID) &&
64762306a36Sopenharmony_ci		    time_after(jiffies,
64862306a36Sopenharmony_ci			       neigh->updated + idev->cnf.rtr_probe_interval)) {
64962306a36Sopenharmony_ci			work = kmalloc(sizeof(*work), GFP_ATOMIC);
65062306a36Sopenharmony_ci			if (work)
65162306a36Sopenharmony_ci				__neigh_set_probe_once(neigh);
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci		write_unlock_bh(&neigh->lock);
65462306a36Sopenharmony_ci	} else if (time_after(jiffies, last_probe +
65562306a36Sopenharmony_ci				       idev->cnf.rtr_probe_interval)) {
65662306a36Sopenharmony_ci		work = kmalloc(sizeof(*work), GFP_ATOMIC);
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (!work || cmpxchg(&fib6_nh->last_probe,
66062306a36Sopenharmony_ci			     last_probe, jiffies) != last_probe) {
66162306a36Sopenharmony_ci		kfree(work);
66262306a36Sopenharmony_ci	} else {
66362306a36Sopenharmony_ci		INIT_WORK(&work->work, rt6_probe_deferred);
66462306a36Sopenharmony_ci		work->target = *nh_gw;
66562306a36Sopenharmony_ci		netdev_hold(dev, &work->dev_tracker, GFP_ATOMIC);
66662306a36Sopenharmony_ci		work->dev = dev;
66762306a36Sopenharmony_ci		schedule_work(&work->work);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ciout:
67162306a36Sopenharmony_ci	rcu_read_unlock();
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci#else
67462306a36Sopenharmony_cistatic inline void rt6_probe(struct fib6_nh *fib6_nh)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci#endif
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/*
68062306a36Sopenharmony_ci * Default Router Selection (RFC 2461 6.3.6)
68162306a36Sopenharmony_ci */
68262306a36Sopenharmony_cistatic enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
68562306a36Sopenharmony_ci	struct neighbour *neigh;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	rcu_read_lock();
68862306a36Sopenharmony_ci	neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
68962306a36Sopenharmony_ci					  &fib6_nh->fib_nh_gw6);
69062306a36Sopenharmony_ci	if (neigh) {
69162306a36Sopenharmony_ci		u8 nud_state = READ_ONCE(neigh->nud_state);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		if (nud_state & NUD_VALID)
69462306a36Sopenharmony_ci			ret = RT6_NUD_SUCCEED;
69562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
69662306a36Sopenharmony_ci		else if (!(nud_state & NUD_FAILED))
69762306a36Sopenharmony_ci			ret = RT6_NUD_SUCCEED;
69862306a36Sopenharmony_ci		else
69962306a36Sopenharmony_ci			ret = RT6_NUD_FAIL_PROBE;
70062306a36Sopenharmony_ci#endif
70162306a36Sopenharmony_ci	} else {
70262306a36Sopenharmony_ci		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
70362306a36Sopenharmony_ci		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci	rcu_read_unlock();
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return ret;
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
71162306a36Sopenharmony_ci			   int strict)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	int m = 0;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (!oif || nh->fib_nh_dev->ifindex == oif)
71662306a36Sopenharmony_ci		m = 2;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (!m && (strict & RT6_LOOKUP_F_IFACE))
71962306a36Sopenharmony_ci		return RT6_NUD_FAIL_HARD;
72062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
72162306a36Sopenharmony_ci	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
72262306a36Sopenharmony_ci#endif
72362306a36Sopenharmony_ci	if ((strict & RT6_LOOKUP_F_REACHABLE) &&
72462306a36Sopenharmony_ci	    !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
72562306a36Sopenharmony_ci		int n = rt6_check_neigh(nh);
72662306a36Sopenharmony_ci		if (n < 0)
72762306a36Sopenharmony_ci			return n;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	return m;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic bool find_match(struct fib6_nh *nh, u32 fib6_flags,
73362306a36Sopenharmony_ci		       int oif, int strict, int *mpri, bool *do_rr)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	bool match_do_rr = false;
73662306a36Sopenharmony_ci	bool rc = false;
73762306a36Sopenharmony_ci	int m;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD)
74062306a36Sopenharmony_ci		goto out;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
74362306a36Sopenharmony_ci	    nh->fib_nh_flags & RTNH_F_LINKDOWN &&
74462306a36Sopenharmony_ci	    !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
74562306a36Sopenharmony_ci		goto out;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	m = rt6_score_route(nh, fib6_flags, oif, strict);
74862306a36Sopenharmony_ci	if (m == RT6_NUD_FAIL_DO_RR) {
74962306a36Sopenharmony_ci		match_do_rr = true;
75062306a36Sopenharmony_ci		m = 0; /* lowest valid score */
75162306a36Sopenharmony_ci	} else if (m == RT6_NUD_FAIL_HARD) {
75262306a36Sopenharmony_ci		goto out;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (strict & RT6_LOOKUP_F_REACHABLE)
75662306a36Sopenharmony_ci		rt6_probe(nh);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
75962306a36Sopenharmony_ci	if (m > *mpri) {
76062306a36Sopenharmony_ci		*do_rr = match_do_rr;
76162306a36Sopenharmony_ci		*mpri = m;
76262306a36Sopenharmony_ci		rc = true;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ciout:
76562306a36Sopenharmony_ci	return rc;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistruct fib6_nh_frl_arg {
76962306a36Sopenharmony_ci	u32		flags;
77062306a36Sopenharmony_ci	int		oif;
77162306a36Sopenharmony_ci	int		strict;
77262306a36Sopenharmony_ci	int		*mpri;
77362306a36Sopenharmony_ci	bool		*do_rr;
77462306a36Sopenharmony_ci	struct fib6_nh	*nh;
77562306a36Sopenharmony_ci};
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic int rt6_nh_find_match(struct fib6_nh *nh, void *_arg)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	struct fib6_nh_frl_arg *arg = _arg;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	arg->nh = nh;
78262306a36Sopenharmony_ci	return find_match(nh, arg->flags, arg->oif, arg->strict,
78362306a36Sopenharmony_ci			  arg->mpri, arg->do_rr);
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic void __find_rr_leaf(struct fib6_info *f6i_start,
78762306a36Sopenharmony_ci			   struct fib6_info *nomatch, u32 metric,
78862306a36Sopenharmony_ci			   struct fib6_result *res, struct fib6_info **cont,
78962306a36Sopenharmony_ci			   int oif, int strict, bool *do_rr, int *mpri)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct fib6_info *f6i;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	for (f6i = f6i_start;
79462306a36Sopenharmony_ci	     f6i && f6i != nomatch;
79562306a36Sopenharmony_ci	     f6i = rcu_dereference(f6i->fib6_next)) {
79662306a36Sopenharmony_ci		bool matched = false;
79762306a36Sopenharmony_ci		struct fib6_nh *nh;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		if (cont && f6i->fib6_metric != metric) {
80062306a36Sopenharmony_ci			*cont = f6i;
80162306a36Sopenharmony_ci			return;
80262306a36Sopenharmony_ci		}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		if (fib6_check_expired(f6i))
80562306a36Sopenharmony_ci			continue;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		if (unlikely(f6i->nh)) {
80862306a36Sopenharmony_ci			struct fib6_nh_frl_arg arg = {
80962306a36Sopenharmony_ci				.flags  = f6i->fib6_flags,
81062306a36Sopenharmony_ci				.oif    = oif,
81162306a36Sopenharmony_ci				.strict = strict,
81262306a36Sopenharmony_ci				.mpri   = mpri,
81362306a36Sopenharmony_ci				.do_rr  = do_rr
81462306a36Sopenharmony_ci			};
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci			if (nexthop_is_blackhole(f6i->nh)) {
81762306a36Sopenharmony_ci				res->fib6_flags = RTF_REJECT;
81862306a36Sopenharmony_ci				res->fib6_type = RTN_BLACKHOLE;
81962306a36Sopenharmony_ci				res->f6i = f6i;
82062306a36Sopenharmony_ci				res->nh = nexthop_fib6_nh(f6i->nh);
82162306a36Sopenharmony_ci				return;
82262306a36Sopenharmony_ci			}
82362306a36Sopenharmony_ci			if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match,
82462306a36Sopenharmony_ci						     &arg)) {
82562306a36Sopenharmony_ci				matched = true;
82662306a36Sopenharmony_ci				nh = arg.nh;
82762306a36Sopenharmony_ci			}
82862306a36Sopenharmony_ci		} else {
82962306a36Sopenharmony_ci			nh = f6i->fib6_nh;
83062306a36Sopenharmony_ci			if (find_match(nh, f6i->fib6_flags, oif, strict,
83162306a36Sopenharmony_ci				       mpri, do_rr))
83262306a36Sopenharmony_ci				matched = true;
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci		if (matched) {
83562306a36Sopenharmony_ci			res->f6i = f6i;
83662306a36Sopenharmony_ci			res->nh = nh;
83762306a36Sopenharmony_ci			res->fib6_flags = f6i->fib6_flags;
83862306a36Sopenharmony_ci			res->fib6_type = f6i->fib6_type;
83962306a36Sopenharmony_ci		}
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
84462306a36Sopenharmony_ci			 struct fib6_info *rr_head, int oif, int strict,
84562306a36Sopenharmony_ci			 bool *do_rr, struct fib6_result *res)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	u32 metric = rr_head->fib6_metric;
84862306a36Sopenharmony_ci	struct fib6_info *cont = NULL;
84962306a36Sopenharmony_ci	int mpri = -1;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	__find_rr_leaf(rr_head, NULL, metric, res, &cont,
85262306a36Sopenharmony_ci		       oif, strict, do_rr, &mpri);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	__find_rr_leaf(leaf, rr_head, metric, res, &cont,
85562306a36Sopenharmony_ci		       oif, strict, do_rr, &mpri);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (res->f6i || !cont)
85862306a36Sopenharmony_ci		return;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	__find_rr_leaf(cont, NULL, metric, res, NULL,
86162306a36Sopenharmony_ci		       oif, strict, do_rr, &mpri);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic void rt6_select(struct net *net, struct fib6_node *fn, int oif,
86562306a36Sopenharmony_ci		       struct fib6_result *res, int strict)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct fib6_info *leaf = rcu_dereference(fn->leaf);
86862306a36Sopenharmony_ci	struct fib6_info *rt0;
86962306a36Sopenharmony_ci	bool do_rr = false;
87062306a36Sopenharmony_ci	int key_plen;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/* make sure this function or its helpers sets f6i */
87362306a36Sopenharmony_ci	res->f6i = NULL;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!leaf || leaf == net->ipv6.fib6_null_entry)
87662306a36Sopenharmony_ci		goto out;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	rt0 = rcu_dereference(fn->rr_ptr);
87962306a36Sopenharmony_ci	if (!rt0)
88062306a36Sopenharmony_ci		rt0 = leaf;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* Double check to make sure fn is not an intermediate node
88362306a36Sopenharmony_ci	 * and fn->leaf does not points to its child's leaf
88462306a36Sopenharmony_ci	 * (This might happen if all routes under fn are deleted from
88562306a36Sopenharmony_ci	 * the tree and fib6_repair_tree() is called on the node.)
88662306a36Sopenharmony_ci	 */
88762306a36Sopenharmony_ci	key_plen = rt0->fib6_dst.plen;
88862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
88962306a36Sopenharmony_ci	if (rt0->fib6_src.plen)
89062306a36Sopenharmony_ci		key_plen = rt0->fib6_src.plen;
89162306a36Sopenharmony_ci#endif
89262306a36Sopenharmony_ci	if (fn->fn_bit != key_plen)
89362306a36Sopenharmony_ci		goto out;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
89662306a36Sopenharmony_ci	if (do_rr) {
89762306a36Sopenharmony_ci		struct fib6_info *next = rcu_dereference(rt0->fib6_next);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci		/* no entries matched; do round-robin */
90062306a36Sopenharmony_ci		if (!next || next->fib6_metric != rt0->fib6_metric)
90162306a36Sopenharmony_ci			next = leaf;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci		if (next != rt0) {
90462306a36Sopenharmony_ci			spin_lock_bh(&leaf->fib6_table->tb6_lock);
90562306a36Sopenharmony_ci			/* make sure next is not being deleted from the tree */
90662306a36Sopenharmony_ci			if (next->fib6_node)
90762306a36Sopenharmony_ci				rcu_assign_pointer(fn->rr_ptr, next);
90862306a36Sopenharmony_ci			spin_unlock_bh(&leaf->fib6_table->tb6_lock);
90962306a36Sopenharmony_ci		}
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ciout:
91362306a36Sopenharmony_ci	if (!res->f6i) {
91462306a36Sopenharmony_ci		res->f6i = net->ipv6.fib6_null_entry;
91562306a36Sopenharmony_ci		res->nh = res->f6i->fib6_nh;
91662306a36Sopenharmony_ci		res->fib6_flags = res->f6i->fib6_flags;
91762306a36Sopenharmony_ci		res->fib6_type = res->f6i->fib6_type;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
92462306a36Sopenharmony_ci	       res->nh->fib_nh_gw_family;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
92862306a36Sopenharmony_ciint rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
92962306a36Sopenharmony_ci		  const struct in6_addr *gwaddr)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	struct net *net = dev_net(dev);
93262306a36Sopenharmony_ci	struct route_info *rinfo = (struct route_info *) opt;
93362306a36Sopenharmony_ci	struct in6_addr prefix_buf, *prefix;
93462306a36Sopenharmony_ci	unsigned int pref;
93562306a36Sopenharmony_ci	unsigned long lifetime;
93662306a36Sopenharmony_ci	struct fib6_info *rt;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (len < sizeof(struct route_info)) {
93962306a36Sopenharmony_ci		return -EINVAL;
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* Sanity check for prefix_len and length */
94362306a36Sopenharmony_ci	if (rinfo->length > 3) {
94462306a36Sopenharmony_ci		return -EINVAL;
94562306a36Sopenharmony_ci	} else if (rinfo->prefix_len > 128) {
94662306a36Sopenharmony_ci		return -EINVAL;
94762306a36Sopenharmony_ci	} else if (rinfo->prefix_len > 64) {
94862306a36Sopenharmony_ci		if (rinfo->length < 2) {
94962306a36Sopenharmony_ci			return -EINVAL;
95062306a36Sopenharmony_ci		}
95162306a36Sopenharmony_ci	} else if (rinfo->prefix_len > 0) {
95262306a36Sopenharmony_ci		if (rinfo->length < 1) {
95362306a36Sopenharmony_ci			return -EINVAL;
95462306a36Sopenharmony_ci		}
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	pref = rinfo->route_pref;
95862306a36Sopenharmony_ci	if (pref == ICMPV6_ROUTER_PREF_INVALID)
95962306a36Sopenharmony_ci		return -EINVAL;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (rinfo->length == 3)
96462306a36Sopenharmony_ci		prefix = (struct in6_addr *)rinfo->prefix;
96562306a36Sopenharmony_ci	else {
96662306a36Sopenharmony_ci		/* this function is safe */
96762306a36Sopenharmony_ci		ipv6_addr_prefix(&prefix_buf,
96862306a36Sopenharmony_ci				 (struct in6_addr *)rinfo->prefix,
96962306a36Sopenharmony_ci				 rinfo->prefix_len);
97062306a36Sopenharmony_ci		prefix = &prefix_buf;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (rinfo->prefix_len == 0)
97462306a36Sopenharmony_ci		rt = rt6_get_dflt_router(net, gwaddr, dev);
97562306a36Sopenharmony_ci	else
97662306a36Sopenharmony_ci		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
97762306a36Sopenharmony_ci					gwaddr, dev);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (rt && !lifetime) {
98062306a36Sopenharmony_ci		ip6_del_rt(net, rt, false);
98162306a36Sopenharmony_ci		rt = NULL;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (!rt && lifetime)
98562306a36Sopenharmony_ci		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
98662306a36Sopenharmony_ci					dev, pref);
98762306a36Sopenharmony_ci	else if (rt)
98862306a36Sopenharmony_ci		rt->fib6_flags = RTF_ROUTEINFO |
98962306a36Sopenharmony_ci				 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	if (rt) {
99262306a36Sopenharmony_ci		if (!addrconf_finite_timeout(lifetime))
99362306a36Sopenharmony_ci			fib6_clean_expires(rt);
99462306a36Sopenharmony_ci		else
99562306a36Sopenharmony_ci			fib6_set_expires(rt, jiffies + HZ * lifetime);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		fib6_info_release(rt);
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci	return 0;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci#endif
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci/*
100462306a36Sopenharmony_ci *	Misc support functions
100562306a36Sopenharmony_ci */
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci/* called with rcu_lock held */
100862306a36Sopenharmony_cistatic struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct net_device *dev = res->nh->fib_nh_dev;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
101362306a36Sopenharmony_ci		/* for copies of local routes, dst->dev needs to be the
101462306a36Sopenharmony_ci		 * device if it is a master device, the master device if
101562306a36Sopenharmony_ci		 * device is enslaved, and the loopback as the default
101662306a36Sopenharmony_ci		 */
101762306a36Sopenharmony_ci		if (netif_is_l3_slave(dev) &&
101862306a36Sopenharmony_ci		    !rt6_need_strict(&res->f6i->fib6_dst.addr))
101962306a36Sopenharmony_ci			dev = l3mdev_master_dev_rcu(dev);
102062306a36Sopenharmony_ci		else if (!netif_is_l3_master(dev))
102162306a36Sopenharmony_ci			dev = dev_net(dev)->loopback_dev;
102262306a36Sopenharmony_ci		/* last case is netif_is_l3_master(dev) is true in which
102362306a36Sopenharmony_ci		 * case we want dev returned to be dev
102462306a36Sopenharmony_ci		 */
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	return dev;
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic const int fib6_prop[RTN_MAX + 1] = {
103162306a36Sopenharmony_ci	[RTN_UNSPEC]	= 0,
103262306a36Sopenharmony_ci	[RTN_UNICAST]	= 0,
103362306a36Sopenharmony_ci	[RTN_LOCAL]	= 0,
103462306a36Sopenharmony_ci	[RTN_BROADCAST]	= 0,
103562306a36Sopenharmony_ci	[RTN_ANYCAST]	= 0,
103662306a36Sopenharmony_ci	[RTN_MULTICAST]	= 0,
103762306a36Sopenharmony_ci	[RTN_BLACKHOLE]	= -EINVAL,
103862306a36Sopenharmony_ci	[RTN_UNREACHABLE] = -EHOSTUNREACH,
103962306a36Sopenharmony_ci	[RTN_PROHIBIT]	= -EACCES,
104062306a36Sopenharmony_ci	[RTN_THROW]	= -EAGAIN,
104162306a36Sopenharmony_ci	[RTN_NAT]	= -EINVAL,
104262306a36Sopenharmony_ci	[RTN_XRESOLVE]	= -EINVAL,
104362306a36Sopenharmony_ci};
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	return fib6_prop[fib6_type];
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cistatic unsigned short fib6_info_dst_flags(struct fib6_info *rt)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	unsigned short flags = 0;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (rt->dst_nocount)
105562306a36Sopenharmony_ci		flags |= DST_NOCOUNT;
105662306a36Sopenharmony_ci	if (rt->dst_nopolicy)
105762306a36Sopenharmony_ci		flags |= DST_NOPOLICY;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return flags;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	rt->dst.error = ip6_rt_type_to_error(fib6_type);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	switch (fib6_type) {
106762306a36Sopenharmony_ci	case RTN_BLACKHOLE:
106862306a36Sopenharmony_ci		rt->dst.output = dst_discard_out;
106962306a36Sopenharmony_ci		rt->dst.input = dst_discard;
107062306a36Sopenharmony_ci		break;
107162306a36Sopenharmony_ci	case RTN_PROHIBIT:
107262306a36Sopenharmony_ci		rt->dst.output = ip6_pkt_prohibit_out;
107362306a36Sopenharmony_ci		rt->dst.input = ip6_pkt_prohibit;
107462306a36Sopenharmony_ci		break;
107562306a36Sopenharmony_ci	case RTN_THROW:
107662306a36Sopenharmony_ci	case RTN_UNREACHABLE:
107762306a36Sopenharmony_ci	default:
107862306a36Sopenharmony_ci		rt->dst.output = ip6_pkt_discard_out;
107962306a36Sopenharmony_ci		rt->dst.input = ip6_pkt_discard;
108062306a36Sopenharmony_ci		break;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (res->fib6_flags & RTF_REJECT) {
108962306a36Sopenharmony_ci		ip6_rt_init_dst_reject(rt, res->fib6_type);
109062306a36Sopenharmony_ci		return;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	rt->dst.error = 0;
109462306a36Sopenharmony_ci	rt->dst.output = ip6_output;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) {
109762306a36Sopenharmony_ci		rt->dst.input = ip6_input;
109862306a36Sopenharmony_ci	} else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
109962306a36Sopenharmony_ci		rt->dst.input = ip6_mc_input;
110062306a36Sopenharmony_ci	} else {
110162306a36Sopenharmony_ci		rt->dst.input = ip6_forward;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (res->nh->fib_nh_lws) {
110562306a36Sopenharmony_ci		rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
110662306a36Sopenharmony_ci		lwtunnel_set_redirect(&rt->dst);
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	rt->dst.lastuse = jiffies;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci/* Caller must already hold reference to @from */
111362306a36Sopenharmony_cistatic void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	rt->rt6i_flags &= ~RTF_EXPIRES;
111662306a36Sopenharmony_ci	rcu_assign_pointer(rt->from, from);
111762306a36Sopenharmony_ci	ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci/* Caller must already hold reference to f6i in result */
112162306a36Sopenharmony_cistatic void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
112462306a36Sopenharmony_ci	const struct net_device *dev = nh->fib_nh_dev;
112562306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	ip6_rt_init_dst(rt, res);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	rt->rt6i_dst = f6i->fib6_dst;
113062306a36Sopenharmony_ci	rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
113162306a36Sopenharmony_ci	rt->rt6i_flags = res->fib6_flags;
113262306a36Sopenharmony_ci	if (nh->fib_nh_gw_family) {
113362306a36Sopenharmony_ci		rt->rt6i_gateway = nh->fib_nh_gw6;
113462306a36Sopenharmony_ci		rt->rt6i_flags |= RTF_GATEWAY;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci	rt6_set_from(rt, f6i);
113762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
113862306a36Sopenharmony_ci	rt->rt6i_src = f6i->fib6_src;
113962306a36Sopenharmony_ci#endif
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cistatic struct fib6_node* fib6_backtrack(struct fib6_node *fn,
114362306a36Sopenharmony_ci					struct in6_addr *saddr)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct fib6_node *pn, *sn;
114662306a36Sopenharmony_ci	while (1) {
114762306a36Sopenharmony_ci		if (fn->fn_flags & RTN_TL_ROOT)
114862306a36Sopenharmony_ci			return NULL;
114962306a36Sopenharmony_ci		pn = rcu_dereference(fn->parent);
115062306a36Sopenharmony_ci		sn = FIB6_SUBTREE(pn);
115162306a36Sopenharmony_ci		if (sn && sn != fn)
115262306a36Sopenharmony_ci			fn = fib6_node_lookup(sn, NULL, saddr);
115362306a36Sopenharmony_ci		else
115462306a36Sopenharmony_ci			fn = pn;
115562306a36Sopenharmony_ci		if (fn->fn_flags & RTN_RTINFO)
115662306a36Sopenharmony_ci			return fn;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct rt6_info *rt = *prt;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (dst_hold_safe(&rt->dst))
116562306a36Sopenharmony_ci		return true;
116662306a36Sopenharmony_ci	if (net) {
116762306a36Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
116862306a36Sopenharmony_ci		dst_hold(&rt->dst);
116962306a36Sopenharmony_ci	} else {
117062306a36Sopenharmony_ci		rt = NULL;
117162306a36Sopenharmony_ci	}
117262306a36Sopenharmony_ci	*prt = rt;
117362306a36Sopenharmony_ci	return false;
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci/* called with rcu_lock held */
117762306a36Sopenharmony_cistatic struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct net_device *dev = res->nh->fib_nh_dev;
118062306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
118162306a36Sopenharmony_ci	unsigned short flags;
118262306a36Sopenharmony_ci	struct rt6_info *nrt;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (!fib6_info_hold_safe(f6i))
118562306a36Sopenharmony_ci		goto fallback;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	flags = fib6_info_dst_flags(f6i);
118862306a36Sopenharmony_ci	nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
118962306a36Sopenharmony_ci	if (!nrt) {
119062306a36Sopenharmony_ci		fib6_info_release(f6i);
119162306a36Sopenharmony_ci		goto fallback;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	ip6_rt_copy_init(nrt, res);
119562306a36Sopenharmony_ci	return nrt;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_cifallback:
119862306a36Sopenharmony_ci	nrt = dev_net(dev)->ipv6.ip6_null_entry;
119962306a36Sopenharmony_ci	dst_hold(&nrt->dst);
120062306a36Sopenharmony_ci	return nrt;
120162306a36Sopenharmony_ci}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net,
120462306a36Sopenharmony_ci					     struct fib6_table *table,
120562306a36Sopenharmony_ci					     struct flowi6 *fl6,
120662306a36Sopenharmony_ci					     const struct sk_buff *skb,
120762306a36Sopenharmony_ci					     int flags)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	struct fib6_result res = {};
121062306a36Sopenharmony_ci	struct fib6_node *fn;
121162306a36Sopenharmony_ci	struct rt6_info *rt;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	rcu_read_lock();
121462306a36Sopenharmony_ci	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
121562306a36Sopenharmony_cirestart:
121662306a36Sopenharmony_ci	res.f6i = rcu_dereference(fn->leaf);
121762306a36Sopenharmony_ci	if (!res.f6i)
121862306a36Sopenharmony_ci		res.f6i = net->ipv6.fib6_null_entry;
121962306a36Sopenharmony_ci	else
122062306a36Sopenharmony_ci		rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
122162306a36Sopenharmony_ci				 flags);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	if (res.f6i == net->ipv6.fib6_null_entry) {
122462306a36Sopenharmony_ci		fn = fib6_backtrack(fn, &fl6->saddr);
122562306a36Sopenharmony_ci		if (fn)
122662306a36Sopenharmony_ci			goto restart;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
122962306a36Sopenharmony_ci		dst_hold(&rt->dst);
123062306a36Sopenharmony_ci		goto out;
123162306a36Sopenharmony_ci	} else if (res.fib6_flags & RTF_REJECT) {
123262306a36Sopenharmony_ci		goto do_create;
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
123662306a36Sopenharmony_ci			 fl6->flowi6_oif != 0, skb, flags);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	/* Search through exception table */
123962306a36Sopenharmony_ci	rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
124062306a36Sopenharmony_ci	if (rt) {
124162306a36Sopenharmony_ci		if (ip6_hold_safe(net, &rt))
124262306a36Sopenharmony_ci			dst_use_noref(&rt->dst, jiffies);
124362306a36Sopenharmony_ci	} else {
124462306a36Sopenharmony_cido_create:
124562306a36Sopenharmony_ci		rt = ip6_create_rt_rcu(&res);
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ciout:
124962306a36Sopenharmony_ci	trace_fib6_table_lookup(net, &res, table, fl6);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	rcu_read_unlock();
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	return rt;
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_cistruct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
125762306a36Sopenharmony_ci				   const struct sk_buff *skb, int flags)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_lookup);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistruct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
126462306a36Sopenharmony_ci			    const struct in6_addr *saddr, int oif,
126562306a36Sopenharmony_ci			    const struct sk_buff *skb, int strict)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	struct flowi6 fl6 = {
126862306a36Sopenharmony_ci		.flowi6_oif = oif,
126962306a36Sopenharmony_ci		.daddr = *daddr,
127062306a36Sopenharmony_ci	};
127162306a36Sopenharmony_ci	struct dst_entry *dst;
127262306a36Sopenharmony_ci	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (saddr) {
127562306a36Sopenharmony_ci		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
127662306a36Sopenharmony_ci		flags |= RT6_LOOKUP_F_HAS_SADDR;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
128062306a36Sopenharmony_ci	if (dst->error == 0)
128162306a36Sopenharmony_ci		return (struct rt6_info *) dst;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	dst_release(dst);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	return NULL;
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ciEXPORT_SYMBOL(rt6_lookup);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci/* ip6_ins_rt is called with FREE table->tb6_lock.
129062306a36Sopenharmony_ci * It takes new route entry, the addition fails by any reason the
129162306a36Sopenharmony_ci * route is released.
129262306a36Sopenharmony_ci * Caller must hold dst before calling it.
129362306a36Sopenharmony_ci */
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
129662306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	int err;
129962306a36Sopenharmony_ci	struct fib6_table *table;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	table = rt->fib6_table;
130262306a36Sopenharmony_ci	spin_lock_bh(&table->tb6_lock);
130362306a36Sopenharmony_ci	err = fib6_add(&table->tb6_root, rt, info, extack);
130462306a36Sopenharmony_ci	spin_unlock_bh(&table->tb6_lock);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return err;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ciint ip6_ins_rt(struct net *net, struct fib6_info *rt)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct nl_info info = {	.nl_net = net, };
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	return __ip6_ins_rt(rt, &info, NULL);
131462306a36Sopenharmony_ci}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_cistatic struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
131762306a36Sopenharmony_ci					   const struct in6_addr *daddr,
131862306a36Sopenharmony_ci					   const struct in6_addr *saddr)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
132162306a36Sopenharmony_ci	struct net_device *dev;
132262306a36Sopenharmony_ci	struct rt6_info *rt;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/*
132562306a36Sopenharmony_ci	 *	Clone the route.
132662306a36Sopenharmony_ci	 */
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (!fib6_info_hold_safe(f6i))
132962306a36Sopenharmony_ci		return NULL;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	dev = ip6_rt_get_dev_rcu(res);
133262306a36Sopenharmony_ci	rt = ip6_dst_alloc(dev_net(dev), dev, 0);
133362306a36Sopenharmony_ci	if (!rt) {
133462306a36Sopenharmony_ci		fib6_info_release(f6i);
133562306a36Sopenharmony_ci		return NULL;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	ip6_rt_copy_init(rt, res);
133962306a36Sopenharmony_ci	rt->rt6i_flags |= RTF_CACHE;
134062306a36Sopenharmony_ci	rt->rt6i_dst.addr = *daddr;
134162306a36Sopenharmony_ci	rt->rt6i_dst.plen = 128;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (!rt6_is_gw_or_nonexthop(res)) {
134462306a36Sopenharmony_ci		if (f6i->fib6_dst.plen != 128 &&
134562306a36Sopenharmony_ci		    ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
134662306a36Sopenharmony_ci			rt->rt6i_flags |= RTF_ANYCAST;
134762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
134862306a36Sopenharmony_ci		if (rt->rt6i_src.plen && saddr) {
134962306a36Sopenharmony_ci			rt->rt6i_src.addr = *saddr;
135062306a36Sopenharmony_ci			rt->rt6i_src.plen = 128;
135162306a36Sopenharmony_ci		}
135262306a36Sopenharmony_ci#endif
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	return rt;
135662306a36Sopenharmony_ci}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cistatic struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
135962306a36Sopenharmony_ci{
136062306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
136162306a36Sopenharmony_ci	unsigned short flags = fib6_info_dst_flags(f6i);
136262306a36Sopenharmony_ci	struct net_device *dev;
136362306a36Sopenharmony_ci	struct rt6_info *pcpu_rt;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	if (!fib6_info_hold_safe(f6i))
136662306a36Sopenharmony_ci		return NULL;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	rcu_read_lock();
136962306a36Sopenharmony_ci	dev = ip6_rt_get_dev_rcu(res);
137062306a36Sopenharmony_ci	pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags | DST_NOCOUNT);
137162306a36Sopenharmony_ci	rcu_read_unlock();
137262306a36Sopenharmony_ci	if (!pcpu_rt) {
137362306a36Sopenharmony_ci		fib6_info_release(f6i);
137462306a36Sopenharmony_ci		return NULL;
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci	ip6_rt_copy_init(pcpu_rt, res);
137762306a36Sopenharmony_ci	pcpu_rt->rt6i_flags |= RTF_PCPU;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (f6i->nh)
138062306a36Sopenharmony_ci		pcpu_rt->sernum = rt_genid_ipv6(dev_net(dev));
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	return pcpu_rt;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic bool rt6_is_valid(const struct rt6_info *rt6)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev));
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci/* It should be called with rcu_read_lock() acquired */
139162306a36Sopenharmony_cistatic struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
139262306a36Sopenharmony_ci{
139362306a36Sopenharmony_ci	struct rt6_info *pcpu_rt;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (pcpu_rt && pcpu_rt->sernum && !rt6_is_valid(pcpu_rt)) {
139862306a36Sopenharmony_ci		struct rt6_info *prev, **p;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		p = this_cpu_ptr(res->nh->rt6i_pcpu);
140162306a36Sopenharmony_ci		prev = xchg(p, NULL);
140262306a36Sopenharmony_ci		if (prev) {
140362306a36Sopenharmony_ci			dst_dev_put(&prev->dst);
140462306a36Sopenharmony_ci			dst_release(&prev->dst);
140562306a36Sopenharmony_ci		}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci		pcpu_rt = NULL;
140862306a36Sopenharmony_ci	}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	return pcpu_rt;
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_cistatic struct rt6_info *rt6_make_pcpu_route(struct net *net,
141462306a36Sopenharmony_ci					    const struct fib6_result *res)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	struct rt6_info *pcpu_rt, *prev, **p;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	pcpu_rt = ip6_rt_pcpu_alloc(res);
141962306a36Sopenharmony_ci	if (!pcpu_rt)
142062306a36Sopenharmony_ci		return NULL;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	p = this_cpu_ptr(res->nh->rt6i_pcpu);
142362306a36Sopenharmony_ci	prev = cmpxchg(p, NULL, pcpu_rt);
142462306a36Sopenharmony_ci	BUG_ON(prev);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (res->f6i->fib6_destroying) {
142762306a36Sopenharmony_ci		struct fib6_info *from;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
143062306a36Sopenharmony_ci		fib6_info_release(from);
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	return pcpu_rt;
143462306a36Sopenharmony_ci}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci/* exception hash table implementation
143762306a36Sopenharmony_ci */
143862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(rt6_exception_lock);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci/* Remove rt6_ex from hash table and free the memory
144162306a36Sopenharmony_ci * Caller must hold rt6_exception_lock
144262306a36Sopenharmony_ci */
144362306a36Sopenharmony_cistatic void rt6_remove_exception(struct rt6_exception_bucket *bucket,
144462306a36Sopenharmony_ci				 struct rt6_exception *rt6_ex)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct fib6_info *from;
144762306a36Sopenharmony_ci	struct net *net;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	if (!bucket || !rt6_ex)
145062306a36Sopenharmony_ci		return;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	net = dev_net(rt6_ex->rt6i->dst.dev);
145362306a36Sopenharmony_ci	net->ipv6.rt6_stats->fib_rt_cache--;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	/* purge completely the exception to allow releasing the held resources:
145662306a36Sopenharmony_ci	 * some [sk] cache may keep the dst around for unlimited time
145762306a36Sopenharmony_ci	 */
145862306a36Sopenharmony_ci	from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL);
145962306a36Sopenharmony_ci	fib6_info_release(from);
146062306a36Sopenharmony_ci	dst_dev_put(&rt6_ex->rt6i->dst);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	hlist_del_rcu(&rt6_ex->hlist);
146362306a36Sopenharmony_ci	dst_release(&rt6_ex->rt6i->dst);
146462306a36Sopenharmony_ci	kfree_rcu(rt6_ex, rcu);
146562306a36Sopenharmony_ci	WARN_ON_ONCE(!bucket->depth);
146662306a36Sopenharmony_ci	bucket->depth--;
146762306a36Sopenharmony_ci}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci/* Remove oldest rt6_ex in bucket and free the memory
147062306a36Sopenharmony_ci * Caller must hold rt6_exception_lock
147162306a36Sopenharmony_ci */
147262306a36Sopenharmony_cistatic void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct rt6_exception *rt6_ex, *oldest = NULL;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	if (!bucket)
147762306a36Sopenharmony_ci		return;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
148062306a36Sopenharmony_ci		if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
148162306a36Sopenharmony_ci			oldest = rt6_ex;
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci	rt6_remove_exception(bucket, oldest);
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic u32 rt6_exception_hash(const struct in6_addr *dst,
148762306a36Sopenharmony_ci			      const struct in6_addr *src)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	static siphash_aligned_key_t rt6_exception_key;
149062306a36Sopenharmony_ci	struct {
149162306a36Sopenharmony_ci		struct in6_addr dst;
149262306a36Sopenharmony_ci		struct in6_addr src;
149362306a36Sopenharmony_ci	} __aligned(SIPHASH_ALIGNMENT) combined = {
149462306a36Sopenharmony_ci		.dst = *dst,
149562306a36Sopenharmony_ci	};
149662306a36Sopenharmony_ci	u64 val;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	net_get_random_once(&rt6_exception_key, sizeof(rt6_exception_key));
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
150162306a36Sopenharmony_ci	if (src)
150262306a36Sopenharmony_ci		combined.src = *src;
150362306a36Sopenharmony_ci#endif
150462306a36Sopenharmony_ci	val = siphash(&combined, sizeof(combined), &rt6_exception_key);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	return hash_64(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci/* Helper function to find the cached rt in the hash table
151062306a36Sopenharmony_ci * and update bucket pointer to point to the bucket for this
151162306a36Sopenharmony_ci * (daddr, saddr) pair
151262306a36Sopenharmony_ci * Caller must hold rt6_exception_lock
151362306a36Sopenharmony_ci */
151462306a36Sopenharmony_cistatic struct rt6_exception *
151562306a36Sopenharmony_ci__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
151662306a36Sopenharmony_ci			      const struct in6_addr *daddr,
151762306a36Sopenharmony_ci			      const struct in6_addr *saddr)
151862306a36Sopenharmony_ci{
151962306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
152062306a36Sopenharmony_ci	u32 hval;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	if (!(*bucket) || !daddr)
152362306a36Sopenharmony_ci		return NULL;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	hval = rt6_exception_hash(daddr, saddr);
152662306a36Sopenharmony_ci	*bucket += hval;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
152962306a36Sopenharmony_ci		struct rt6_info *rt6 = rt6_ex->rt6i;
153062306a36Sopenharmony_ci		bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
153362306a36Sopenharmony_ci		if (matched && saddr)
153462306a36Sopenharmony_ci			matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
153562306a36Sopenharmony_ci#endif
153662306a36Sopenharmony_ci		if (matched)
153762306a36Sopenharmony_ci			return rt6_ex;
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci	return NULL;
154062306a36Sopenharmony_ci}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci/* Helper function to find the cached rt in the hash table
154362306a36Sopenharmony_ci * and update bucket pointer to point to the bucket for this
154462306a36Sopenharmony_ci * (daddr, saddr) pair
154562306a36Sopenharmony_ci * Caller must hold rcu_read_lock()
154662306a36Sopenharmony_ci */
154762306a36Sopenharmony_cistatic struct rt6_exception *
154862306a36Sopenharmony_ci__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
154962306a36Sopenharmony_ci			 const struct in6_addr *daddr,
155062306a36Sopenharmony_ci			 const struct in6_addr *saddr)
155162306a36Sopenharmony_ci{
155262306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
155362306a36Sopenharmony_ci	u32 hval;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	WARN_ON_ONCE(!rcu_read_lock_held());
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	if (!(*bucket) || !daddr)
155862306a36Sopenharmony_ci		return NULL;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	hval = rt6_exception_hash(daddr, saddr);
156162306a36Sopenharmony_ci	*bucket += hval;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
156462306a36Sopenharmony_ci		struct rt6_info *rt6 = rt6_ex->rt6i;
156562306a36Sopenharmony_ci		bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
156862306a36Sopenharmony_ci		if (matched && saddr)
156962306a36Sopenharmony_ci			matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
157062306a36Sopenharmony_ci#endif
157162306a36Sopenharmony_ci		if (matched)
157262306a36Sopenharmony_ci			return rt6_ex;
157362306a36Sopenharmony_ci	}
157462306a36Sopenharmony_ci	return NULL;
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_cistatic unsigned int fib6_mtu(const struct fib6_result *res)
157862306a36Sopenharmony_ci{
157962306a36Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
158062306a36Sopenharmony_ci	unsigned int mtu;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	if (res->f6i->fib6_pmtu) {
158362306a36Sopenharmony_ci		mtu = res->f6i->fib6_pmtu;
158462306a36Sopenharmony_ci	} else {
158562306a36Sopenharmony_ci		struct net_device *dev = nh->fib_nh_dev;
158662306a36Sopenharmony_ci		struct inet6_dev *idev;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci		rcu_read_lock();
158962306a36Sopenharmony_ci		idev = __in6_dev_get(dev);
159062306a36Sopenharmony_ci		mtu = idev->cnf.mtu6;
159162306a36Sopenharmony_ci		rcu_read_unlock();
159262306a36Sopenharmony_ci	}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
159762306a36Sopenharmony_ci}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci#define FIB6_EXCEPTION_BUCKET_FLUSHED  0x1UL
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci/* used when the flushed bit is not relevant, only access to the bucket
160262306a36Sopenharmony_ci * (ie., all bucket users except rt6_insert_exception);
160362306a36Sopenharmony_ci *
160462306a36Sopenharmony_ci * called under rcu lock; sometimes called with rt6_exception_lock held
160562306a36Sopenharmony_ci */
160662306a36Sopenharmony_cistatic
160762306a36Sopenharmony_cistruct rt6_exception_bucket *fib6_nh_get_excptn_bucket(const struct fib6_nh *nh,
160862306a36Sopenharmony_ci						       spinlock_t *lock)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	if (lock)
161362306a36Sopenharmony_ci		bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
161462306a36Sopenharmony_ci						   lockdep_is_held(lock));
161562306a36Sopenharmony_ci	else
161662306a36Sopenharmony_ci		bucket = rcu_dereference(nh->rt6i_exception_bucket);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	/* remove bucket flushed bit if set */
161962306a36Sopenharmony_ci	if (bucket) {
162062306a36Sopenharmony_ci		unsigned long p = (unsigned long)bucket;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci		p &= ~FIB6_EXCEPTION_BUCKET_FLUSHED;
162362306a36Sopenharmony_ci		bucket = (struct rt6_exception_bucket *)p;
162462306a36Sopenharmony_ci	}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	return bucket;
162762306a36Sopenharmony_ci}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_cistatic bool fib6_nh_excptn_bucket_flushed(struct rt6_exception_bucket *bucket)
163062306a36Sopenharmony_ci{
163162306a36Sopenharmony_ci	unsigned long p = (unsigned long)bucket;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	return !!(p & FIB6_EXCEPTION_BUCKET_FLUSHED);
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci/* called with rt6_exception_lock held */
163762306a36Sopenharmony_cistatic void fib6_nh_excptn_bucket_set_flushed(struct fib6_nh *nh,
163862306a36Sopenharmony_ci					      spinlock_t *lock)
163962306a36Sopenharmony_ci{
164062306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
164162306a36Sopenharmony_ci	unsigned long p;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
164462306a36Sopenharmony_ci					   lockdep_is_held(lock));
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	p = (unsigned long)bucket;
164762306a36Sopenharmony_ci	p |= FIB6_EXCEPTION_BUCKET_FLUSHED;
164862306a36Sopenharmony_ci	bucket = (struct rt6_exception_bucket *)p;
164962306a36Sopenharmony_ci	rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
165062306a36Sopenharmony_ci}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_cistatic int rt6_insert_exception(struct rt6_info *nrt,
165362306a36Sopenharmony_ci				const struct fib6_result *res)
165462306a36Sopenharmony_ci{
165562306a36Sopenharmony_ci	struct net *net = dev_net(nrt->dst.dev);
165662306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
165762306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
165862306a36Sopenharmony_ci	struct in6_addr *src_key = NULL;
165962306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
166062306a36Sopenharmony_ci	struct fib6_nh *nh = res->nh;
166162306a36Sopenharmony_ci	int max_depth;
166262306a36Sopenharmony_ci	int err = 0;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
166762306a36Sopenharmony_ci					  lockdep_is_held(&rt6_exception_lock));
166862306a36Sopenharmony_ci	if (!bucket) {
166962306a36Sopenharmony_ci		bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
167062306a36Sopenharmony_ci				 GFP_ATOMIC);
167162306a36Sopenharmony_ci		if (!bucket) {
167262306a36Sopenharmony_ci			err = -ENOMEM;
167362306a36Sopenharmony_ci			goto out;
167462306a36Sopenharmony_ci		}
167562306a36Sopenharmony_ci		rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
167662306a36Sopenharmony_ci	} else if (fib6_nh_excptn_bucket_flushed(bucket)) {
167762306a36Sopenharmony_ci		err = -EINVAL;
167862306a36Sopenharmony_ci		goto out;
167962306a36Sopenharmony_ci	}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
168262306a36Sopenharmony_ci	/* fib6_src.plen != 0 indicates f6i is in subtree
168362306a36Sopenharmony_ci	 * and exception table is indexed by a hash of
168462306a36Sopenharmony_ci	 * both fib6_dst and fib6_src.
168562306a36Sopenharmony_ci	 * Otherwise, the exception table is indexed by
168662306a36Sopenharmony_ci	 * a hash of only fib6_dst.
168762306a36Sopenharmony_ci	 */
168862306a36Sopenharmony_ci	if (f6i->fib6_src.plen)
168962306a36Sopenharmony_ci		src_key = &nrt->rt6i_src.addr;
169062306a36Sopenharmony_ci#endif
169162306a36Sopenharmony_ci	/* rt6_mtu_change() might lower mtu on f6i.
169262306a36Sopenharmony_ci	 * Only insert this exception route if its mtu
169362306a36Sopenharmony_ci	 * is less than f6i's mtu value.
169462306a36Sopenharmony_ci	 */
169562306a36Sopenharmony_ci	if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
169662306a36Sopenharmony_ci		err = -EINVAL;
169762306a36Sopenharmony_ci		goto out;
169862306a36Sopenharmony_ci	}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
170162306a36Sopenharmony_ci					       src_key);
170262306a36Sopenharmony_ci	if (rt6_ex)
170362306a36Sopenharmony_ci		rt6_remove_exception(bucket, rt6_ex);
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
170662306a36Sopenharmony_ci	if (!rt6_ex) {
170762306a36Sopenharmony_ci		err = -ENOMEM;
170862306a36Sopenharmony_ci		goto out;
170962306a36Sopenharmony_ci	}
171062306a36Sopenharmony_ci	rt6_ex->rt6i = nrt;
171162306a36Sopenharmony_ci	rt6_ex->stamp = jiffies;
171262306a36Sopenharmony_ci	hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
171362306a36Sopenharmony_ci	bucket->depth++;
171462306a36Sopenharmony_ci	net->ipv6.rt6_stats->fib_rt_cache++;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	/* Randomize max depth to avoid some side channels attacks. */
171762306a36Sopenharmony_ci	max_depth = FIB6_MAX_DEPTH + get_random_u32_below(FIB6_MAX_DEPTH);
171862306a36Sopenharmony_ci	while (bucket->depth > max_depth)
171962306a36Sopenharmony_ci		rt6_exception_remove_oldest(bucket);
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ciout:
172262306a36Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	/* Update fn->fn_sernum to invalidate all cached dst */
172562306a36Sopenharmony_ci	if (!err) {
172662306a36Sopenharmony_ci		spin_lock_bh(&f6i->fib6_table->tb6_lock);
172762306a36Sopenharmony_ci		fib6_update_sernum(net, f6i);
172862306a36Sopenharmony_ci		spin_unlock_bh(&f6i->fib6_table->tb6_lock);
172962306a36Sopenharmony_ci		fib6_force_start_gc(net);
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	return err;
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_cistatic void fib6_nh_flush_exceptions(struct fib6_nh *nh, struct fib6_info *from)
173662306a36Sopenharmony_ci{
173762306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
173862306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
173962306a36Sopenharmony_ci	struct hlist_node *tmp;
174062306a36Sopenharmony_ci	int i;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
174562306a36Sopenharmony_ci	if (!bucket)
174662306a36Sopenharmony_ci		goto out;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	/* Prevent rt6_insert_exception() to recreate the bucket list */
174962306a36Sopenharmony_ci	if (!from)
175062306a36Sopenharmony_ci		fib6_nh_excptn_bucket_set_flushed(nh, &rt6_exception_lock);
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
175362306a36Sopenharmony_ci		hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) {
175462306a36Sopenharmony_ci			if (!from ||
175562306a36Sopenharmony_ci			    rcu_access_pointer(rt6_ex->rt6i->from) == from)
175662306a36Sopenharmony_ci				rt6_remove_exception(bucket, rt6_ex);
175762306a36Sopenharmony_ci		}
175862306a36Sopenharmony_ci		WARN_ON_ONCE(!from && bucket->depth);
175962306a36Sopenharmony_ci		bucket++;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ciout:
176262306a36Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
176362306a36Sopenharmony_ci}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cistatic int rt6_nh_flush_exceptions(struct fib6_nh *nh, void *arg)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	struct fib6_info *f6i = arg;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	fib6_nh_flush_exceptions(nh, f6i);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	return 0;
177262306a36Sopenharmony_ci}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_civoid rt6_flush_exceptions(struct fib6_info *f6i)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	if (f6i->nh)
177762306a36Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_flush_exceptions,
177862306a36Sopenharmony_ci					 f6i);
177962306a36Sopenharmony_ci	else
178062306a36Sopenharmony_ci		fib6_nh_flush_exceptions(f6i->fib6_nh, f6i);
178162306a36Sopenharmony_ci}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci/* Find cached rt in the hash table inside passed in rt
178462306a36Sopenharmony_ci * Caller has to hold rcu_read_lock()
178562306a36Sopenharmony_ci */
178662306a36Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
178762306a36Sopenharmony_ci					   const struct in6_addr *daddr,
178862306a36Sopenharmony_ci					   const struct in6_addr *saddr)
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci	const struct in6_addr *src_key = NULL;
179162306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
179262306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
179362306a36Sopenharmony_ci	struct rt6_info *ret = NULL;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
179662306a36Sopenharmony_ci	/* fib6i_src.plen != 0 indicates f6i is in subtree
179762306a36Sopenharmony_ci	 * and exception table is indexed by a hash of
179862306a36Sopenharmony_ci	 * both fib6_dst and fib6_src.
179962306a36Sopenharmony_ci	 * However, the src addr used to create the hash
180062306a36Sopenharmony_ci	 * might not be exactly the passed in saddr which
180162306a36Sopenharmony_ci	 * is a /128 addr from the flow.
180262306a36Sopenharmony_ci	 * So we need to use f6i->fib6_src to redo lookup
180362306a36Sopenharmony_ci	 * if the passed in saddr does not find anything.
180462306a36Sopenharmony_ci	 * (See the logic in ip6_rt_cache_alloc() on how
180562306a36Sopenharmony_ci	 * rt->rt6i_src is updated.)
180662306a36Sopenharmony_ci	 */
180762306a36Sopenharmony_ci	if (res->f6i->fib6_src.plen)
180862306a36Sopenharmony_ci		src_key = saddr;
180962306a36Sopenharmony_cifind_ex:
181062306a36Sopenharmony_ci#endif
181162306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(res->nh, NULL);
181262306a36Sopenharmony_ci	rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
181562306a36Sopenharmony_ci		ret = rt6_ex->rt6i;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
181862306a36Sopenharmony_ci	/* Use fib6_src as src_key and redo lookup */
181962306a36Sopenharmony_ci	if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) {
182062306a36Sopenharmony_ci		src_key = &res->f6i->fib6_src.addr;
182162306a36Sopenharmony_ci		goto find_ex;
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci#endif
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	return ret;
182662306a36Sopenharmony_ci}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci/* Remove the passed in cached rt from the hash table that contains it */
182962306a36Sopenharmony_cistatic int fib6_nh_remove_exception(const struct fib6_nh *nh, int plen,
183062306a36Sopenharmony_ci				    const struct rt6_info *rt)
183162306a36Sopenharmony_ci{
183262306a36Sopenharmony_ci	const struct in6_addr *src_key = NULL;
183362306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
183462306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
183562306a36Sopenharmony_ci	int err;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
183862306a36Sopenharmony_ci		return -ENOENT;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
184162306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
184462306a36Sopenharmony_ci	/* rt6i_src.plen != 0 indicates 'from' is in subtree
184562306a36Sopenharmony_ci	 * and exception table is indexed by a hash of
184662306a36Sopenharmony_ci	 * both rt6i_dst and rt6i_src.
184762306a36Sopenharmony_ci	 * Otherwise, the exception table is indexed by
184862306a36Sopenharmony_ci	 * a hash of only rt6i_dst.
184962306a36Sopenharmony_ci	 */
185062306a36Sopenharmony_ci	if (plen)
185162306a36Sopenharmony_ci		src_key = &rt->rt6i_src.addr;
185262306a36Sopenharmony_ci#endif
185362306a36Sopenharmony_ci	rt6_ex = __rt6_find_exception_spinlock(&bucket,
185462306a36Sopenharmony_ci					       &rt->rt6i_dst.addr,
185562306a36Sopenharmony_ci					       src_key);
185662306a36Sopenharmony_ci	if (rt6_ex) {
185762306a36Sopenharmony_ci		rt6_remove_exception(bucket, rt6_ex);
185862306a36Sopenharmony_ci		err = 0;
185962306a36Sopenharmony_ci	} else {
186062306a36Sopenharmony_ci		err = -ENOENT;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
186462306a36Sopenharmony_ci	return err;
186562306a36Sopenharmony_ci}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_cistruct fib6_nh_excptn_arg {
186862306a36Sopenharmony_ci	struct rt6_info	*rt;
186962306a36Sopenharmony_ci	int		plen;
187062306a36Sopenharmony_ci};
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_cistatic int rt6_nh_remove_exception_rt(struct fib6_nh *nh, void *_arg)
187362306a36Sopenharmony_ci{
187462306a36Sopenharmony_ci	struct fib6_nh_excptn_arg *arg = _arg;
187562306a36Sopenharmony_ci	int err;
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	err = fib6_nh_remove_exception(nh, arg->plen, arg->rt);
187862306a36Sopenharmony_ci	if (err == 0)
187962306a36Sopenharmony_ci		return 1;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	return 0;
188262306a36Sopenharmony_ci}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_cistatic int rt6_remove_exception_rt(struct rt6_info *rt)
188562306a36Sopenharmony_ci{
188662306a36Sopenharmony_ci	struct fib6_info *from;
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	from = rcu_dereference(rt->from);
188962306a36Sopenharmony_ci	if (!from || !(rt->rt6i_flags & RTF_CACHE))
189062306a36Sopenharmony_ci		return -EINVAL;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	if (from->nh) {
189362306a36Sopenharmony_ci		struct fib6_nh_excptn_arg arg = {
189462306a36Sopenharmony_ci			.rt = rt,
189562306a36Sopenharmony_ci			.plen = from->fib6_src.plen
189662306a36Sopenharmony_ci		};
189762306a36Sopenharmony_ci		int rc;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		/* rc = 1 means an entry was found */
190062306a36Sopenharmony_ci		rc = nexthop_for_each_fib6_nh(from->nh,
190162306a36Sopenharmony_ci					      rt6_nh_remove_exception_rt,
190262306a36Sopenharmony_ci					      &arg);
190362306a36Sopenharmony_ci		return rc ? 0 : -ENOENT;
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	return fib6_nh_remove_exception(from->fib6_nh,
190762306a36Sopenharmony_ci					from->fib6_src.plen, rt);
190862306a36Sopenharmony_ci}
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci/* Find rt6_ex which contains the passed in rt cache and
191162306a36Sopenharmony_ci * refresh its stamp
191262306a36Sopenharmony_ci */
191362306a36Sopenharmony_cistatic void fib6_nh_update_exception(const struct fib6_nh *nh, int plen,
191462306a36Sopenharmony_ci				     const struct rt6_info *rt)
191562306a36Sopenharmony_ci{
191662306a36Sopenharmony_ci	const struct in6_addr *src_key = NULL;
191762306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
191862306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, NULL);
192162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
192262306a36Sopenharmony_ci	/* rt6i_src.plen != 0 indicates 'from' is in subtree
192362306a36Sopenharmony_ci	 * and exception table is indexed by a hash of
192462306a36Sopenharmony_ci	 * both rt6i_dst and rt6i_src.
192562306a36Sopenharmony_ci	 * Otherwise, the exception table is indexed by
192662306a36Sopenharmony_ci	 * a hash of only rt6i_dst.
192762306a36Sopenharmony_ci	 */
192862306a36Sopenharmony_ci	if (plen)
192962306a36Sopenharmony_ci		src_key = &rt->rt6i_src.addr;
193062306a36Sopenharmony_ci#endif
193162306a36Sopenharmony_ci	rt6_ex = __rt6_find_exception_rcu(&bucket, &rt->rt6i_dst.addr, src_key);
193262306a36Sopenharmony_ci	if (rt6_ex)
193362306a36Sopenharmony_ci		rt6_ex->stamp = jiffies;
193462306a36Sopenharmony_ci}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_cistruct fib6_nh_match_arg {
193762306a36Sopenharmony_ci	const struct net_device *dev;
193862306a36Sopenharmony_ci	const struct in6_addr	*gw;
193962306a36Sopenharmony_ci	struct fib6_nh		*match;
194062306a36Sopenharmony_ci};
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci/* determine if fib6_nh has given device and gateway */
194362306a36Sopenharmony_cistatic int fib6_nh_find_match(struct fib6_nh *nh, void *_arg)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	struct fib6_nh_match_arg *arg = _arg;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	if (arg->dev != nh->fib_nh_dev ||
194862306a36Sopenharmony_ci	    (arg->gw && !nh->fib_nh_gw_family) ||
194962306a36Sopenharmony_ci	    (!arg->gw && nh->fib_nh_gw_family) ||
195062306a36Sopenharmony_ci	    (arg->gw && !ipv6_addr_equal(arg->gw, &nh->fib_nh_gw6)))
195162306a36Sopenharmony_ci		return 0;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	arg->match = nh;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	/* found a match, break the loop */
195662306a36Sopenharmony_ci	return 1;
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_cistatic void rt6_update_exception_stamp_rt(struct rt6_info *rt)
196062306a36Sopenharmony_ci{
196162306a36Sopenharmony_ci	struct fib6_info *from;
196262306a36Sopenharmony_ci	struct fib6_nh *fib6_nh;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	rcu_read_lock();
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	from = rcu_dereference(rt->from);
196762306a36Sopenharmony_ci	if (!from || !(rt->rt6i_flags & RTF_CACHE))
196862306a36Sopenharmony_ci		goto unlock;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	if (from->nh) {
197162306a36Sopenharmony_ci		struct fib6_nh_match_arg arg = {
197262306a36Sopenharmony_ci			.dev = rt->dst.dev,
197362306a36Sopenharmony_ci			.gw = &rt->rt6i_gateway,
197462306a36Sopenharmony_ci		};
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci		nexthop_for_each_fib6_nh(from->nh, fib6_nh_find_match, &arg);
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci		if (!arg.match)
197962306a36Sopenharmony_ci			goto unlock;
198062306a36Sopenharmony_ci		fib6_nh = arg.match;
198162306a36Sopenharmony_ci	} else {
198262306a36Sopenharmony_ci		fib6_nh = from->fib6_nh;
198362306a36Sopenharmony_ci	}
198462306a36Sopenharmony_ci	fib6_nh_update_exception(fib6_nh, from->fib6_src.plen, rt);
198562306a36Sopenharmony_ciunlock:
198662306a36Sopenharmony_ci	rcu_read_unlock();
198762306a36Sopenharmony_ci}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_cistatic bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
199062306a36Sopenharmony_ci					 struct rt6_info *rt, int mtu)
199162306a36Sopenharmony_ci{
199262306a36Sopenharmony_ci	/* If the new MTU is lower than the route PMTU, this new MTU will be the
199362306a36Sopenharmony_ci	 * lowest MTU in the path: always allow updating the route PMTU to
199462306a36Sopenharmony_ci	 * reflect PMTU decreases.
199562306a36Sopenharmony_ci	 *
199662306a36Sopenharmony_ci	 * If the new MTU is higher, and the route PMTU is equal to the local
199762306a36Sopenharmony_ci	 * MTU, this means the old MTU is the lowest in the path, so allow
199862306a36Sopenharmony_ci	 * updating it: if other nodes now have lower MTUs, PMTU discovery will
199962306a36Sopenharmony_ci	 * handle this.
200062306a36Sopenharmony_ci	 */
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	if (dst_mtu(&rt->dst) >= mtu)
200362306a36Sopenharmony_ci		return true;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
200662306a36Sopenharmony_ci		return true;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	return false;
200962306a36Sopenharmony_ci}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_cistatic void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
201262306a36Sopenharmony_ci				       const struct fib6_nh *nh, int mtu)
201362306a36Sopenharmony_ci{
201462306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
201562306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
201662306a36Sopenharmony_ci	int i;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
201962306a36Sopenharmony_ci	if (!bucket)
202062306a36Sopenharmony_ci		return;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
202362306a36Sopenharmony_ci		hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
202462306a36Sopenharmony_ci			struct rt6_info *entry = rt6_ex->rt6i;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci			/* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
202762306a36Sopenharmony_ci			 * route), the metrics of its rt->from have already
202862306a36Sopenharmony_ci			 * been updated.
202962306a36Sopenharmony_ci			 */
203062306a36Sopenharmony_ci			if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
203162306a36Sopenharmony_ci			    rt6_mtu_change_route_allowed(idev, entry, mtu))
203262306a36Sopenharmony_ci				dst_metric_set(&entry->dst, RTAX_MTU, mtu);
203362306a36Sopenharmony_ci		}
203462306a36Sopenharmony_ci		bucket++;
203562306a36Sopenharmony_ci	}
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci#define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_cistatic void fib6_nh_exceptions_clean_tohost(const struct fib6_nh *nh,
204162306a36Sopenharmony_ci					    const struct in6_addr *gateway)
204262306a36Sopenharmony_ci{
204362306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
204462306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
204562306a36Sopenharmony_ci	struct hlist_node *tmp;
204662306a36Sopenharmony_ci	int i;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
204962306a36Sopenharmony_ci		return;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	spin_lock_bh(&rt6_exception_lock);
205262306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
205362306a36Sopenharmony_ci	if (bucket) {
205462306a36Sopenharmony_ci		for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
205562306a36Sopenharmony_ci			hlist_for_each_entry_safe(rt6_ex, tmp,
205662306a36Sopenharmony_ci						  &bucket->chain, hlist) {
205762306a36Sopenharmony_ci				struct rt6_info *entry = rt6_ex->rt6i;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci				if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
206062306a36Sopenharmony_ci				    RTF_CACHE_GATEWAY &&
206162306a36Sopenharmony_ci				    ipv6_addr_equal(gateway,
206262306a36Sopenharmony_ci						    &entry->rt6i_gateway)) {
206362306a36Sopenharmony_ci					rt6_remove_exception(bucket, rt6_ex);
206462306a36Sopenharmony_ci				}
206562306a36Sopenharmony_ci			}
206662306a36Sopenharmony_ci			bucket++;
206762306a36Sopenharmony_ci		}
206862306a36Sopenharmony_ci	}
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	spin_unlock_bh(&rt6_exception_lock);
207162306a36Sopenharmony_ci}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_cistatic void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
207462306a36Sopenharmony_ci				      struct rt6_exception *rt6_ex,
207562306a36Sopenharmony_ci				      struct fib6_gc_args *gc_args,
207662306a36Sopenharmony_ci				      unsigned long now)
207762306a36Sopenharmony_ci{
207862306a36Sopenharmony_ci	struct rt6_info *rt = rt6_ex->rt6i;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	/* we are pruning and obsoleting aged-out and non gateway exceptions
208162306a36Sopenharmony_ci	 * even if others have still references to them, so that on next
208262306a36Sopenharmony_ci	 * dst_check() such references can be dropped.
208362306a36Sopenharmony_ci	 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
208462306a36Sopenharmony_ci	 * expired, independently from their aging, as per RFC 8201 section 4
208562306a36Sopenharmony_ci	 */
208662306a36Sopenharmony_ci	if (!(rt->rt6i_flags & RTF_EXPIRES)) {
208762306a36Sopenharmony_ci		if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
208862306a36Sopenharmony_ci			RT6_TRACE("aging clone %p\n", rt);
208962306a36Sopenharmony_ci			rt6_remove_exception(bucket, rt6_ex);
209062306a36Sopenharmony_ci			return;
209162306a36Sopenharmony_ci		}
209262306a36Sopenharmony_ci	} else if (time_after(jiffies, rt->dst.expires)) {
209362306a36Sopenharmony_ci		RT6_TRACE("purging expired route %p\n", rt);
209462306a36Sopenharmony_ci		rt6_remove_exception(bucket, rt6_ex);
209562306a36Sopenharmony_ci		return;
209662306a36Sopenharmony_ci	}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	if (rt->rt6i_flags & RTF_GATEWAY) {
209962306a36Sopenharmony_ci		struct neighbour *neigh;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci		neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci		if (!(neigh && (neigh->flags & NTF_ROUTER))) {
210462306a36Sopenharmony_ci			RT6_TRACE("purging route %p via non-router but gateway\n",
210562306a36Sopenharmony_ci				  rt);
210662306a36Sopenharmony_ci			rt6_remove_exception(bucket, rt6_ex);
210762306a36Sopenharmony_ci			return;
210862306a36Sopenharmony_ci		}
210962306a36Sopenharmony_ci	}
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	gc_args->more++;
211262306a36Sopenharmony_ci}
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_cistatic void fib6_nh_age_exceptions(const struct fib6_nh *nh,
211562306a36Sopenharmony_ci				   struct fib6_gc_args *gc_args,
211662306a36Sopenharmony_ci				   unsigned long now)
211762306a36Sopenharmony_ci{
211862306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
211962306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
212062306a36Sopenharmony_ci	struct hlist_node *tmp;
212162306a36Sopenharmony_ci	int i;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
212462306a36Sopenharmony_ci		return;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	rcu_read_lock_bh();
212762306a36Sopenharmony_ci	spin_lock(&rt6_exception_lock);
212862306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
212962306a36Sopenharmony_ci	if (bucket) {
213062306a36Sopenharmony_ci		for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
213162306a36Sopenharmony_ci			hlist_for_each_entry_safe(rt6_ex, tmp,
213262306a36Sopenharmony_ci						  &bucket->chain, hlist) {
213362306a36Sopenharmony_ci				rt6_age_examine_exception(bucket, rt6_ex,
213462306a36Sopenharmony_ci							  gc_args, now);
213562306a36Sopenharmony_ci			}
213662306a36Sopenharmony_ci			bucket++;
213762306a36Sopenharmony_ci		}
213862306a36Sopenharmony_ci	}
213962306a36Sopenharmony_ci	spin_unlock(&rt6_exception_lock);
214062306a36Sopenharmony_ci	rcu_read_unlock_bh();
214162306a36Sopenharmony_ci}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_cistruct fib6_nh_age_excptn_arg {
214462306a36Sopenharmony_ci	struct fib6_gc_args	*gc_args;
214562306a36Sopenharmony_ci	unsigned long		now;
214662306a36Sopenharmony_ci};
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int rt6_nh_age_exceptions(struct fib6_nh *nh, void *_arg)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	struct fib6_nh_age_excptn_arg *arg = _arg;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	fib6_nh_age_exceptions(nh, arg->gc_args, arg->now);
215362306a36Sopenharmony_ci	return 0;
215462306a36Sopenharmony_ci}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_civoid rt6_age_exceptions(struct fib6_info *f6i,
215762306a36Sopenharmony_ci			struct fib6_gc_args *gc_args,
215862306a36Sopenharmony_ci			unsigned long now)
215962306a36Sopenharmony_ci{
216062306a36Sopenharmony_ci	if (f6i->nh) {
216162306a36Sopenharmony_ci		struct fib6_nh_age_excptn_arg arg = {
216262306a36Sopenharmony_ci			.gc_args = gc_args,
216362306a36Sopenharmony_ci			.now = now
216462306a36Sopenharmony_ci		};
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_age_exceptions,
216762306a36Sopenharmony_ci					 &arg);
216862306a36Sopenharmony_ci	} else {
216962306a36Sopenharmony_ci		fib6_nh_age_exceptions(f6i->fib6_nh, gc_args, now);
217062306a36Sopenharmony_ci	}
217162306a36Sopenharmony_ci}
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci/* must be called with rcu lock held */
217462306a36Sopenharmony_ciint fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
217562306a36Sopenharmony_ci		      struct flowi6 *fl6, struct fib6_result *res, int strict)
217662306a36Sopenharmony_ci{
217762306a36Sopenharmony_ci	struct fib6_node *fn, *saved_fn;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
218062306a36Sopenharmony_ci	saved_fn = fn;
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ciredo_rt6_select:
218362306a36Sopenharmony_ci	rt6_select(net, fn, oif, res, strict);
218462306a36Sopenharmony_ci	if (res->f6i == net->ipv6.fib6_null_entry) {
218562306a36Sopenharmony_ci		fn = fib6_backtrack(fn, &fl6->saddr);
218662306a36Sopenharmony_ci		if (fn)
218762306a36Sopenharmony_ci			goto redo_rt6_select;
218862306a36Sopenharmony_ci		else if (strict & RT6_LOOKUP_F_REACHABLE) {
218962306a36Sopenharmony_ci			/* also consider unreachable route */
219062306a36Sopenharmony_ci			strict &= ~RT6_LOOKUP_F_REACHABLE;
219162306a36Sopenharmony_ci			fn = saved_fn;
219262306a36Sopenharmony_ci			goto redo_rt6_select;
219362306a36Sopenharmony_ci		}
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	trace_fib6_table_lookup(net, res, table, fl6);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	return 0;
219962306a36Sopenharmony_ci}
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_cistruct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
220262306a36Sopenharmony_ci			       int oif, struct flowi6 *fl6,
220362306a36Sopenharmony_ci			       const struct sk_buff *skb, int flags)
220462306a36Sopenharmony_ci{
220562306a36Sopenharmony_ci	struct fib6_result res = {};
220662306a36Sopenharmony_ci	struct rt6_info *rt = NULL;
220762306a36Sopenharmony_ci	int strict = 0;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) &&
221062306a36Sopenharmony_ci		     !rcu_read_lock_held());
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	strict |= flags & RT6_LOOKUP_F_IFACE;
221362306a36Sopenharmony_ci	strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
221462306a36Sopenharmony_ci	if (net->ipv6.devconf_all->forwarding == 0)
221562306a36Sopenharmony_ci		strict |= RT6_LOOKUP_F_REACHABLE;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	rcu_read_lock();
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	fib6_table_lookup(net, table, oif, fl6, &res, strict);
222062306a36Sopenharmony_ci	if (res.f6i == net->ipv6.fib6_null_entry)
222162306a36Sopenharmony_ci		goto out;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	fib6_select_path(net, &res, fl6, oif, false, skb, strict);
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	/*Search through exception table */
222662306a36Sopenharmony_ci	rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
222762306a36Sopenharmony_ci	if (rt) {
222862306a36Sopenharmony_ci		goto out;
222962306a36Sopenharmony_ci	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
223062306a36Sopenharmony_ci			    !res.nh->fib_nh_gw_family)) {
223162306a36Sopenharmony_ci		/* Create a RTF_CACHE clone which will not be
223262306a36Sopenharmony_ci		 * owned by the fib6 tree.  It is for the special case where
223362306a36Sopenharmony_ci		 * the daddr in the skb during the neighbor look-up is different
223462306a36Sopenharmony_ci		 * from the fl6->daddr used to look-up route here.
223562306a36Sopenharmony_ci		 */
223662306a36Sopenharmony_ci		rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci		if (rt) {
223962306a36Sopenharmony_ci			/* 1 refcnt is taken during ip6_rt_cache_alloc().
224062306a36Sopenharmony_ci			 * As rt6_uncached_list_add() does not consume refcnt,
224162306a36Sopenharmony_ci			 * this refcnt is always returned to the caller even
224262306a36Sopenharmony_ci			 * if caller sets RT6_LOOKUP_F_DST_NOREF flag.
224362306a36Sopenharmony_ci			 */
224462306a36Sopenharmony_ci			rt6_uncached_list_add(rt);
224562306a36Sopenharmony_ci			rcu_read_unlock();
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci			return rt;
224862306a36Sopenharmony_ci		}
224962306a36Sopenharmony_ci	} else {
225062306a36Sopenharmony_ci		/* Get a percpu copy */
225162306a36Sopenharmony_ci		local_bh_disable();
225262306a36Sopenharmony_ci		rt = rt6_get_pcpu_route(&res);
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci		if (!rt)
225562306a36Sopenharmony_ci			rt = rt6_make_pcpu_route(net, &res);
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci		local_bh_enable();
225862306a36Sopenharmony_ci	}
225962306a36Sopenharmony_ciout:
226062306a36Sopenharmony_ci	if (!rt)
226162306a36Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
226262306a36Sopenharmony_ci	if (!(flags & RT6_LOOKUP_F_DST_NOREF))
226362306a36Sopenharmony_ci		ip6_hold_safe(net, &rt);
226462306a36Sopenharmony_ci	rcu_read_unlock();
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	return rt;
226762306a36Sopenharmony_ci}
226862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_pol_route);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_input(struct net *net,
227162306a36Sopenharmony_ci					    struct fib6_table *table,
227262306a36Sopenharmony_ci					    struct flowi6 *fl6,
227362306a36Sopenharmony_ci					    const struct sk_buff *skb,
227462306a36Sopenharmony_ci					    int flags)
227562306a36Sopenharmony_ci{
227662306a36Sopenharmony_ci	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
227762306a36Sopenharmony_ci}
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_cistruct dst_entry *ip6_route_input_lookup(struct net *net,
228062306a36Sopenharmony_ci					 struct net_device *dev,
228162306a36Sopenharmony_ci					 struct flowi6 *fl6,
228262306a36Sopenharmony_ci					 const struct sk_buff *skb,
228362306a36Sopenharmony_ci					 int flags)
228462306a36Sopenharmony_ci{
228562306a36Sopenharmony_ci	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
228662306a36Sopenharmony_ci		flags |= RT6_LOOKUP_F_IFACE;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
228962306a36Sopenharmony_ci}
229062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_input_lookup);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_cistatic void ip6_multipath_l3_keys(const struct sk_buff *skb,
229362306a36Sopenharmony_ci				  struct flow_keys *keys,
229462306a36Sopenharmony_ci				  struct flow_keys *flkeys)
229562306a36Sopenharmony_ci{
229662306a36Sopenharmony_ci	const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
229762306a36Sopenharmony_ci	const struct ipv6hdr *key_iph = outer_iph;
229862306a36Sopenharmony_ci	struct flow_keys *_flkeys = flkeys;
229962306a36Sopenharmony_ci	const struct ipv6hdr *inner_iph;
230062306a36Sopenharmony_ci	const struct icmp6hdr *icmph;
230162306a36Sopenharmony_ci	struct ipv6hdr _inner_iph;
230262306a36Sopenharmony_ci	struct icmp6hdr _icmph;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
230562306a36Sopenharmony_ci		goto out;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	icmph = skb_header_pointer(skb, skb_transport_offset(skb),
230862306a36Sopenharmony_ci				   sizeof(_icmph), &_icmph);
230962306a36Sopenharmony_ci	if (!icmph)
231062306a36Sopenharmony_ci		goto out;
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	if (!icmpv6_is_err(icmph->icmp6_type))
231362306a36Sopenharmony_ci		goto out;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	inner_iph = skb_header_pointer(skb,
231662306a36Sopenharmony_ci				       skb_transport_offset(skb) + sizeof(*icmph),
231762306a36Sopenharmony_ci				       sizeof(_inner_iph), &_inner_iph);
231862306a36Sopenharmony_ci	if (!inner_iph)
231962306a36Sopenharmony_ci		goto out;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	key_iph = inner_iph;
232262306a36Sopenharmony_ci	_flkeys = NULL;
232362306a36Sopenharmony_ciout:
232462306a36Sopenharmony_ci	if (_flkeys) {
232562306a36Sopenharmony_ci		keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
232662306a36Sopenharmony_ci		keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
232762306a36Sopenharmony_ci		keys->tags.flow_label = _flkeys->tags.flow_label;
232862306a36Sopenharmony_ci		keys->basic.ip_proto = _flkeys->basic.ip_proto;
232962306a36Sopenharmony_ci	} else {
233062306a36Sopenharmony_ci		keys->addrs.v6addrs.src = key_iph->saddr;
233162306a36Sopenharmony_ci		keys->addrs.v6addrs.dst = key_iph->daddr;
233262306a36Sopenharmony_ci		keys->tags.flow_label = ip6_flowlabel(key_iph);
233362306a36Sopenharmony_ci		keys->basic.ip_proto = key_iph->nexthdr;
233462306a36Sopenharmony_ci	}
233562306a36Sopenharmony_ci}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_outer(const struct net *net,
233862306a36Sopenharmony_ci					   const struct sk_buff *skb,
233962306a36Sopenharmony_ci					   bool *p_has_inner)
234062306a36Sopenharmony_ci{
234162306a36Sopenharmony_ci	u32 hash_fields = ip6_multipath_hash_fields(net);
234262306a36Sopenharmony_ci	struct flow_keys keys, hash_keys;
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK))
234562306a36Sopenharmony_ci		return 0;
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	memset(&hash_keys, 0, sizeof(hash_keys));
234862306a36Sopenharmony_ci	skb_flow_dissect_flow_keys(skb, &keys, FLOW_DISSECTOR_F_STOP_AT_ENCAP);
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
235162306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP)
235262306a36Sopenharmony_ci		hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src;
235362306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP)
235462306a36Sopenharmony_ci		hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst;
235562306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)
235662306a36Sopenharmony_ci		hash_keys.basic.ip_proto = keys.basic.ip_proto;
235762306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL)
235862306a36Sopenharmony_ci		hash_keys.tags.flow_label = keys.tags.flow_label;
235962306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)
236062306a36Sopenharmony_ci		hash_keys.ports.src = keys.ports.src;
236162306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
236262306a36Sopenharmony_ci		hash_keys.ports.dst = keys.ports.dst;
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	*p_has_inner = !!(keys.control.flags & FLOW_DIS_ENCAPSULATION);
236562306a36Sopenharmony_ci	return flow_hash_from_keys(&hash_keys);
236662306a36Sopenharmony_ci}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_inner(const struct net *net,
236962306a36Sopenharmony_ci					   const struct sk_buff *skb,
237062306a36Sopenharmony_ci					   bool has_inner)
237162306a36Sopenharmony_ci{
237262306a36Sopenharmony_ci	u32 hash_fields = ip6_multipath_hash_fields(net);
237362306a36Sopenharmony_ci	struct flow_keys keys, hash_keys;
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	/* We assume the packet carries an encapsulation, but if none was
237662306a36Sopenharmony_ci	 * encountered during dissection of the outer flow, then there is no
237762306a36Sopenharmony_ci	 * point in calling the flow dissector again.
237862306a36Sopenharmony_ci	 */
237962306a36Sopenharmony_ci	if (!has_inner)
238062306a36Sopenharmony_ci		return 0;
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_MASK))
238362306a36Sopenharmony_ci		return 0;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	memset(&hash_keys, 0, sizeof(hash_keys));
238662306a36Sopenharmony_ci	skb_flow_dissect_flow_keys(skb, &keys, 0);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	if (!(keys.control.flags & FLOW_DIS_ENCAPSULATION))
238962306a36Sopenharmony_ci		return 0;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
239262306a36Sopenharmony_ci		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
239362306a36Sopenharmony_ci		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP)
239462306a36Sopenharmony_ci			hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
239562306a36Sopenharmony_ci		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP)
239662306a36Sopenharmony_ci			hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
239762306a36Sopenharmony_ci	} else if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
239862306a36Sopenharmony_ci		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
239962306a36Sopenharmony_ci		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP)
240062306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src;
240162306a36Sopenharmony_ci		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP)
240262306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst;
240362306a36Sopenharmony_ci		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_FLOWLABEL)
240462306a36Sopenharmony_ci			hash_keys.tags.flow_label = keys.tags.flow_label;
240562306a36Sopenharmony_ci	}
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_IP_PROTO)
240862306a36Sopenharmony_ci		hash_keys.basic.ip_proto = keys.basic.ip_proto;
240962306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_PORT)
241062306a36Sopenharmony_ci		hash_keys.ports.src = keys.ports.src;
241162306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_PORT)
241262306a36Sopenharmony_ci		hash_keys.ports.dst = keys.ports.dst;
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	return flow_hash_from_keys(&hash_keys);
241562306a36Sopenharmony_ci}
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_skb(const struct net *net,
241862306a36Sopenharmony_ci					 const struct sk_buff *skb)
241962306a36Sopenharmony_ci{
242062306a36Sopenharmony_ci	u32 mhash, mhash_inner;
242162306a36Sopenharmony_ci	bool has_inner = true;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	mhash = rt6_multipath_custom_hash_outer(net, skb, &has_inner);
242462306a36Sopenharmony_ci	mhash_inner = rt6_multipath_custom_hash_inner(net, skb, has_inner);
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	return jhash_2words(mhash, mhash_inner, 0);
242762306a36Sopenharmony_ci}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_fl6(const struct net *net,
243062306a36Sopenharmony_ci					 const struct flowi6 *fl6)
243162306a36Sopenharmony_ci{
243262306a36Sopenharmony_ci	u32 hash_fields = ip6_multipath_hash_fields(net);
243362306a36Sopenharmony_ci	struct flow_keys hash_keys;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK))
243662306a36Sopenharmony_ci		return 0;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	memset(&hash_keys, 0, sizeof(hash_keys));
243962306a36Sopenharmony_ci	hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
244062306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP)
244162306a36Sopenharmony_ci		hash_keys.addrs.v6addrs.src = fl6->saddr;
244262306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP)
244362306a36Sopenharmony_ci		hash_keys.addrs.v6addrs.dst = fl6->daddr;
244462306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)
244562306a36Sopenharmony_ci		hash_keys.basic.ip_proto = fl6->flowi6_proto;
244662306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL)
244762306a36Sopenharmony_ci		hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
244862306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)
244962306a36Sopenharmony_ci		hash_keys.ports.src = fl6->fl6_sport;
245062306a36Sopenharmony_ci	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
245162306a36Sopenharmony_ci		hash_keys.ports.dst = fl6->fl6_dport;
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	return flow_hash_from_keys(&hash_keys);
245462306a36Sopenharmony_ci}
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci/* if skb is set it will be used and fl6 can be NULL */
245762306a36Sopenharmony_ciu32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
245862306a36Sopenharmony_ci		       const struct sk_buff *skb, struct flow_keys *flkeys)
245962306a36Sopenharmony_ci{
246062306a36Sopenharmony_ci	struct flow_keys hash_keys;
246162306a36Sopenharmony_ci	u32 mhash = 0;
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	switch (ip6_multipath_hash_policy(net)) {
246462306a36Sopenharmony_ci	case 0:
246562306a36Sopenharmony_ci		memset(&hash_keys, 0, sizeof(hash_keys));
246662306a36Sopenharmony_ci		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
246762306a36Sopenharmony_ci		if (skb) {
246862306a36Sopenharmony_ci			ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
246962306a36Sopenharmony_ci		} else {
247062306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.src = fl6->saddr;
247162306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = fl6->daddr;
247262306a36Sopenharmony_ci			hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
247362306a36Sopenharmony_ci			hash_keys.basic.ip_proto = fl6->flowi6_proto;
247462306a36Sopenharmony_ci		}
247562306a36Sopenharmony_ci		mhash = flow_hash_from_keys(&hash_keys);
247662306a36Sopenharmony_ci		break;
247762306a36Sopenharmony_ci	case 1:
247862306a36Sopenharmony_ci		if (skb) {
247962306a36Sopenharmony_ci			unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
248062306a36Sopenharmony_ci			struct flow_keys keys;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci			/* short-circuit if we already have L4 hash present */
248362306a36Sopenharmony_ci			if (skb->l4_hash)
248462306a36Sopenharmony_ci				return skb_get_hash_raw(skb) >> 1;
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci			memset(&hash_keys, 0, sizeof(hash_keys));
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci			if (!flkeys) {
248962306a36Sopenharmony_ci				skb_flow_dissect_flow_keys(skb, &keys, flag);
249062306a36Sopenharmony_ci				flkeys = &keys;
249162306a36Sopenharmony_ci			}
249262306a36Sopenharmony_ci			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
249362306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
249462306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
249562306a36Sopenharmony_ci			hash_keys.ports.src = flkeys->ports.src;
249662306a36Sopenharmony_ci			hash_keys.ports.dst = flkeys->ports.dst;
249762306a36Sopenharmony_ci			hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
249862306a36Sopenharmony_ci		} else {
249962306a36Sopenharmony_ci			memset(&hash_keys, 0, sizeof(hash_keys));
250062306a36Sopenharmony_ci			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
250162306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.src = fl6->saddr;
250262306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = fl6->daddr;
250362306a36Sopenharmony_ci			hash_keys.ports.src = fl6->fl6_sport;
250462306a36Sopenharmony_ci			hash_keys.ports.dst = fl6->fl6_dport;
250562306a36Sopenharmony_ci			hash_keys.basic.ip_proto = fl6->flowi6_proto;
250662306a36Sopenharmony_ci		}
250762306a36Sopenharmony_ci		mhash = flow_hash_from_keys(&hash_keys);
250862306a36Sopenharmony_ci		break;
250962306a36Sopenharmony_ci	case 2:
251062306a36Sopenharmony_ci		memset(&hash_keys, 0, sizeof(hash_keys));
251162306a36Sopenharmony_ci		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
251262306a36Sopenharmony_ci		if (skb) {
251362306a36Sopenharmony_ci			struct flow_keys keys;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci			if (!flkeys) {
251662306a36Sopenharmony_ci				skb_flow_dissect_flow_keys(skb, &keys, 0);
251762306a36Sopenharmony_ci				flkeys = &keys;
251862306a36Sopenharmony_ci			}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci			/* Inner can be v4 or v6 */
252162306a36Sopenharmony_ci			if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
252262306a36Sopenharmony_ci				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
252362306a36Sopenharmony_ci				hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;
252462306a36Sopenharmony_ci				hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;
252562306a36Sopenharmony_ci			} else if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
252662306a36Sopenharmony_ci				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
252762306a36Sopenharmony_ci				hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
252862306a36Sopenharmony_ci				hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
252962306a36Sopenharmony_ci				hash_keys.tags.flow_label = flkeys->tags.flow_label;
253062306a36Sopenharmony_ci				hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
253162306a36Sopenharmony_ci			} else {
253262306a36Sopenharmony_ci				/* Same as case 0 */
253362306a36Sopenharmony_ci				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
253462306a36Sopenharmony_ci				ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
253562306a36Sopenharmony_ci			}
253662306a36Sopenharmony_ci		} else {
253762306a36Sopenharmony_ci			/* Same as case 0 */
253862306a36Sopenharmony_ci			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
253962306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.src = fl6->saddr;
254062306a36Sopenharmony_ci			hash_keys.addrs.v6addrs.dst = fl6->daddr;
254162306a36Sopenharmony_ci			hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
254262306a36Sopenharmony_ci			hash_keys.basic.ip_proto = fl6->flowi6_proto;
254362306a36Sopenharmony_ci		}
254462306a36Sopenharmony_ci		mhash = flow_hash_from_keys(&hash_keys);
254562306a36Sopenharmony_ci		break;
254662306a36Sopenharmony_ci	case 3:
254762306a36Sopenharmony_ci		if (skb)
254862306a36Sopenharmony_ci			mhash = rt6_multipath_custom_hash_skb(net, skb);
254962306a36Sopenharmony_ci		else
255062306a36Sopenharmony_ci			mhash = rt6_multipath_custom_hash_fl6(net, fl6);
255162306a36Sopenharmony_ci		break;
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	return mhash >> 1;
255562306a36Sopenharmony_ci}
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci/* Called with rcu held */
255862306a36Sopenharmony_civoid ip6_route_input(struct sk_buff *skb)
255962306a36Sopenharmony_ci{
256062306a36Sopenharmony_ci	const struct ipv6hdr *iph = ipv6_hdr(skb);
256162306a36Sopenharmony_ci	struct net *net = dev_net(skb->dev);
256262306a36Sopenharmony_ci	int flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_DST_NOREF;
256362306a36Sopenharmony_ci	struct ip_tunnel_info *tun_info;
256462306a36Sopenharmony_ci	struct flowi6 fl6 = {
256562306a36Sopenharmony_ci		.flowi6_iif = skb->dev->ifindex,
256662306a36Sopenharmony_ci		.daddr = iph->daddr,
256762306a36Sopenharmony_ci		.saddr = iph->saddr,
256862306a36Sopenharmony_ci		.flowlabel = ip6_flowinfo(iph),
256962306a36Sopenharmony_ci		.flowi6_mark = skb->mark,
257062306a36Sopenharmony_ci		.flowi6_proto = iph->nexthdr,
257162306a36Sopenharmony_ci	};
257262306a36Sopenharmony_ci	struct flow_keys *flkeys = NULL, _flkeys;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	tun_info = skb_tunnel_info(skb);
257562306a36Sopenharmony_ci	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
257662306a36Sopenharmony_ci		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
257962306a36Sopenharmony_ci		flkeys = &_flkeys;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
258262306a36Sopenharmony_ci		fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
258362306a36Sopenharmony_ci	skb_dst_drop(skb);
258462306a36Sopenharmony_ci	skb_dst_set_noref(skb, ip6_route_input_lookup(net, skb->dev,
258562306a36Sopenharmony_ci						      &fl6, skb, flags));
258662306a36Sopenharmony_ci}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_output(struct net *net,
258962306a36Sopenharmony_ci					     struct fib6_table *table,
259062306a36Sopenharmony_ci					     struct flowi6 *fl6,
259162306a36Sopenharmony_ci					     const struct sk_buff *skb,
259262306a36Sopenharmony_ci					     int flags)
259362306a36Sopenharmony_ci{
259462306a36Sopenharmony_ci	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
259562306a36Sopenharmony_ci}
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_cistatic struct dst_entry *ip6_route_output_flags_noref(struct net *net,
259862306a36Sopenharmony_ci						      const struct sock *sk,
259962306a36Sopenharmony_ci						      struct flowi6 *fl6,
260062306a36Sopenharmony_ci						      int flags)
260162306a36Sopenharmony_ci{
260262306a36Sopenharmony_ci	bool any_src;
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_ci	if (ipv6_addr_type(&fl6->daddr) &
260562306a36Sopenharmony_ci	    (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
260662306a36Sopenharmony_ci		struct dst_entry *dst;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci		/* This function does not take refcnt on the dst */
260962306a36Sopenharmony_ci		dst = l3mdev_link_scope_lookup(net, fl6);
261062306a36Sopenharmony_ci		if (dst)
261162306a36Sopenharmony_ci			return dst;
261262306a36Sopenharmony_ci	}
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci	fl6->flowi6_iif = LOOPBACK_IFINDEX;
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	flags |= RT6_LOOKUP_F_DST_NOREF;
261762306a36Sopenharmony_ci	any_src = ipv6_addr_any(&fl6->saddr);
261862306a36Sopenharmony_ci	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
261962306a36Sopenharmony_ci	    (fl6->flowi6_oif && any_src))
262062306a36Sopenharmony_ci		flags |= RT6_LOOKUP_F_IFACE;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	if (!any_src)
262362306a36Sopenharmony_ci		flags |= RT6_LOOKUP_F_HAS_SADDR;
262462306a36Sopenharmony_ci	else if (sk)
262562306a36Sopenharmony_ci		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
262862306a36Sopenharmony_ci}
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_cistruct dst_entry *ip6_route_output_flags(struct net *net,
263162306a36Sopenharmony_ci					 const struct sock *sk,
263262306a36Sopenharmony_ci					 struct flowi6 *fl6,
263362306a36Sopenharmony_ci					 int flags)
263462306a36Sopenharmony_ci{
263562306a36Sopenharmony_ci	struct dst_entry *dst;
263662306a36Sopenharmony_ci	struct rt6_info *rt6;
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	rcu_read_lock();
263962306a36Sopenharmony_ci	dst = ip6_route_output_flags_noref(net, sk, fl6, flags);
264062306a36Sopenharmony_ci	rt6 = (struct rt6_info *)dst;
264162306a36Sopenharmony_ci	/* For dst cached in uncached_list, refcnt is already taken. */
264262306a36Sopenharmony_ci	if (list_empty(&rt6->dst.rt_uncached) && !dst_hold_safe(dst)) {
264362306a36Sopenharmony_ci		dst = &net->ipv6.ip6_null_entry->dst;
264462306a36Sopenharmony_ci		dst_hold(dst);
264562306a36Sopenharmony_ci	}
264662306a36Sopenharmony_ci	rcu_read_unlock();
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	return dst;
264962306a36Sopenharmony_ci}
265062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_output_flags);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_cistruct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
265362306a36Sopenharmony_ci{
265462306a36Sopenharmony_ci	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
265562306a36Sopenharmony_ci	struct net_device *loopback_dev = net->loopback_dev;
265662306a36Sopenharmony_ci	struct dst_entry *new = NULL;
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
265962306a36Sopenharmony_ci		       DST_OBSOLETE_DEAD, 0);
266062306a36Sopenharmony_ci	if (rt) {
266162306a36Sopenharmony_ci		rt6_info_init(rt);
266262306a36Sopenharmony_ci		atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci		new = &rt->dst;
266562306a36Sopenharmony_ci		new->__use = 1;
266662306a36Sopenharmony_ci		new->input = dst_discard;
266762306a36Sopenharmony_ci		new->output = dst_discard_out;
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci		dst_copy_metrics(new, &ort->dst);
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci		rt->rt6i_idev = in6_dev_get(loopback_dev);
267262306a36Sopenharmony_ci		rt->rt6i_gateway = ort->rt6i_gateway;
267362306a36Sopenharmony_ci		rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
267662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
267762306a36Sopenharmony_ci		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
267862306a36Sopenharmony_ci#endif
267962306a36Sopenharmony_ci	}
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	dst_release(dst_orig);
268262306a36Sopenharmony_ci	return new ? new : ERR_PTR(-ENOMEM);
268362306a36Sopenharmony_ci}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci/*
268662306a36Sopenharmony_ci *	Destination cache support functions
268762306a36Sopenharmony_ci */
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_cistatic bool fib6_check(struct fib6_info *f6i, u32 cookie)
269062306a36Sopenharmony_ci{
269162306a36Sopenharmony_ci	u32 rt_cookie = 0;
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
269462306a36Sopenharmony_ci		return false;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	if (fib6_check_expired(f6i))
269762306a36Sopenharmony_ci		return false;
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	return true;
270062306a36Sopenharmony_ci}
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_cistatic struct dst_entry *rt6_check(struct rt6_info *rt,
270362306a36Sopenharmony_ci				   struct fib6_info *from,
270462306a36Sopenharmony_ci				   u32 cookie)
270562306a36Sopenharmony_ci{
270662306a36Sopenharmony_ci	u32 rt_cookie = 0;
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci	if (!from || !fib6_get_cookie_safe(from, &rt_cookie) ||
270962306a36Sopenharmony_ci	    rt_cookie != cookie)
271062306a36Sopenharmony_ci		return NULL;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	if (rt6_check_expired(rt))
271362306a36Sopenharmony_ci		return NULL;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	return &rt->dst;
271662306a36Sopenharmony_ci}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_cistatic struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
271962306a36Sopenharmony_ci					    struct fib6_info *from,
272062306a36Sopenharmony_ci					    u32 cookie)
272162306a36Sopenharmony_ci{
272262306a36Sopenharmony_ci	if (!__rt6_check_expired(rt) &&
272362306a36Sopenharmony_ci	    rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
272462306a36Sopenharmony_ci	    fib6_check(from, cookie))
272562306a36Sopenharmony_ci		return &rt->dst;
272662306a36Sopenharmony_ci	else
272762306a36Sopenharmony_ci		return NULL;
272862306a36Sopenharmony_ci}
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst,
273162306a36Sopenharmony_ci							u32 cookie)
273262306a36Sopenharmony_ci{
273362306a36Sopenharmony_ci	struct dst_entry *dst_ret;
273462306a36Sopenharmony_ci	struct fib6_info *from;
273562306a36Sopenharmony_ci	struct rt6_info *rt;
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	rt = container_of(dst, struct rt6_info, dst);
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci	if (rt->sernum)
274062306a36Sopenharmony_ci		return rt6_is_valid(rt) ? dst : NULL;
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	rcu_read_lock();
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	/* All IPV6 dsts are created with ->obsolete set to the value
274562306a36Sopenharmony_ci	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
274662306a36Sopenharmony_ci	 * into this function always.
274762306a36Sopenharmony_ci	 */
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	from = rcu_dereference(rt->from);
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	if (from && (rt->rt6i_flags & RTF_PCPU ||
275262306a36Sopenharmony_ci	    unlikely(!list_empty(&rt->dst.rt_uncached))))
275362306a36Sopenharmony_ci		dst_ret = rt6_dst_from_check(rt, from, cookie);
275462306a36Sopenharmony_ci	else
275562306a36Sopenharmony_ci		dst_ret = rt6_check(rt, from, cookie);
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci	rcu_read_unlock();
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	return dst_ret;
276062306a36Sopenharmony_ci}
276162306a36Sopenharmony_ciEXPORT_INDIRECT_CALLABLE(ip6_dst_check);
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_cistatic struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
276462306a36Sopenharmony_ci{
276562306a36Sopenharmony_ci	struct rt6_info *rt = (struct rt6_info *) dst;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	if (rt) {
276862306a36Sopenharmony_ci		if (rt->rt6i_flags & RTF_CACHE) {
276962306a36Sopenharmony_ci			rcu_read_lock();
277062306a36Sopenharmony_ci			if (rt6_check_expired(rt)) {
277162306a36Sopenharmony_ci				rt6_remove_exception_rt(rt);
277262306a36Sopenharmony_ci				dst = NULL;
277362306a36Sopenharmony_ci			}
277462306a36Sopenharmony_ci			rcu_read_unlock();
277562306a36Sopenharmony_ci		} else {
277662306a36Sopenharmony_ci			dst_release(dst);
277762306a36Sopenharmony_ci			dst = NULL;
277862306a36Sopenharmony_ci		}
277962306a36Sopenharmony_ci	}
278062306a36Sopenharmony_ci	return dst;
278162306a36Sopenharmony_ci}
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_cistatic void ip6_link_failure(struct sk_buff *skb)
278462306a36Sopenharmony_ci{
278562306a36Sopenharmony_ci	struct rt6_info *rt;
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	rt = (struct rt6_info *) skb_dst(skb);
279062306a36Sopenharmony_ci	if (rt) {
279162306a36Sopenharmony_ci		rcu_read_lock();
279262306a36Sopenharmony_ci		if (rt->rt6i_flags & RTF_CACHE) {
279362306a36Sopenharmony_ci			rt6_remove_exception_rt(rt);
279462306a36Sopenharmony_ci		} else {
279562306a36Sopenharmony_ci			struct fib6_info *from;
279662306a36Sopenharmony_ci			struct fib6_node *fn;
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci			from = rcu_dereference(rt->from);
279962306a36Sopenharmony_ci			if (from) {
280062306a36Sopenharmony_ci				fn = rcu_dereference(from->fib6_node);
280162306a36Sopenharmony_ci				if (fn && (rt->rt6i_flags & RTF_DEFAULT))
280262306a36Sopenharmony_ci					WRITE_ONCE(fn->fn_sernum, -1);
280362306a36Sopenharmony_ci			}
280462306a36Sopenharmony_ci		}
280562306a36Sopenharmony_ci		rcu_read_unlock();
280662306a36Sopenharmony_ci	}
280762306a36Sopenharmony_ci}
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_cistatic void rt6_update_expires(struct rt6_info *rt0, int timeout)
281062306a36Sopenharmony_ci{
281162306a36Sopenharmony_ci	if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
281262306a36Sopenharmony_ci		struct fib6_info *from;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci		rcu_read_lock();
281562306a36Sopenharmony_ci		from = rcu_dereference(rt0->from);
281662306a36Sopenharmony_ci		if (from)
281762306a36Sopenharmony_ci			rt0->dst.expires = from->expires;
281862306a36Sopenharmony_ci		rcu_read_unlock();
281962306a36Sopenharmony_ci	}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	dst_set_expires(&rt0->dst, timeout);
282262306a36Sopenharmony_ci	rt0->rt6i_flags |= RTF_EXPIRES;
282362306a36Sopenharmony_ci}
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_cistatic void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
282662306a36Sopenharmony_ci{
282762306a36Sopenharmony_ci	struct net *net = dev_net(rt->dst.dev);
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	dst_metric_set(&rt->dst, RTAX_MTU, mtu);
283062306a36Sopenharmony_ci	rt->rt6i_flags |= RTF_MODIFIED;
283162306a36Sopenharmony_ci	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
283262306a36Sopenharmony_ci}
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_cistatic bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
283562306a36Sopenharmony_ci{
283662306a36Sopenharmony_ci	return !(rt->rt6i_flags & RTF_CACHE) &&
283762306a36Sopenharmony_ci		(rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
283862306a36Sopenharmony_ci}
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_cistatic void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
284162306a36Sopenharmony_ci				 const struct ipv6hdr *iph, u32 mtu,
284262306a36Sopenharmony_ci				 bool confirm_neigh)
284362306a36Sopenharmony_ci{
284462306a36Sopenharmony_ci	const struct in6_addr *daddr, *saddr;
284562306a36Sopenharmony_ci	struct rt6_info *rt6 = (struct rt6_info *)dst;
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	/* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU)
284862306a36Sopenharmony_ci	 * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it.
284962306a36Sopenharmony_ci	 * [see also comment in rt6_mtu_change_route()]
285062306a36Sopenharmony_ci	 */
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci	if (iph) {
285362306a36Sopenharmony_ci		daddr = &iph->daddr;
285462306a36Sopenharmony_ci		saddr = &iph->saddr;
285562306a36Sopenharmony_ci	} else if (sk) {
285662306a36Sopenharmony_ci		daddr = &sk->sk_v6_daddr;
285762306a36Sopenharmony_ci		saddr = &inet6_sk(sk)->saddr;
285862306a36Sopenharmony_ci	} else {
285962306a36Sopenharmony_ci		daddr = NULL;
286062306a36Sopenharmony_ci		saddr = NULL;
286162306a36Sopenharmony_ci	}
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	if (confirm_neigh)
286462306a36Sopenharmony_ci		dst_confirm_neigh(dst, daddr);
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	if (mtu < IPV6_MIN_MTU)
286762306a36Sopenharmony_ci		return;
286862306a36Sopenharmony_ci	if (mtu >= dst_mtu(dst))
286962306a36Sopenharmony_ci		return;
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci	if (!rt6_cache_allowed_for_pmtu(rt6)) {
287262306a36Sopenharmony_ci		rt6_do_update_pmtu(rt6, mtu);
287362306a36Sopenharmony_ci		/* update rt6_ex->stamp for cache */
287462306a36Sopenharmony_ci		if (rt6->rt6i_flags & RTF_CACHE)
287562306a36Sopenharmony_ci			rt6_update_exception_stamp_rt(rt6);
287662306a36Sopenharmony_ci	} else if (daddr) {
287762306a36Sopenharmony_ci		struct fib6_result res = {};
287862306a36Sopenharmony_ci		struct rt6_info *nrt6;
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci		rcu_read_lock();
288162306a36Sopenharmony_ci		res.f6i = rcu_dereference(rt6->from);
288262306a36Sopenharmony_ci		if (!res.f6i)
288362306a36Sopenharmony_ci			goto out_unlock;
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci		res.fib6_flags = res.f6i->fib6_flags;
288662306a36Sopenharmony_ci		res.fib6_type = res.f6i->fib6_type;
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci		if (res.f6i->nh) {
288962306a36Sopenharmony_ci			struct fib6_nh_match_arg arg = {
289062306a36Sopenharmony_ci				.dev = dst->dev,
289162306a36Sopenharmony_ci				.gw = &rt6->rt6i_gateway,
289262306a36Sopenharmony_ci			};
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci			nexthop_for_each_fib6_nh(res.f6i->nh,
289562306a36Sopenharmony_ci						 fib6_nh_find_match, &arg);
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci			/* fib6_info uses a nexthop that does not have fib6_nh
289862306a36Sopenharmony_ci			 * using the dst->dev + gw. Should be impossible.
289962306a36Sopenharmony_ci			 */
290062306a36Sopenharmony_ci			if (!arg.match)
290162306a36Sopenharmony_ci				goto out_unlock;
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci			res.nh = arg.match;
290462306a36Sopenharmony_ci		} else {
290562306a36Sopenharmony_ci			res.nh = res.f6i->fib6_nh;
290662306a36Sopenharmony_ci		}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci		nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
290962306a36Sopenharmony_ci		if (nrt6) {
291062306a36Sopenharmony_ci			rt6_do_update_pmtu(nrt6, mtu);
291162306a36Sopenharmony_ci			if (rt6_insert_exception(nrt6, &res))
291262306a36Sopenharmony_ci				dst_release_immediate(&nrt6->dst);
291362306a36Sopenharmony_ci		}
291462306a36Sopenharmony_ciout_unlock:
291562306a36Sopenharmony_ci		rcu_read_unlock();
291662306a36Sopenharmony_ci	}
291762306a36Sopenharmony_ci}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_cistatic void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
292062306a36Sopenharmony_ci			       struct sk_buff *skb, u32 mtu,
292162306a36Sopenharmony_ci			       bool confirm_neigh)
292262306a36Sopenharmony_ci{
292362306a36Sopenharmony_ci	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu,
292462306a36Sopenharmony_ci			     confirm_neigh);
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_civoid ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
292862306a36Sopenharmony_ci		     int oif, u32 mark, kuid_t uid)
292962306a36Sopenharmony_ci{
293062306a36Sopenharmony_ci	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
293162306a36Sopenharmony_ci	struct dst_entry *dst;
293262306a36Sopenharmony_ci	struct flowi6 fl6 = {
293362306a36Sopenharmony_ci		.flowi6_oif = oif,
293462306a36Sopenharmony_ci		.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
293562306a36Sopenharmony_ci		.daddr = iph->daddr,
293662306a36Sopenharmony_ci		.saddr = iph->saddr,
293762306a36Sopenharmony_ci		.flowlabel = ip6_flowinfo(iph),
293862306a36Sopenharmony_ci		.flowi6_uid = uid,
293962306a36Sopenharmony_ci	};
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	dst = ip6_route_output(net, NULL, &fl6);
294262306a36Sopenharmony_ci	if (!dst->error)
294362306a36Sopenharmony_ci		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true);
294462306a36Sopenharmony_ci	dst_release(dst);
294562306a36Sopenharmony_ci}
294662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_update_pmtu);
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_civoid ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
294962306a36Sopenharmony_ci{
295062306a36Sopenharmony_ci	int oif = sk->sk_bound_dev_if;
295162306a36Sopenharmony_ci	struct dst_entry *dst;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	if (!oif && skb->dev)
295462306a36Sopenharmony_ci		oif = l3mdev_master_ifindex(skb->dev);
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	ip6_update_pmtu(skb, sock_net(sk), mtu, oif, READ_ONCE(sk->sk_mark),
295762306a36Sopenharmony_ci			sk->sk_uid);
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci	dst = __sk_dst_get(sk);
296062306a36Sopenharmony_ci	if (!dst || !dst->obsolete ||
296162306a36Sopenharmony_ci	    dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
296262306a36Sopenharmony_ci		return;
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	bh_lock_sock(sk);
296562306a36Sopenharmony_ci	if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
296662306a36Sopenharmony_ci		ip6_datagram_dst_update(sk, false);
296762306a36Sopenharmony_ci	bh_unlock_sock(sk);
296862306a36Sopenharmony_ci}
296962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_civoid ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
297262306a36Sopenharmony_ci			   const struct flowi6 *fl6)
297362306a36Sopenharmony_ci{
297462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
297562306a36Sopenharmony_ci	struct ipv6_pinfo *np = inet6_sk(sk);
297662306a36Sopenharmony_ci#endif
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci	ip6_dst_store(sk, dst,
297962306a36Sopenharmony_ci		      ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
298062306a36Sopenharmony_ci		      &sk->sk_v6_daddr : NULL,
298162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
298262306a36Sopenharmony_ci		      ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
298362306a36Sopenharmony_ci		      &np->saddr :
298462306a36Sopenharmony_ci#endif
298562306a36Sopenharmony_ci		      NULL);
298662306a36Sopenharmony_ci}
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_cistatic bool ip6_redirect_nh_match(const struct fib6_result *res,
298962306a36Sopenharmony_ci				  struct flowi6 *fl6,
299062306a36Sopenharmony_ci				  const struct in6_addr *gw,
299162306a36Sopenharmony_ci				  struct rt6_info **ret)
299262306a36Sopenharmony_ci{
299362306a36Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
299662306a36Sopenharmony_ci	    fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
299762306a36Sopenharmony_ci		return false;
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	/* rt_cache's gateway might be different from its 'parent'
300062306a36Sopenharmony_ci	 * in the case of an ip redirect.
300162306a36Sopenharmony_ci	 * So we keep searching in the exception table if the gateway
300262306a36Sopenharmony_ci	 * is different.
300362306a36Sopenharmony_ci	 */
300462306a36Sopenharmony_ci	if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
300562306a36Sopenharmony_ci		struct rt6_info *rt_cache;
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci		rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
300862306a36Sopenharmony_ci		if (rt_cache &&
300962306a36Sopenharmony_ci		    ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
301062306a36Sopenharmony_ci			*ret = rt_cache;
301162306a36Sopenharmony_ci			return true;
301262306a36Sopenharmony_ci		}
301362306a36Sopenharmony_ci		return false;
301462306a36Sopenharmony_ci	}
301562306a36Sopenharmony_ci	return true;
301662306a36Sopenharmony_ci}
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_cistruct fib6_nh_rd_arg {
301962306a36Sopenharmony_ci	struct fib6_result	*res;
302062306a36Sopenharmony_ci	struct flowi6		*fl6;
302162306a36Sopenharmony_ci	const struct in6_addr	*gw;
302262306a36Sopenharmony_ci	struct rt6_info		**ret;
302362306a36Sopenharmony_ci};
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_cistatic int fib6_nh_redirect_match(struct fib6_nh *nh, void *_arg)
302662306a36Sopenharmony_ci{
302762306a36Sopenharmony_ci	struct fib6_nh_rd_arg *arg = _arg;
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci	arg->res->nh = nh;
303062306a36Sopenharmony_ci	return ip6_redirect_nh_match(arg->res, arg->fl6, arg->gw, arg->ret);
303162306a36Sopenharmony_ci}
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci/* Handle redirects */
303462306a36Sopenharmony_cistruct ip6rd_flowi {
303562306a36Sopenharmony_ci	struct flowi6 fl6;
303662306a36Sopenharmony_ci	struct in6_addr gateway;
303762306a36Sopenharmony_ci};
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net,
304062306a36Sopenharmony_ci					     struct fib6_table *table,
304162306a36Sopenharmony_ci					     struct flowi6 *fl6,
304262306a36Sopenharmony_ci					     const struct sk_buff *skb,
304362306a36Sopenharmony_ci					     int flags)
304462306a36Sopenharmony_ci{
304562306a36Sopenharmony_ci	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
304662306a36Sopenharmony_ci	struct rt6_info *ret = NULL;
304762306a36Sopenharmony_ci	struct fib6_result res = {};
304862306a36Sopenharmony_ci	struct fib6_nh_rd_arg arg = {
304962306a36Sopenharmony_ci		.res = &res,
305062306a36Sopenharmony_ci		.fl6 = fl6,
305162306a36Sopenharmony_ci		.gw  = &rdfl->gateway,
305262306a36Sopenharmony_ci		.ret = &ret
305362306a36Sopenharmony_ci	};
305462306a36Sopenharmony_ci	struct fib6_info *rt;
305562306a36Sopenharmony_ci	struct fib6_node *fn;
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci	/* Get the "current" route for this destination and
305862306a36Sopenharmony_ci	 * check if the redirect has come from appropriate router.
305962306a36Sopenharmony_ci	 *
306062306a36Sopenharmony_ci	 * RFC 4861 specifies that redirects should only be
306162306a36Sopenharmony_ci	 * accepted if they come from the nexthop to the target.
306262306a36Sopenharmony_ci	 * Due to the way the routes are chosen, this notion
306362306a36Sopenharmony_ci	 * is a bit fuzzy and one might need to check all possible
306462306a36Sopenharmony_ci	 * routes.
306562306a36Sopenharmony_ci	 */
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	rcu_read_lock();
306862306a36Sopenharmony_ci	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
306962306a36Sopenharmony_cirestart:
307062306a36Sopenharmony_ci	for_each_fib6_node_rt_rcu(fn) {
307162306a36Sopenharmony_ci		res.f6i = rt;
307262306a36Sopenharmony_ci		if (fib6_check_expired(rt))
307362306a36Sopenharmony_ci			continue;
307462306a36Sopenharmony_ci		if (rt->fib6_flags & RTF_REJECT)
307562306a36Sopenharmony_ci			break;
307662306a36Sopenharmony_ci		if (unlikely(rt->nh)) {
307762306a36Sopenharmony_ci			if (nexthop_is_blackhole(rt->nh))
307862306a36Sopenharmony_ci				continue;
307962306a36Sopenharmony_ci			/* on match, res->nh is filled in and potentially ret */
308062306a36Sopenharmony_ci			if (nexthop_for_each_fib6_nh(rt->nh,
308162306a36Sopenharmony_ci						     fib6_nh_redirect_match,
308262306a36Sopenharmony_ci						     &arg))
308362306a36Sopenharmony_ci				goto out;
308462306a36Sopenharmony_ci		} else {
308562306a36Sopenharmony_ci			res.nh = rt->fib6_nh;
308662306a36Sopenharmony_ci			if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway,
308762306a36Sopenharmony_ci						  &ret))
308862306a36Sopenharmony_ci				goto out;
308962306a36Sopenharmony_ci		}
309062306a36Sopenharmony_ci	}
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci	if (!rt)
309362306a36Sopenharmony_ci		rt = net->ipv6.fib6_null_entry;
309462306a36Sopenharmony_ci	else if (rt->fib6_flags & RTF_REJECT) {
309562306a36Sopenharmony_ci		ret = net->ipv6.ip6_null_entry;
309662306a36Sopenharmony_ci		goto out;
309762306a36Sopenharmony_ci	}
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry) {
310062306a36Sopenharmony_ci		fn = fib6_backtrack(fn, &fl6->saddr);
310162306a36Sopenharmony_ci		if (fn)
310262306a36Sopenharmony_ci			goto restart;
310362306a36Sopenharmony_ci	}
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	res.f6i = rt;
310662306a36Sopenharmony_ci	res.nh = rt->fib6_nh;
310762306a36Sopenharmony_ciout:
310862306a36Sopenharmony_ci	if (ret) {
310962306a36Sopenharmony_ci		ip6_hold_safe(net, &ret);
311062306a36Sopenharmony_ci	} else {
311162306a36Sopenharmony_ci		res.fib6_flags = res.f6i->fib6_flags;
311262306a36Sopenharmony_ci		res.fib6_type = res.f6i->fib6_type;
311362306a36Sopenharmony_ci		ret = ip6_create_rt_rcu(&res);
311462306a36Sopenharmony_ci	}
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci	rcu_read_unlock();
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	trace_fib6_table_lookup(net, &res, table, fl6);
311962306a36Sopenharmony_ci	return ret;
312062306a36Sopenharmony_ci};
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_cistatic struct dst_entry *ip6_route_redirect(struct net *net,
312362306a36Sopenharmony_ci					    const struct flowi6 *fl6,
312462306a36Sopenharmony_ci					    const struct sk_buff *skb,
312562306a36Sopenharmony_ci					    const struct in6_addr *gateway)
312662306a36Sopenharmony_ci{
312762306a36Sopenharmony_ci	int flags = RT6_LOOKUP_F_HAS_SADDR;
312862306a36Sopenharmony_ci	struct ip6rd_flowi rdfl;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	rdfl.fl6 = *fl6;
313162306a36Sopenharmony_ci	rdfl.gateway = *gateway;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	return fib6_rule_lookup(net, &rdfl.fl6, skb,
313462306a36Sopenharmony_ci				flags, __ip6_route_redirect);
313562306a36Sopenharmony_ci}
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_civoid ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
313862306a36Sopenharmony_ci		  kuid_t uid)
313962306a36Sopenharmony_ci{
314062306a36Sopenharmony_ci	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
314162306a36Sopenharmony_ci	struct dst_entry *dst;
314262306a36Sopenharmony_ci	struct flowi6 fl6 = {
314362306a36Sopenharmony_ci		.flowi6_iif = LOOPBACK_IFINDEX,
314462306a36Sopenharmony_ci		.flowi6_oif = oif,
314562306a36Sopenharmony_ci		.flowi6_mark = mark,
314662306a36Sopenharmony_ci		.daddr = iph->daddr,
314762306a36Sopenharmony_ci		.saddr = iph->saddr,
314862306a36Sopenharmony_ci		.flowlabel = ip6_flowinfo(iph),
314962306a36Sopenharmony_ci		.flowi6_uid = uid,
315062306a36Sopenharmony_ci	};
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
315362306a36Sopenharmony_ci	rt6_do_redirect(dst, NULL, skb);
315462306a36Sopenharmony_ci	dst_release(dst);
315562306a36Sopenharmony_ci}
315662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_redirect);
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_civoid ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
315962306a36Sopenharmony_ci{
316062306a36Sopenharmony_ci	const struct ipv6hdr *iph = ipv6_hdr(skb);
316162306a36Sopenharmony_ci	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
316262306a36Sopenharmony_ci	struct dst_entry *dst;
316362306a36Sopenharmony_ci	struct flowi6 fl6 = {
316462306a36Sopenharmony_ci		.flowi6_iif = LOOPBACK_IFINDEX,
316562306a36Sopenharmony_ci		.flowi6_oif = oif,
316662306a36Sopenharmony_ci		.daddr = msg->dest,
316762306a36Sopenharmony_ci		.saddr = iph->daddr,
316862306a36Sopenharmony_ci		.flowi6_uid = sock_net_uid(net, NULL),
316962306a36Sopenharmony_ci	};
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
317262306a36Sopenharmony_ci	rt6_do_redirect(dst, NULL, skb);
317362306a36Sopenharmony_ci	dst_release(dst);
317462306a36Sopenharmony_ci}
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_civoid ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
317762306a36Sopenharmony_ci{
317862306a36Sopenharmony_ci	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if,
317962306a36Sopenharmony_ci		     READ_ONCE(sk->sk_mark), sk->sk_uid);
318062306a36Sopenharmony_ci}
318162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_redirect);
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_cistatic unsigned int ip6_default_advmss(const struct dst_entry *dst)
318462306a36Sopenharmony_ci{
318562306a36Sopenharmony_ci	struct net_device *dev = dst->dev;
318662306a36Sopenharmony_ci	unsigned int mtu = dst_mtu(dst);
318762306a36Sopenharmony_ci	struct net *net = dev_net(dev);
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
319262306a36Sopenharmony_ci		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci	/*
319562306a36Sopenharmony_ci	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
319662306a36Sopenharmony_ci	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
319762306a36Sopenharmony_ci	 * IPV6_MAXPLEN is also valid and means: "any MSS,
319862306a36Sopenharmony_ci	 * rely only on pmtu discovery"
319962306a36Sopenharmony_ci	 */
320062306a36Sopenharmony_ci	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
320162306a36Sopenharmony_ci		mtu = IPV6_MAXPLEN;
320262306a36Sopenharmony_ci	return mtu;
320362306a36Sopenharmony_ci}
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE unsigned int ip6_mtu(const struct dst_entry *dst)
320662306a36Sopenharmony_ci{
320762306a36Sopenharmony_ci	return ip6_dst_mtu_maybe_forward(dst, false);
320862306a36Sopenharmony_ci}
320962306a36Sopenharmony_ciEXPORT_INDIRECT_CALLABLE(ip6_mtu);
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci/* MTU selection:
321262306a36Sopenharmony_ci * 1. mtu on route is locked - use it
321362306a36Sopenharmony_ci * 2. mtu from nexthop exception
321462306a36Sopenharmony_ci * 3. mtu from egress device
321562306a36Sopenharmony_ci *
321662306a36Sopenharmony_ci * based on ip6_dst_mtu_forward and exception logic of
321762306a36Sopenharmony_ci * rt6_find_cached_rt; called with rcu_read_lock
321862306a36Sopenharmony_ci */
321962306a36Sopenharmony_ciu32 ip6_mtu_from_fib6(const struct fib6_result *res,
322062306a36Sopenharmony_ci		      const struct in6_addr *daddr,
322162306a36Sopenharmony_ci		      const struct in6_addr *saddr)
322262306a36Sopenharmony_ci{
322362306a36Sopenharmony_ci	const struct fib6_nh *nh = res->nh;
322462306a36Sopenharmony_ci	struct fib6_info *f6i = res->f6i;
322562306a36Sopenharmony_ci	struct inet6_dev *idev;
322662306a36Sopenharmony_ci	struct rt6_info *rt;
322762306a36Sopenharmony_ci	u32 mtu = 0;
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci	if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
323062306a36Sopenharmony_ci		mtu = f6i->fib6_pmtu;
323162306a36Sopenharmony_ci		if (mtu)
323262306a36Sopenharmony_ci			goto out;
323362306a36Sopenharmony_ci	}
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci	rt = rt6_find_cached_rt(res, daddr, saddr);
323662306a36Sopenharmony_ci	if (unlikely(rt)) {
323762306a36Sopenharmony_ci		mtu = dst_metric_raw(&rt->dst, RTAX_MTU);
323862306a36Sopenharmony_ci	} else {
323962306a36Sopenharmony_ci		struct net_device *dev = nh->fib_nh_dev;
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci		mtu = IPV6_MIN_MTU;
324262306a36Sopenharmony_ci		idev = __in6_dev_get(dev);
324362306a36Sopenharmony_ci		if (idev && idev->cnf.mtu6 > mtu)
324462306a36Sopenharmony_ci			mtu = idev->cnf.mtu6;
324562306a36Sopenharmony_ci	}
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
324862306a36Sopenharmony_ciout:
324962306a36Sopenharmony_ci	return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
325062306a36Sopenharmony_ci}
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_cistruct dst_entry *icmp6_dst_alloc(struct net_device *dev,
325362306a36Sopenharmony_ci				  struct flowi6 *fl6)
325462306a36Sopenharmony_ci{
325562306a36Sopenharmony_ci	struct dst_entry *dst;
325662306a36Sopenharmony_ci	struct rt6_info *rt;
325762306a36Sopenharmony_ci	struct inet6_dev *idev = in6_dev_get(dev);
325862306a36Sopenharmony_ci	struct net *net = dev_net(dev);
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	if (unlikely(!idev))
326162306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci	rt = ip6_dst_alloc(net, dev, 0);
326462306a36Sopenharmony_ci	if (unlikely(!rt)) {
326562306a36Sopenharmony_ci		in6_dev_put(idev);
326662306a36Sopenharmony_ci		dst = ERR_PTR(-ENOMEM);
326762306a36Sopenharmony_ci		goto out;
326862306a36Sopenharmony_ci	}
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ci	rt->dst.input = ip6_input;
327162306a36Sopenharmony_ci	rt->dst.output  = ip6_output;
327262306a36Sopenharmony_ci	rt->rt6i_gateway  = fl6->daddr;
327362306a36Sopenharmony_ci	rt->rt6i_dst.addr = fl6->daddr;
327462306a36Sopenharmony_ci	rt->rt6i_dst.plen = 128;
327562306a36Sopenharmony_ci	rt->rt6i_idev     = idev;
327662306a36Sopenharmony_ci	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci	/* Add this dst into uncached_list so that rt6_disable_ip() can
327962306a36Sopenharmony_ci	 * do proper release of the net_device
328062306a36Sopenharmony_ci	 */
328162306a36Sopenharmony_ci	rt6_uncached_list_add(rt);
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ciout:
328662306a36Sopenharmony_ci	return dst;
328762306a36Sopenharmony_ci}
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_cistatic void ip6_dst_gc(struct dst_ops *ops)
329062306a36Sopenharmony_ci{
329162306a36Sopenharmony_ci	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
329262306a36Sopenharmony_ci	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
329362306a36Sopenharmony_ci	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
329462306a36Sopenharmony_ci	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
329562306a36Sopenharmony_ci	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
329662306a36Sopenharmony_ci	unsigned int val;
329762306a36Sopenharmony_ci	int entries;
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_ci	if (time_after(rt_last_gc + rt_min_interval, jiffies))
330062306a36Sopenharmony_ci		goto out;
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	fib6_run_gc(atomic_inc_return(&net->ipv6.ip6_rt_gc_expire), net, true);
330362306a36Sopenharmony_ci	entries = dst_entries_get_slow(ops);
330462306a36Sopenharmony_ci	if (entries < ops->gc_thresh)
330562306a36Sopenharmony_ci		atomic_set(&net->ipv6.ip6_rt_gc_expire, rt_gc_timeout >> 1);
330662306a36Sopenharmony_ciout:
330762306a36Sopenharmony_ci	val = atomic_read(&net->ipv6.ip6_rt_gc_expire);
330862306a36Sopenharmony_ci	atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity));
330962306a36Sopenharmony_ci}
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_cistatic int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg,
331262306a36Sopenharmony_ci			       const struct in6_addr *gw_addr, u32 tbid,
331362306a36Sopenharmony_ci			       int flags, struct fib6_result *res)
331462306a36Sopenharmony_ci{
331562306a36Sopenharmony_ci	struct flowi6 fl6 = {
331662306a36Sopenharmony_ci		.flowi6_oif = cfg->fc_ifindex,
331762306a36Sopenharmony_ci		.daddr = *gw_addr,
331862306a36Sopenharmony_ci		.saddr = cfg->fc_prefsrc,
331962306a36Sopenharmony_ci	};
332062306a36Sopenharmony_ci	struct fib6_table *table;
332162306a36Sopenharmony_ci	int err;
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	table = fib6_get_table(net, tbid);
332462306a36Sopenharmony_ci	if (!table)
332562306a36Sopenharmony_ci		return -EINVAL;
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci	if (!ipv6_addr_any(&cfg->fc_prefsrc))
332862306a36Sopenharmony_ci		flags |= RT6_LOOKUP_F_HAS_SADDR;
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci	err = fib6_table_lookup(net, table, cfg->fc_ifindex, &fl6, res, flags);
333362306a36Sopenharmony_ci	if (!err && res->f6i != net->ipv6.fib6_null_entry)
333462306a36Sopenharmony_ci		fib6_select_path(net, res, &fl6, cfg->fc_ifindex,
333562306a36Sopenharmony_ci				 cfg->fc_ifindex != 0, NULL, flags);
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci	return err;
333862306a36Sopenharmony_ci}
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_cistatic int ip6_route_check_nh_onlink(struct net *net,
334162306a36Sopenharmony_ci				     struct fib6_config *cfg,
334262306a36Sopenharmony_ci				     const struct net_device *dev,
334362306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
334462306a36Sopenharmony_ci{
334562306a36Sopenharmony_ci	u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
334662306a36Sopenharmony_ci	const struct in6_addr *gw_addr = &cfg->fc_gateway;
334762306a36Sopenharmony_ci	struct fib6_result res = {};
334862306a36Sopenharmony_ci	int err;
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	err = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0, &res);
335162306a36Sopenharmony_ci	if (!err && !(res.fib6_flags & RTF_REJECT) &&
335262306a36Sopenharmony_ci	    /* ignore match if it is the default route */
335362306a36Sopenharmony_ci	    !ipv6_addr_any(&res.f6i->fib6_dst.addr) &&
335462306a36Sopenharmony_ci	    (res.fib6_type != RTN_UNICAST || dev != res.nh->fib_nh_dev)) {
335562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
335662306a36Sopenharmony_ci			       "Nexthop has invalid gateway or device mismatch");
335762306a36Sopenharmony_ci		err = -EINVAL;
335862306a36Sopenharmony_ci	}
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	return err;
336162306a36Sopenharmony_ci}
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_cistatic int ip6_route_check_nh(struct net *net,
336462306a36Sopenharmony_ci			      struct fib6_config *cfg,
336562306a36Sopenharmony_ci			      struct net_device **_dev,
336662306a36Sopenharmony_ci			      netdevice_tracker *dev_tracker,
336762306a36Sopenharmony_ci			      struct inet6_dev **idev)
336862306a36Sopenharmony_ci{
336962306a36Sopenharmony_ci	const struct in6_addr *gw_addr = &cfg->fc_gateway;
337062306a36Sopenharmony_ci	struct net_device *dev = _dev ? *_dev : NULL;
337162306a36Sopenharmony_ci	int flags = RT6_LOOKUP_F_IFACE;
337262306a36Sopenharmony_ci	struct fib6_result res = {};
337362306a36Sopenharmony_ci	int err = -EHOSTUNREACH;
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci	if (cfg->fc_table) {
337662306a36Sopenharmony_ci		err = ip6_nh_lookup_table(net, cfg, gw_addr,
337762306a36Sopenharmony_ci					  cfg->fc_table, flags, &res);
337862306a36Sopenharmony_ci		/* gw_addr can not require a gateway or resolve to a reject
337962306a36Sopenharmony_ci		 * route. If a device is given, it must match the result.
338062306a36Sopenharmony_ci		 */
338162306a36Sopenharmony_ci		if (err || res.fib6_flags & RTF_REJECT ||
338262306a36Sopenharmony_ci		    res.nh->fib_nh_gw_family ||
338362306a36Sopenharmony_ci		    (dev && dev != res.nh->fib_nh_dev))
338462306a36Sopenharmony_ci			err = -EHOSTUNREACH;
338562306a36Sopenharmony_ci	}
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci	if (err < 0) {
338862306a36Sopenharmony_ci		struct flowi6 fl6 = {
338962306a36Sopenharmony_ci			.flowi6_oif = cfg->fc_ifindex,
339062306a36Sopenharmony_ci			.daddr = *gw_addr,
339162306a36Sopenharmony_ci		};
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci		err = fib6_lookup(net, cfg->fc_ifindex, &fl6, &res, flags);
339462306a36Sopenharmony_ci		if (err || res.fib6_flags & RTF_REJECT ||
339562306a36Sopenharmony_ci		    res.nh->fib_nh_gw_family)
339662306a36Sopenharmony_ci			err = -EHOSTUNREACH;
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci		if (err)
339962306a36Sopenharmony_ci			return err;
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ci		fib6_select_path(net, &res, &fl6, cfg->fc_ifindex,
340262306a36Sopenharmony_ci				 cfg->fc_ifindex != 0, NULL, flags);
340362306a36Sopenharmony_ci	}
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	err = 0;
340662306a36Sopenharmony_ci	if (dev) {
340762306a36Sopenharmony_ci		if (dev != res.nh->fib_nh_dev)
340862306a36Sopenharmony_ci			err = -EHOSTUNREACH;
340962306a36Sopenharmony_ci	} else {
341062306a36Sopenharmony_ci		*_dev = dev = res.nh->fib_nh_dev;
341162306a36Sopenharmony_ci		netdev_hold(dev, dev_tracker, GFP_ATOMIC);
341262306a36Sopenharmony_ci		*idev = in6_dev_get(dev);
341362306a36Sopenharmony_ci	}
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci	return err;
341662306a36Sopenharmony_ci}
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_cistatic int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
341962306a36Sopenharmony_ci			   struct net_device **_dev,
342062306a36Sopenharmony_ci			   netdevice_tracker *dev_tracker,
342162306a36Sopenharmony_ci			   struct inet6_dev **idev,
342262306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
342362306a36Sopenharmony_ci{
342462306a36Sopenharmony_ci	const struct in6_addr *gw_addr = &cfg->fc_gateway;
342562306a36Sopenharmony_ci	int gwa_type = ipv6_addr_type(gw_addr);
342662306a36Sopenharmony_ci	bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
342762306a36Sopenharmony_ci	const struct net_device *dev = *_dev;
342862306a36Sopenharmony_ci	bool need_addr_check = !dev;
342962306a36Sopenharmony_ci	int err = -EINVAL;
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci	/* if gw_addr is local we will fail to detect this in case
343262306a36Sopenharmony_ci	 * address is still TENTATIVE (DAD in progress). rt6_lookup()
343362306a36Sopenharmony_ci	 * will return already-added prefix route via interface that
343462306a36Sopenharmony_ci	 * prefix route was assigned to, which might be non-loopback.
343562306a36Sopenharmony_ci	 */
343662306a36Sopenharmony_ci	if (dev &&
343762306a36Sopenharmony_ci	    ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
343862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
343962306a36Sopenharmony_ci		goto out;
344062306a36Sopenharmony_ci	}
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci	if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
344362306a36Sopenharmony_ci		/* IPv6 strictly inhibits using not link-local
344462306a36Sopenharmony_ci		 * addresses as nexthop address.
344562306a36Sopenharmony_ci		 * Otherwise, router will not able to send redirects.
344662306a36Sopenharmony_ci		 * It is very good, but in some (rare!) circumstances
344762306a36Sopenharmony_ci		 * (SIT, PtP, NBMA NOARP links) it is handy to allow
344862306a36Sopenharmony_ci		 * some exceptions. --ANK
344962306a36Sopenharmony_ci		 * We allow IPv4-mapped nexthops to support RFC4798-type
345062306a36Sopenharmony_ci		 * addressing
345162306a36Sopenharmony_ci		 */
345262306a36Sopenharmony_ci		if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
345362306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid gateway address");
345462306a36Sopenharmony_ci			goto out;
345562306a36Sopenharmony_ci		}
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci		rcu_read_lock();
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci		if (cfg->fc_flags & RTNH_F_ONLINK)
346062306a36Sopenharmony_ci			err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
346162306a36Sopenharmony_ci		else
346262306a36Sopenharmony_ci			err = ip6_route_check_nh(net, cfg, _dev, dev_tracker,
346362306a36Sopenharmony_ci						 idev);
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci		rcu_read_unlock();
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_ci		if (err)
346862306a36Sopenharmony_ci			goto out;
346962306a36Sopenharmony_ci	}
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	/* reload in case device was changed */
347262306a36Sopenharmony_ci	dev = *_dev;
347362306a36Sopenharmony_ci
347462306a36Sopenharmony_ci	err = -EINVAL;
347562306a36Sopenharmony_ci	if (!dev) {
347662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Egress device not specified");
347762306a36Sopenharmony_ci		goto out;
347862306a36Sopenharmony_ci	} else if (dev->flags & IFF_LOOPBACK) {
347962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
348062306a36Sopenharmony_ci			       "Egress device can not be loopback device for this route");
348162306a36Sopenharmony_ci		goto out;
348262306a36Sopenharmony_ci	}
348362306a36Sopenharmony_ci
348462306a36Sopenharmony_ci	/* if we did not check gw_addr above, do so now that the
348562306a36Sopenharmony_ci	 * egress device has been resolved.
348662306a36Sopenharmony_ci	 */
348762306a36Sopenharmony_ci	if (need_addr_check &&
348862306a36Sopenharmony_ci	    ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
348962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
349062306a36Sopenharmony_ci		goto out;
349162306a36Sopenharmony_ci	}
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	err = 0;
349462306a36Sopenharmony_ciout:
349562306a36Sopenharmony_ci	return err;
349662306a36Sopenharmony_ci}
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_cistatic bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
349962306a36Sopenharmony_ci{
350062306a36Sopenharmony_ci	if ((flags & RTF_REJECT) ||
350162306a36Sopenharmony_ci	    (dev && (dev->flags & IFF_LOOPBACK) &&
350262306a36Sopenharmony_ci	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
350362306a36Sopenharmony_ci	     !(flags & (RTF_ANYCAST | RTF_LOCAL))))
350462306a36Sopenharmony_ci		return true;
350562306a36Sopenharmony_ci
350662306a36Sopenharmony_ci	return false;
350762306a36Sopenharmony_ci}
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_ciint fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
351062306a36Sopenharmony_ci		 struct fib6_config *cfg, gfp_t gfp_flags,
351162306a36Sopenharmony_ci		 struct netlink_ext_ack *extack)
351262306a36Sopenharmony_ci{
351362306a36Sopenharmony_ci	netdevice_tracker *dev_tracker = &fib6_nh->fib_nh_dev_tracker;
351462306a36Sopenharmony_ci	struct net_device *dev = NULL;
351562306a36Sopenharmony_ci	struct inet6_dev *idev = NULL;
351662306a36Sopenharmony_ci	int addr_type;
351762306a36Sopenharmony_ci	int err;
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	fib6_nh->fib_nh_family = AF_INET6;
352062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
352162306a36Sopenharmony_ci	fib6_nh->last_probe = jiffies;
352262306a36Sopenharmony_ci#endif
352362306a36Sopenharmony_ci	if (cfg->fc_is_fdb) {
352462306a36Sopenharmony_ci		fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
352562306a36Sopenharmony_ci		fib6_nh->fib_nh_gw_family = AF_INET6;
352662306a36Sopenharmony_ci		return 0;
352762306a36Sopenharmony_ci	}
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_ci	err = -ENODEV;
353062306a36Sopenharmony_ci	if (cfg->fc_ifindex) {
353162306a36Sopenharmony_ci		dev = netdev_get_by_index(net, cfg->fc_ifindex,
353262306a36Sopenharmony_ci					  dev_tracker, gfp_flags);
353362306a36Sopenharmony_ci		if (!dev)
353462306a36Sopenharmony_ci			goto out;
353562306a36Sopenharmony_ci		idev = in6_dev_get(dev);
353662306a36Sopenharmony_ci		if (!idev)
353762306a36Sopenharmony_ci			goto out;
353862306a36Sopenharmony_ci	}
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	if (cfg->fc_flags & RTNH_F_ONLINK) {
354162306a36Sopenharmony_ci		if (!dev) {
354262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
354362306a36Sopenharmony_ci				       "Nexthop device required for onlink");
354462306a36Sopenharmony_ci			goto out;
354562306a36Sopenharmony_ci		}
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci		if (!(dev->flags & IFF_UP)) {
354862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthop device is not up");
354962306a36Sopenharmony_ci			err = -ENETDOWN;
355062306a36Sopenharmony_ci			goto out;
355162306a36Sopenharmony_ci		}
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci		fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
355462306a36Sopenharmony_ci	}
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	fib6_nh->fib_nh_weight = 1;
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	/* We cannot add true routes via loopback here,
355962306a36Sopenharmony_ci	 * they would result in kernel looping; promote them to reject routes
356062306a36Sopenharmony_ci	 */
356162306a36Sopenharmony_ci	addr_type = ipv6_addr_type(&cfg->fc_dst);
356262306a36Sopenharmony_ci	if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
356362306a36Sopenharmony_ci		/* hold loopback dev/idev if we haven't done so. */
356462306a36Sopenharmony_ci		if (dev != net->loopback_dev) {
356562306a36Sopenharmony_ci			if (dev) {
356662306a36Sopenharmony_ci				netdev_put(dev, dev_tracker);
356762306a36Sopenharmony_ci				in6_dev_put(idev);
356862306a36Sopenharmony_ci			}
356962306a36Sopenharmony_ci			dev = net->loopback_dev;
357062306a36Sopenharmony_ci			netdev_hold(dev, dev_tracker, gfp_flags);
357162306a36Sopenharmony_ci			idev = in6_dev_get(dev);
357262306a36Sopenharmony_ci			if (!idev) {
357362306a36Sopenharmony_ci				err = -ENODEV;
357462306a36Sopenharmony_ci				goto out;
357562306a36Sopenharmony_ci			}
357662306a36Sopenharmony_ci		}
357762306a36Sopenharmony_ci		goto pcpu_alloc;
357862306a36Sopenharmony_ci	}
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci	if (cfg->fc_flags & RTF_GATEWAY) {
358162306a36Sopenharmony_ci		err = ip6_validate_gw(net, cfg, &dev, dev_tracker,
358262306a36Sopenharmony_ci				      &idev, extack);
358362306a36Sopenharmony_ci		if (err)
358462306a36Sopenharmony_ci			goto out;
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci		fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
358762306a36Sopenharmony_ci		fib6_nh->fib_nh_gw_family = AF_INET6;
358862306a36Sopenharmony_ci	}
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	err = -ENODEV;
359162306a36Sopenharmony_ci	if (!dev)
359262306a36Sopenharmony_ci		goto out;
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci	if (idev->cnf.disable_ipv6) {
359562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
359662306a36Sopenharmony_ci		err = -EACCES;
359762306a36Sopenharmony_ci		goto out;
359862306a36Sopenharmony_ci	}
359962306a36Sopenharmony_ci
360062306a36Sopenharmony_ci	if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
360162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Nexthop device is not up");
360262306a36Sopenharmony_ci		err = -ENETDOWN;
360362306a36Sopenharmony_ci		goto out;
360462306a36Sopenharmony_ci	}
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci	if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
360762306a36Sopenharmony_ci	    !netif_carrier_ok(dev))
360862306a36Sopenharmony_ci		fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	err = fib_nh_common_init(net, &fib6_nh->nh_common, cfg->fc_encap,
361162306a36Sopenharmony_ci				 cfg->fc_encap_type, cfg, gfp_flags, extack);
361262306a36Sopenharmony_ci	if (err)
361362306a36Sopenharmony_ci		goto out;
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_cipcpu_alloc:
361662306a36Sopenharmony_ci	fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
361762306a36Sopenharmony_ci	if (!fib6_nh->rt6i_pcpu) {
361862306a36Sopenharmony_ci		err = -ENOMEM;
361962306a36Sopenharmony_ci		goto out;
362062306a36Sopenharmony_ci	}
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci	fib6_nh->fib_nh_dev = dev;
362362306a36Sopenharmony_ci	fib6_nh->fib_nh_oif = dev->ifindex;
362462306a36Sopenharmony_ci	err = 0;
362562306a36Sopenharmony_ciout:
362662306a36Sopenharmony_ci	if (idev)
362762306a36Sopenharmony_ci		in6_dev_put(idev);
362862306a36Sopenharmony_ci
362962306a36Sopenharmony_ci	if (err) {
363062306a36Sopenharmony_ci		lwtstate_put(fib6_nh->fib_nh_lws);
363162306a36Sopenharmony_ci		fib6_nh->fib_nh_lws = NULL;
363262306a36Sopenharmony_ci		netdev_put(dev, dev_tracker);
363362306a36Sopenharmony_ci	}
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	return err;
363662306a36Sopenharmony_ci}
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_civoid fib6_nh_release(struct fib6_nh *fib6_nh)
363962306a36Sopenharmony_ci{
364062306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	rcu_read_lock();
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci	fib6_nh_flush_exceptions(fib6_nh, NULL);
364562306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(fib6_nh, NULL);
364662306a36Sopenharmony_ci	if (bucket) {
364762306a36Sopenharmony_ci		rcu_assign_pointer(fib6_nh->rt6i_exception_bucket, NULL);
364862306a36Sopenharmony_ci		kfree(bucket);
364962306a36Sopenharmony_ci	}
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	rcu_read_unlock();
365262306a36Sopenharmony_ci
365362306a36Sopenharmony_ci	fib6_nh_release_dsts(fib6_nh);
365462306a36Sopenharmony_ci	free_percpu(fib6_nh->rt6i_pcpu);
365562306a36Sopenharmony_ci
365662306a36Sopenharmony_ci	fib_nh_common_release(&fib6_nh->nh_common);
365762306a36Sopenharmony_ci}
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_civoid fib6_nh_release_dsts(struct fib6_nh *fib6_nh)
366062306a36Sopenharmony_ci{
366162306a36Sopenharmony_ci	int cpu;
366262306a36Sopenharmony_ci
366362306a36Sopenharmony_ci	if (!fib6_nh->rt6i_pcpu)
366462306a36Sopenharmony_ci		return;
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
366762306a36Sopenharmony_ci		struct rt6_info *pcpu_rt, **ppcpu_rt;
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci		ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
367062306a36Sopenharmony_ci		pcpu_rt = xchg(ppcpu_rt, NULL);
367162306a36Sopenharmony_ci		if (pcpu_rt) {
367262306a36Sopenharmony_ci			dst_dev_put(&pcpu_rt->dst);
367362306a36Sopenharmony_ci			dst_release(&pcpu_rt->dst);
367462306a36Sopenharmony_ci		}
367562306a36Sopenharmony_ci	}
367662306a36Sopenharmony_ci}
367762306a36Sopenharmony_ci
367862306a36Sopenharmony_cistatic struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
367962306a36Sopenharmony_ci					      gfp_t gfp_flags,
368062306a36Sopenharmony_ci					      struct netlink_ext_ack *extack)
368162306a36Sopenharmony_ci{
368262306a36Sopenharmony_ci	struct net *net = cfg->fc_nlinfo.nl_net;
368362306a36Sopenharmony_ci	struct fib6_info *rt = NULL;
368462306a36Sopenharmony_ci	struct nexthop *nh = NULL;
368562306a36Sopenharmony_ci	struct fib6_table *table;
368662306a36Sopenharmony_ci	struct fib6_nh *fib6_nh;
368762306a36Sopenharmony_ci	int err = -EINVAL;
368862306a36Sopenharmony_ci	int addr_type;
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci	/* RTF_PCPU is an internal flag; can not be set by userspace */
369162306a36Sopenharmony_ci	if (cfg->fc_flags & RTF_PCPU) {
369262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
369362306a36Sopenharmony_ci		goto out;
369462306a36Sopenharmony_ci	}
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci	/* RTF_CACHE is an internal flag; can not be set by userspace */
369762306a36Sopenharmony_ci	if (cfg->fc_flags & RTF_CACHE) {
369862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
369962306a36Sopenharmony_ci		goto out;
370062306a36Sopenharmony_ci	}
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_ci	if (cfg->fc_type > RTN_MAX) {
370362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid route type");
370462306a36Sopenharmony_ci		goto out;
370562306a36Sopenharmony_ci	}
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_ci	if (cfg->fc_dst_len > 128) {
370862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid prefix length");
370962306a36Sopenharmony_ci		goto out;
371062306a36Sopenharmony_ci	}
371162306a36Sopenharmony_ci	if (cfg->fc_src_len > 128) {
371262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid source address length");
371362306a36Sopenharmony_ci		goto out;
371462306a36Sopenharmony_ci	}
371562306a36Sopenharmony_ci#ifndef CONFIG_IPV6_SUBTREES
371662306a36Sopenharmony_ci	if (cfg->fc_src_len) {
371762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
371862306a36Sopenharmony_ci			       "Specifying source address requires IPV6_SUBTREES to be enabled");
371962306a36Sopenharmony_ci		goto out;
372062306a36Sopenharmony_ci	}
372162306a36Sopenharmony_ci#endif
372262306a36Sopenharmony_ci	if (cfg->fc_nh_id) {
372362306a36Sopenharmony_ci		nh = nexthop_find_by_id(net, cfg->fc_nh_id);
372462306a36Sopenharmony_ci		if (!nh) {
372562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
372662306a36Sopenharmony_ci			goto out;
372762306a36Sopenharmony_ci		}
372862306a36Sopenharmony_ci		err = fib6_check_nexthop(nh, cfg, extack);
372962306a36Sopenharmony_ci		if (err)
373062306a36Sopenharmony_ci			goto out;
373162306a36Sopenharmony_ci	}
373262306a36Sopenharmony_ci
373362306a36Sopenharmony_ci	err = -ENOBUFS;
373462306a36Sopenharmony_ci	if (cfg->fc_nlinfo.nlh &&
373562306a36Sopenharmony_ci	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
373662306a36Sopenharmony_ci		table = fib6_get_table(net, cfg->fc_table);
373762306a36Sopenharmony_ci		if (!table) {
373862306a36Sopenharmony_ci			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
373962306a36Sopenharmony_ci			table = fib6_new_table(net, cfg->fc_table);
374062306a36Sopenharmony_ci		}
374162306a36Sopenharmony_ci	} else {
374262306a36Sopenharmony_ci		table = fib6_new_table(net, cfg->fc_table);
374362306a36Sopenharmony_ci	}
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	if (!table)
374662306a36Sopenharmony_ci		goto out;
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci	err = -ENOMEM;
374962306a36Sopenharmony_ci	rt = fib6_info_alloc(gfp_flags, !nh);
375062306a36Sopenharmony_ci	if (!rt)
375162306a36Sopenharmony_ci		goto out;
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_ci	rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
375462306a36Sopenharmony_ci					       extack);
375562306a36Sopenharmony_ci	if (IS_ERR(rt->fib6_metrics)) {
375662306a36Sopenharmony_ci		err = PTR_ERR(rt->fib6_metrics);
375762306a36Sopenharmony_ci		/* Do not leave garbage there. */
375862306a36Sopenharmony_ci		rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
375962306a36Sopenharmony_ci		goto out_free;
376062306a36Sopenharmony_ci	}
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci	if (cfg->fc_flags & RTF_ADDRCONF)
376362306a36Sopenharmony_ci		rt->dst_nocount = true;
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci	if (cfg->fc_flags & RTF_EXPIRES)
376662306a36Sopenharmony_ci		fib6_set_expires(rt, jiffies +
376762306a36Sopenharmony_ci				clock_t_to_jiffies(cfg->fc_expires));
376862306a36Sopenharmony_ci	else
376962306a36Sopenharmony_ci		fib6_clean_expires(rt);
377062306a36Sopenharmony_ci
377162306a36Sopenharmony_ci	if (cfg->fc_protocol == RTPROT_UNSPEC)
377262306a36Sopenharmony_ci		cfg->fc_protocol = RTPROT_BOOT;
377362306a36Sopenharmony_ci	rt->fib6_protocol = cfg->fc_protocol;
377462306a36Sopenharmony_ci
377562306a36Sopenharmony_ci	rt->fib6_table = table;
377662306a36Sopenharmony_ci	rt->fib6_metric = cfg->fc_metric;
377762306a36Sopenharmony_ci	rt->fib6_type = cfg->fc_type ? : RTN_UNICAST;
377862306a36Sopenharmony_ci	rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
378162306a36Sopenharmony_ci	rt->fib6_dst.plen = cfg->fc_dst_len;
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
378462306a36Sopenharmony_ci	ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
378562306a36Sopenharmony_ci	rt->fib6_src.plen = cfg->fc_src_len;
378662306a36Sopenharmony_ci#endif
378762306a36Sopenharmony_ci	if (nh) {
378862306a36Sopenharmony_ci		if (rt->fib6_src.plen) {
378962306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthops can not be used with source routing");
379062306a36Sopenharmony_ci			goto out_free;
379162306a36Sopenharmony_ci		}
379262306a36Sopenharmony_ci		if (!nexthop_get(nh)) {
379362306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
379462306a36Sopenharmony_ci			goto out_free;
379562306a36Sopenharmony_ci		}
379662306a36Sopenharmony_ci		rt->nh = nh;
379762306a36Sopenharmony_ci		fib6_nh = nexthop_fib6_nh(rt->nh);
379862306a36Sopenharmony_ci	} else {
379962306a36Sopenharmony_ci		err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack);
380062306a36Sopenharmony_ci		if (err)
380162306a36Sopenharmony_ci			goto out;
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci		fib6_nh = rt->fib6_nh;
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci		/* We cannot add true routes via loopback here, they would
380662306a36Sopenharmony_ci		 * result in kernel looping; promote them to reject routes
380762306a36Sopenharmony_ci		 */
380862306a36Sopenharmony_ci		addr_type = ipv6_addr_type(&cfg->fc_dst);
380962306a36Sopenharmony_ci		if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev,
381062306a36Sopenharmony_ci				   addr_type))
381162306a36Sopenharmony_ci			rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
381262306a36Sopenharmony_ci	}
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
381562306a36Sopenharmony_ci		struct net_device *dev = fib6_nh->fib_nh_dev;
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
381862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid source address");
381962306a36Sopenharmony_ci			err = -EINVAL;
382062306a36Sopenharmony_ci			goto out;
382162306a36Sopenharmony_ci		}
382262306a36Sopenharmony_ci		rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
382362306a36Sopenharmony_ci		rt->fib6_prefsrc.plen = 128;
382462306a36Sopenharmony_ci	} else
382562306a36Sopenharmony_ci		rt->fib6_prefsrc.plen = 0;
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci	return rt;
382862306a36Sopenharmony_ciout:
382962306a36Sopenharmony_ci	fib6_info_release(rt);
383062306a36Sopenharmony_ci	return ERR_PTR(err);
383162306a36Sopenharmony_ciout_free:
383262306a36Sopenharmony_ci	ip_fib_metrics_put(rt->fib6_metrics);
383362306a36Sopenharmony_ci	kfree(rt);
383462306a36Sopenharmony_ci	return ERR_PTR(err);
383562306a36Sopenharmony_ci}
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ciint ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
383862306a36Sopenharmony_ci		  struct netlink_ext_ack *extack)
383962306a36Sopenharmony_ci{
384062306a36Sopenharmony_ci	struct fib6_info *rt;
384162306a36Sopenharmony_ci	int err;
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci	rt = ip6_route_info_create(cfg, gfp_flags, extack);
384462306a36Sopenharmony_ci	if (IS_ERR(rt))
384562306a36Sopenharmony_ci		return PTR_ERR(rt);
384662306a36Sopenharmony_ci
384762306a36Sopenharmony_ci	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
384862306a36Sopenharmony_ci	fib6_info_release(rt);
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci	return err;
385162306a36Sopenharmony_ci}
385262306a36Sopenharmony_ci
385362306a36Sopenharmony_cistatic int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
385462306a36Sopenharmony_ci{
385562306a36Sopenharmony_ci	struct net *net = info->nl_net;
385662306a36Sopenharmony_ci	struct fib6_table *table;
385762306a36Sopenharmony_ci	int err;
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry) {
386062306a36Sopenharmony_ci		err = -ENOENT;
386162306a36Sopenharmony_ci		goto out;
386262306a36Sopenharmony_ci	}
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	table = rt->fib6_table;
386562306a36Sopenharmony_ci	spin_lock_bh(&table->tb6_lock);
386662306a36Sopenharmony_ci	err = fib6_del(rt, info);
386762306a36Sopenharmony_ci	spin_unlock_bh(&table->tb6_lock);
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ciout:
387062306a36Sopenharmony_ci	fib6_info_release(rt);
387162306a36Sopenharmony_ci	return err;
387262306a36Sopenharmony_ci}
387362306a36Sopenharmony_ci
387462306a36Sopenharmony_ciint ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
387562306a36Sopenharmony_ci{
387662306a36Sopenharmony_ci	struct nl_info info = {
387762306a36Sopenharmony_ci		.nl_net = net,
387862306a36Sopenharmony_ci		.skip_notify = skip_notify
387962306a36Sopenharmony_ci	};
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_ci	return __ip6_del_rt(rt, &info);
388262306a36Sopenharmony_ci}
388362306a36Sopenharmony_ci
388462306a36Sopenharmony_cistatic int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
388562306a36Sopenharmony_ci{
388662306a36Sopenharmony_ci	struct nl_info *info = &cfg->fc_nlinfo;
388762306a36Sopenharmony_ci	struct net *net = info->nl_net;
388862306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
388962306a36Sopenharmony_ci	struct fib6_table *table;
389062306a36Sopenharmony_ci	int err = -ENOENT;
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry)
389362306a36Sopenharmony_ci		goto out_put;
389462306a36Sopenharmony_ci	table = rt->fib6_table;
389562306a36Sopenharmony_ci	spin_lock_bh(&table->tb6_lock);
389662306a36Sopenharmony_ci
389762306a36Sopenharmony_ci	if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
389862306a36Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
389962306a36Sopenharmony_ci		struct fib6_node *fn;
390062306a36Sopenharmony_ci
390162306a36Sopenharmony_ci		/* prefer to send a single notification with all hops */
390262306a36Sopenharmony_ci		skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
390362306a36Sopenharmony_ci		if (skb) {
390462306a36Sopenharmony_ci			u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci			if (rt6_fill_node(net, skb, rt, NULL,
390762306a36Sopenharmony_ci					  NULL, NULL, 0, RTM_DELROUTE,
390862306a36Sopenharmony_ci					  info->portid, seq, 0) < 0) {
390962306a36Sopenharmony_ci				kfree_skb(skb);
391062306a36Sopenharmony_ci				skb = NULL;
391162306a36Sopenharmony_ci			} else
391262306a36Sopenharmony_ci				info->skip_notify = 1;
391362306a36Sopenharmony_ci		}
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci		/* 'rt' points to the first sibling route. If it is not the
391662306a36Sopenharmony_ci		 * leaf, then we do not need to send a notification. Otherwise,
391762306a36Sopenharmony_ci		 * we need to check if the last sibling has a next route or not
391862306a36Sopenharmony_ci		 * and emit a replace or delete notification, respectively.
391962306a36Sopenharmony_ci		 */
392062306a36Sopenharmony_ci		info->skip_notify_kernel = 1;
392162306a36Sopenharmony_ci		fn = rcu_dereference_protected(rt->fib6_node,
392262306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
392362306a36Sopenharmony_ci		if (rcu_access_pointer(fn->leaf) == rt) {
392462306a36Sopenharmony_ci			struct fib6_info *last_sibling, *replace_rt;
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci			last_sibling = list_last_entry(&rt->fib6_siblings,
392762306a36Sopenharmony_ci						       struct fib6_info,
392862306a36Sopenharmony_ci						       fib6_siblings);
392962306a36Sopenharmony_ci			replace_rt = rcu_dereference_protected(
393062306a36Sopenharmony_ci					    last_sibling->fib6_next,
393162306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
393262306a36Sopenharmony_ci			if (replace_rt)
393362306a36Sopenharmony_ci				call_fib6_entry_notifiers_replace(net,
393462306a36Sopenharmony_ci								  replace_rt);
393562306a36Sopenharmony_ci			else
393662306a36Sopenharmony_ci				call_fib6_multipath_entry_notifiers(net,
393762306a36Sopenharmony_ci						       FIB_EVENT_ENTRY_DEL,
393862306a36Sopenharmony_ci						       rt, rt->fib6_nsiblings,
393962306a36Sopenharmony_ci						       NULL);
394062306a36Sopenharmony_ci		}
394162306a36Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
394262306a36Sopenharmony_ci					 &rt->fib6_siblings,
394362306a36Sopenharmony_ci					 fib6_siblings) {
394462306a36Sopenharmony_ci			err = fib6_del(sibling, info);
394562306a36Sopenharmony_ci			if (err)
394662306a36Sopenharmony_ci				goto out_unlock;
394762306a36Sopenharmony_ci		}
394862306a36Sopenharmony_ci	}
394962306a36Sopenharmony_ci
395062306a36Sopenharmony_ci	err = fib6_del(rt, info);
395162306a36Sopenharmony_ciout_unlock:
395262306a36Sopenharmony_ci	spin_unlock_bh(&table->tb6_lock);
395362306a36Sopenharmony_ciout_put:
395462306a36Sopenharmony_ci	fib6_info_release(rt);
395562306a36Sopenharmony_ci
395662306a36Sopenharmony_ci	if (skb) {
395762306a36Sopenharmony_ci		rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
395862306a36Sopenharmony_ci			    info->nlh, gfp_any());
395962306a36Sopenharmony_ci	}
396062306a36Sopenharmony_ci	return err;
396162306a36Sopenharmony_ci}
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_cistatic int __ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
396462306a36Sopenharmony_ci{
396562306a36Sopenharmony_ci	int rc = -ESRCH;
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci	if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
396862306a36Sopenharmony_ci		goto out;
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_ci	if (cfg->fc_flags & RTF_GATEWAY &&
397162306a36Sopenharmony_ci	    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
397262306a36Sopenharmony_ci		goto out;
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_ci	rc = rt6_remove_exception_rt(rt);
397562306a36Sopenharmony_ciout:
397662306a36Sopenharmony_ci	return rc;
397762306a36Sopenharmony_ci}
397862306a36Sopenharmony_ci
397962306a36Sopenharmony_cistatic int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt,
398062306a36Sopenharmony_ci			     struct fib6_nh *nh)
398162306a36Sopenharmony_ci{
398262306a36Sopenharmony_ci	struct fib6_result res = {
398362306a36Sopenharmony_ci		.f6i = rt,
398462306a36Sopenharmony_ci		.nh = nh,
398562306a36Sopenharmony_ci	};
398662306a36Sopenharmony_ci	struct rt6_info *rt_cache;
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_ci	rt_cache = rt6_find_cached_rt(&res, &cfg->fc_dst, &cfg->fc_src);
398962306a36Sopenharmony_ci	if (rt_cache)
399062306a36Sopenharmony_ci		return __ip6_del_cached_rt(rt_cache, cfg);
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci	return 0;
399362306a36Sopenharmony_ci}
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_cistruct fib6_nh_del_cached_rt_arg {
399662306a36Sopenharmony_ci	struct fib6_config *cfg;
399762306a36Sopenharmony_ci	struct fib6_info *f6i;
399862306a36Sopenharmony_ci};
399962306a36Sopenharmony_ci
400062306a36Sopenharmony_cistatic int fib6_nh_del_cached_rt(struct fib6_nh *nh, void *_arg)
400162306a36Sopenharmony_ci{
400262306a36Sopenharmony_ci	struct fib6_nh_del_cached_rt_arg *arg = _arg;
400362306a36Sopenharmony_ci	int rc;
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_ci	rc = ip6_del_cached_rt(arg->cfg, arg->f6i, nh);
400662306a36Sopenharmony_ci	return rc != -ESRCH ? rc : 0;
400762306a36Sopenharmony_ci}
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_cistatic int ip6_del_cached_rt_nh(struct fib6_config *cfg, struct fib6_info *f6i)
401062306a36Sopenharmony_ci{
401162306a36Sopenharmony_ci	struct fib6_nh_del_cached_rt_arg arg = {
401262306a36Sopenharmony_ci		.cfg = cfg,
401362306a36Sopenharmony_ci		.f6i = f6i
401462306a36Sopenharmony_ci	};
401562306a36Sopenharmony_ci
401662306a36Sopenharmony_ci	return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_del_cached_rt, &arg);
401762306a36Sopenharmony_ci}
401862306a36Sopenharmony_ci
401962306a36Sopenharmony_cistatic int ip6_route_del(struct fib6_config *cfg,
402062306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
402162306a36Sopenharmony_ci{
402262306a36Sopenharmony_ci	struct fib6_table *table;
402362306a36Sopenharmony_ci	struct fib6_info *rt;
402462306a36Sopenharmony_ci	struct fib6_node *fn;
402562306a36Sopenharmony_ci	int err = -ESRCH;
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_ci	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
402862306a36Sopenharmony_ci	if (!table) {
402962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "FIB table does not exist");
403062306a36Sopenharmony_ci		return err;
403162306a36Sopenharmony_ci	}
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci	rcu_read_lock();
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_ci	fn = fib6_locate(&table->tb6_root,
403662306a36Sopenharmony_ci			 &cfg->fc_dst, cfg->fc_dst_len,
403762306a36Sopenharmony_ci			 &cfg->fc_src, cfg->fc_src_len,
403862306a36Sopenharmony_ci			 !(cfg->fc_flags & RTF_CACHE));
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_ci	if (fn) {
404162306a36Sopenharmony_ci		for_each_fib6_node_rt_rcu(fn) {
404262306a36Sopenharmony_ci			struct fib6_nh *nh;
404362306a36Sopenharmony_ci
404462306a36Sopenharmony_ci			if (rt->nh && cfg->fc_nh_id &&
404562306a36Sopenharmony_ci			    rt->nh->id != cfg->fc_nh_id)
404662306a36Sopenharmony_ci				continue;
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci			if (cfg->fc_flags & RTF_CACHE) {
404962306a36Sopenharmony_ci				int rc = 0;
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci				if (rt->nh) {
405262306a36Sopenharmony_ci					rc = ip6_del_cached_rt_nh(cfg, rt);
405362306a36Sopenharmony_ci				} else if (cfg->fc_nh_id) {
405462306a36Sopenharmony_ci					continue;
405562306a36Sopenharmony_ci				} else {
405662306a36Sopenharmony_ci					nh = rt->fib6_nh;
405762306a36Sopenharmony_ci					rc = ip6_del_cached_rt(cfg, rt, nh);
405862306a36Sopenharmony_ci				}
405962306a36Sopenharmony_ci				if (rc != -ESRCH) {
406062306a36Sopenharmony_ci					rcu_read_unlock();
406162306a36Sopenharmony_ci					return rc;
406262306a36Sopenharmony_ci				}
406362306a36Sopenharmony_ci				continue;
406462306a36Sopenharmony_ci			}
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci			if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
406762306a36Sopenharmony_ci				continue;
406862306a36Sopenharmony_ci			if (cfg->fc_protocol &&
406962306a36Sopenharmony_ci			    cfg->fc_protocol != rt->fib6_protocol)
407062306a36Sopenharmony_ci				continue;
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci			if (rt->nh) {
407362306a36Sopenharmony_ci				if (!fib6_info_hold_safe(rt))
407462306a36Sopenharmony_ci					continue;
407562306a36Sopenharmony_ci				rcu_read_unlock();
407662306a36Sopenharmony_ci
407762306a36Sopenharmony_ci				return __ip6_del_rt(rt, &cfg->fc_nlinfo);
407862306a36Sopenharmony_ci			}
407962306a36Sopenharmony_ci			if (cfg->fc_nh_id)
408062306a36Sopenharmony_ci				continue;
408162306a36Sopenharmony_ci
408262306a36Sopenharmony_ci			nh = rt->fib6_nh;
408362306a36Sopenharmony_ci			if (cfg->fc_ifindex &&
408462306a36Sopenharmony_ci			    (!nh->fib_nh_dev ||
408562306a36Sopenharmony_ci			     nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
408662306a36Sopenharmony_ci				continue;
408762306a36Sopenharmony_ci			if (cfg->fc_flags & RTF_GATEWAY &&
408862306a36Sopenharmony_ci			    !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
408962306a36Sopenharmony_ci				continue;
409062306a36Sopenharmony_ci			if (!fib6_info_hold_safe(rt))
409162306a36Sopenharmony_ci				continue;
409262306a36Sopenharmony_ci			rcu_read_unlock();
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_ci			/* if gateway was specified only delete the one hop */
409562306a36Sopenharmony_ci			if (cfg->fc_flags & RTF_GATEWAY)
409662306a36Sopenharmony_ci				return __ip6_del_rt(rt, &cfg->fc_nlinfo);
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci			return __ip6_del_rt_siblings(rt, cfg);
409962306a36Sopenharmony_ci		}
410062306a36Sopenharmony_ci	}
410162306a36Sopenharmony_ci	rcu_read_unlock();
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	return err;
410462306a36Sopenharmony_ci}
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_cistatic void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
410762306a36Sopenharmony_ci{
410862306a36Sopenharmony_ci	struct netevent_redirect netevent;
410962306a36Sopenharmony_ci	struct rt6_info *rt, *nrt = NULL;
411062306a36Sopenharmony_ci	struct fib6_result res = {};
411162306a36Sopenharmony_ci	struct ndisc_options ndopts;
411262306a36Sopenharmony_ci	struct inet6_dev *in6_dev;
411362306a36Sopenharmony_ci	struct neighbour *neigh;
411462306a36Sopenharmony_ci	struct rd_msg *msg;
411562306a36Sopenharmony_ci	int optlen, on_link;
411662306a36Sopenharmony_ci	u8 *lladdr;
411762306a36Sopenharmony_ci
411862306a36Sopenharmony_ci	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
411962306a36Sopenharmony_ci	optlen -= sizeof(*msg);
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	if (optlen < 0) {
412262306a36Sopenharmony_ci		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
412362306a36Sopenharmony_ci		return;
412462306a36Sopenharmony_ci	}
412562306a36Sopenharmony_ci
412662306a36Sopenharmony_ci	msg = (struct rd_msg *)icmp6_hdr(skb);
412762306a36Sopenharmony_ci
412862306a36Sopenharmony_ci	if (ipv6_addr_is_multicast(&msg->dest)) {
412962306a36Sopenharmony_ci		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
413062306a36Sopenharmony_ci		return;
413162306a36Sopenharmony_ci	}
413262306a36Sopenharmony_ci
413362306a36Sopenharmony_ci	on_link = 0;
413462306a36Sopenharmony_ci	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
413562306a36Sopenharmony_ci		on_link = 1;
413662306a36Sopenharmony_ci	} else if (ipv6_addr_type(&msg->target) !=
413762306a36Sopenharmony_ci		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
413862306a36Sopenharmony_ci		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
413962306a36Sopenharmony_ci		return;
414062306a36Sopenharmony_ci	}
414162306a36Sopenharmony_ci
414262306a36Sopenharmony_ci	in6_dev = __in6_dev_get(skb->dev);
414362306a36Sopenharmony_ci	if (!in6_dev)
414462306a36Sopenharmony_ci		return;
414562306a36Sopenharmony_ci	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
414662306a36Sopenharmony_ci		return;
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci	/* RFC2461 8.1:
414962306a36Sopenharmony_ci	 *	The IP source address of the Redirect MUST be the same as the current
415062306a36Sopenharmony_ci	 *	first-hop router for the specified ICMP Destination Address.
415162306a36Sopenharmony_ci	 */
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ci	if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
415462306a36Sopenharmony_ci		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
415562306a36Sopenharmony_ci		return;
415662306a36Sopenharmony_ci	}
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci	lladdr = NULL;
415962306a36Sopenharmony_ci	if (ndopts.nd_opts_tgt_lladdr) {
416062306a36Sopenharmony_ci		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
416162306a36Sopenharmony_ci					     skb->dev);
416262306a36Sopenharmony_ci		if (!lladdr) {
416362306a36Sopenharmony_ci			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
416462306a36Sopenharmony_ci			return;
416562306a36Sopenharmony_ci		}
416662306a36Sopenharmony_ci	}
416762306a36Sopenharmony_ci
416862306a36Sopenharmony_ci	rt = (struct rt6_info *) dst;
416962306a36Sopenharmony_ci	if (rt->rt6i_flags & RTF_REJECT) {
417062306a36Sopenharmony_ci		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
417162306a36Sopenharmony_ci		return;
417262306a36Sopenharmony_ci	}
417362306a36Sopenharmony_ci
417462306a36Sopenharmony_ci	/* Redirect received -> path was valid.
417562306a36Sopenharmony_ci	 * Look, redirects are sent only in response to data packets,
417662306a36Sopenharmony_ci	 * so that this nexthop apparently is reachable. --ANK
417762306a36Sopenharmony_ci	 */
417862306a36Sopenharmony_ci	dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
418162306a36Sopenharmony_ci	if (!neigh)
418262306a36Sopenharmony_ci		return;
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_ci	/*
418562306a36Sopenharmony_ci	 *	We have finally decided to accept it.
418662306a36Sopenharmony_ci	 */
418762306a36Sopenharmony_ci
418862306a36Sopenharmony_ci	ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
418962306a36Sopenharmony_ci		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
419062306a36Sopenharmony_ci		     NEIGH_UPDATE_F_OVERRIDE|
419162306a36Sopenharmony_ci		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
419262306a36Sopenharmony_ci				     NEIGH_UPDATE_F_ISROUTER)),
419362306a36Sopenharmony_ci		     NDISC_REDIRECT, &ndopts);
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ci	rcu_read_lock();
419662306a36Sopenharmony_ci	res.f6i = rcu_dereference(rt->from);
419762306a36Sopenharmony_ci	if (!res.f6i)
419862306a36Sopenharmony_ci		goto out;
419962306a36Sopenharmony_ci
420062306a36Sopenharmony_ci	if (res.f6i->nh) {
420162306a36Sopenharmony_ci		struct fib6_nh_match_arg arg = {
420262306a36Sopenharmony_ci			.dev = dst->dev,
420362306a36Sopenharmony_ci			.gw = &rt->rt6i_gateway,
420462306a36Sopenharmony_ci		};
420562306a36Sopenharmony_ci
420662306a36Sopenharmony_ci		nexthop_for_each_fib6_nh(res.f6i->nh,
420762306a36Sopenharmony_ci					 fib6_nh_find_match, &arg);
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci		/* fib6_info uses a nexthop that does not have fib6_nh
421062306a36Sopenharmony_ci		 * using the dst->dev. Should be impossible
421162306a36Sopenharmony_ci		 */
421262306a36Sopenharmony_ci		if (!arg.match)
421362306a36Sopenharmony_ci			goto out;
421462306a36Sopenharmony_ci		res.nh = arg.match;
421562306a36Sopenharmony_ci	} else {
421662306a36Sopenharmony_ci		res.nh = res.f6i->fib6_nh;
421762306a36Sopenharmony_ci	}
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci	res.fib6_flags = res.f6i->fib6_flags;
422062306a36Sopenharmony_ci	res.fib6_type = res.f6i->fib6_type;
422162306a36Sopenharmony_ci	nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
422262306a36Sopenharmony_ci	if (!nrt)
422362306a36Sopenharmony_ci		goto out;
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_ci	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
422662306a36Sopenharmony_ci	if (on_link)
422762306a36Sopenharmony_ci		nrt->rt6i_flags &= ~RTF_GATEWAY;
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
423062306a36Sopenharmony_ci
423162306a36Sopenharmony_ci	/* rt6_insert_exception() will take care of duplicated exceptions */
423262306a36Sopenharmony_ci	if (rt6_insert_exception(nrt, &res)) {
423362306a36Sopenharmony_ci		dst_release_immediate(&nrt->dst);
423462306a36Sopenharmony_ci		goto out;
423562306a36Sopenharmony_ci	}
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_ci	netevent.old = &rt->dst;
423862306a36Sopenharmony_ci	netevent.new = &nrt->dst;
423962306a36Sopenharmony_ci	netevent.daddr = &msg->dest;
424062306a36Sopenharmony_ci	netevent.neigh = neigh;
424162306a36Sopenharmony_ci	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
424262306a36Sopenharmony_ci
424362306a36Sopenharmony_ciout:
424462306a36Sopenharmony_ci	rcu_read_unlock();
424562306a36Sopenharmony_ci	neigh_release(neigh);
424662306a36Sopenharmony_ci}
424762306a36Sopenharmony_ci
424862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
424962306a36Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net,
425062306a36Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
425162306a36Sopenharmony_ci					   const struct in6_addr *gwaddr,
425262306a36Sopenharmony_ci					   struct net_device *dev)
425362306a36Sopenharmony_ci{
425462306a36Sopenharmony_ci	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
425562306a36Sopenharmony_ci	int ifindex = dev->ifindex;
425662306a36Sopenharmony_ci	struct fib6_node *fn;
425762306a36Sopenharmony_ci	struct fib6_info *rt = NULL;
425862306a36Sopenharmony_ci	struct fib6_table *table;
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_ci	table = fib6_get_table(net, tb_id);
426162306a36Sopenharmony_ci	if (!table)
426262306a36Sopenharmony_ci		return NULL;
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	rcu_read_lock();
426562306a36Sopenharmony_ci	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
426662306a36Sopenharmony_ci	if (!fn)
426762306a36Sopenharmony_ci		goto out;
426862306a36Sopenharmony_ci
426962306a36Sopenharmony_ci	for_each_fib6_node_rt_rcu(fn) {
427062306a36Sopenharmony_ci		/* these routes do not use nexthops */
427162306a36Sopenharmony_ci		if (rt->nh)
427262306a36Sopenharmony_ci			continue;
427362306a36Sopenharmony_ci		if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex)
427462306a36Sopenharmony_ci			continue;
427562306a36Sopenharmony_ci		if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
427662306a36Sopenharmony_ci		    !rt->fib6_nh->fib_nh_gw_family)
427762306a36Sopenharmony_ci			continue;
427862306a36Sopenharmony_ci		if (!ipv6_addr_equal(&rt->fib6_nh->fib_nh_gw6, gwaddr))
427962306a36Sopenharmony_ci			continue;
428062306a36Sopenharmony_ci		if (!fib6_info_hold_safe(rt))
428162306a36Sopenharmony_ci			continue;
428262306a36Sopenharmony_ci		break;
428362306a36Sopenharmony_ci	}
428462306a36Sopenharmony_ciout:
428562306a36Sopenharmony_ci	rcu_read_unlock();
428662306a36Sopenharmony_ci	return rt;
428762306a36Sopenharmony_ci}
428862306a36Sopenharmony_ci
428962306a36Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net,
429062306a36Sopenharmony_ci					   const struct in6_addr *prefix, int prefixlen,
429162306a36Sopenharmony_ci					   const struct in6_addr *gwaddr,
429262306a36Sopenharmony_ci					   struct net_device *dev,
429362306a36Sopenharmony_ci					   unsigned int pref)
429462306a36Sopenharmony_ci{
429562306a36Sopenharmony_ci	struct fib6_config cfg = {
429662306a36Sopenharmony_ci		.fc_metric	= IP6_RT_PRIO_USER,
429762306a36Sopenharmony_ci		.fc_ifindex	= dev->ifindex,
429862306a36Sopenharmony_ci		.fc_dst_len	= prefixlen,
429962306a36Sopenharmony_ci		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
430062306a36Sopenharmony_ci				  RTF_UP | RTF_PREF(pref),
430162306a36Sopenharmony_ci		.fc_protocol = RTPROT_RA,
430262306a36Sopenharmony_ci		.fc_type = RTN_UNICAST,
430362306a36Sopenharmony_ci		.fc_nlinfo.portid = 0,
430462306a36Sopenharmony_ci		.fc_nlinfo.nlh = NULL,
430562306a36Sopenharmony_ci		.fc_nlinfo.nl_net = net,
430662306a36Sopenharmony_ci	};
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci	cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
430962306a36Sopenharmony_ci	cfg.fc_dst = *prefix;
431062306a36Sopenharmony_ci	cfg.fc_gateway = *gwaddr;
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci	/* We should treat it as a default route if prefix length is 0. */
431362306a36Sopenharmony_ci	if (!prefixlen)
431462306a36Sopenharmony_ci		cfg.fc_flags |= RTF_DEFAULT;
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	ip6_route_add(&cfg, GFP_ATOMIC, NULL);
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
431962306a36Sopenharmony_ci}
432062306a36Sopenharmony_ci#endif
432162306a36Sopenharmony_ci
432262306a36Sopenharmony_cistruct fib6_info *rt6_get_dflt_router(struct net *net,
432362306a36Sopenharmony_ci				     const struct in6_addr *addr,
432462306a36Sopenharmony_ci				     struct net_device *dev)
432562306a36Sopenharmony_ci{
432662306a36Sopenharmony_ci	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
432762306a36Sopenharmony_ci	struct fib6_info *rt;
432862306a36Sopenharmony_ci	struct fib6_table *table;
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci	table = fib6_get_table(net, tb_id);
433162306a36Sopenharmony_ci	if (!table)
433262306a36Sopenharmony_ci		return NULL;
433362306a36Sopenharmony_ci
433462306a36Sopenharmony_ci	rcu_read_lock();
433562306a36Sopenharmony_ci	for_each_fib6_node_rt_rcu(&table->tb6_root) {
433662306a36Sopenharmony_ci		struct fib6_nh *nh;
433762306a36Sopenharmony_ci
433862306a36Sopenharmony_ci		/* RA routes do not use nexthops */
433962306a36Sopenharmony_ci		if (rt->nh)
434062306a36Sopenharmony_ci			continue;
434162306a36Sopenharmony_ci
434262306a36Sopenharmony_ci		nh = rt->fib6_nh;
434362306a36Sopenharmony_ci		if (dev == nh->fib_nh_dev &&
434462306a36Sopenharmony_ci		    ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
434562306a36Sopenharmony_ci		    ipv6_addr_equal(&nh->fib_nh_gw6, addr))
434662306a36Sopenharmony_ci			break;
434762306a36Sopenharmony_ci	}
434862306a36Sopenharmony_ci	if (rt && !fib6_info_hold_safe(rt))
434962306a36Sopenharmony_ci		rt = NULL;
435062306a36Sopenharmony_ci	rcu_read_unlock();
435162306a36Sopenharmony_ci	return rt;
435262306a36Sopenharmony_ci}
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_cistruct fib6_info *rt6_add_dflt_router(struct net *net,
435562306a36Sopenharmony_ci				     const struct in6_addr *gwaddr,
435662306a36Sopenharmony_ci				     struct net_device *dev,
435762306a36Sopenharmony_ci				     unsigned int pref,
435862306a36Sopenharmony_ci				     u32 defrtr_usr_metric)
435962306a36Sopenharmony_ci{
436062306a36Sopenharmony_ci	struct fib6_config cfg = {
436162306a36Sopenharmony_ci		.fc_table	= l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
436262306a36Sopenharmony_ci		.fc_metric	= defrtr_usr_metric,
436362306a36Sopenharmony_ci		.fc_ifindex	= dev->ifindex,
436462306a36Sopenharmony_ci		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
436562306a36Sopenharmony_ci				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
436662306a36Sopenharmony_ci		.fc_protocol = RTPROT_RA,
436762306a36Sopenharmony_ci		.fc_type = RTN_UNICAST,
436862306a36Sopenharmony_ci		.fc_nlinfo.portid = 0,
436962306a36Sopenharmony_ci		.fc_nlinfo.nlh = NULL,
437062306a36Sopenharmony_ci		.fc_nlinfo.nl_net = net,
437162306a36Sopenharmony_ci	};
437262306a36Sopenharmony_ci
437362306a36Sopenharmony_ci	cfg.fc_gateway = *gwaddr;
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ci	if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
437662306a36Sopenharmony_ci		struct fib6_table *table;
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ci		table = fib6_get_table(dev_net(dev), cfg.fc_table);
437962306a36Sopenharmony_ci		if (table)
438062306a36Sopenharmony_ci			table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
438162306a36Sopenharmony_ci	}
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci	return rt6_get_dflt_router(net, gwaddr, dev);
438462306a36Sopenharmony_ci}
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_cistatic void __rt6_purge_dflt_routers(struct net *net,
438762306a36Sopenharmony_ci				     struct fib6_table *table)
438862306a36Sopenharmony_ci{
438962306a36Sopenharmony_ci	struct fib6_info *rt;
439062306a36Sopenharmony_ci
439162306a36Sopenharmony_cirestart:
439262306a36Sopenharmony_ci	rcu_read_lock();
439362306a36Sopenharmony_ci	for_each_fib6_node_rt_rcu(&table->tb6_root) {
439462306a36Sopenharmony_ci		struct net_device *dev = fib6_info_nh_dev(rt);
439562306a36Sopenharmony_ci		struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
439662306a36Sopenharmony_ci
439762306a36Sopenharmony_ci		if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
439862306a36Sopenharmony_ci		    (!idev || idev->cnf.accept_ra != 2) &&
439962306a36Sopenharmony_ci		    fib6_info_hold_safe(rt)) {
440062306a36Sopenharmony_ci			rcu_read_unlock();
440162306a36Sopenharmony_ci			ip6_del_rt(net, rt, false);
440262306a36Sopenharmony_ci			goto restart;
440362306a36Sopenharmony_ci		}
440462306a36Sopenharmony_ci	}
440562306a36Sopenharmony_ci	rcu_read_unlock();
440662306a36Sopenharmony_ci
440762306a36Sopenharmony_ci	table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
440862306a36Sopenharmony_ci}
440962306a36Sopenharmony_ci
441062306a36Sopenharmony_civoid rt6_purge_dflt_routers(struct net *net)
441162306a36Sopenharmony_ci{
441262306a36Sopenharmony_ci	struct fib6_table *table;
441362306a36Sopenharmony_ci	struct hlist_head *head;
441462306a36Sopenharmony_ci	unsigned int h;
441562306a36Sopenharmony_ci
441662306a36Sopenharmony_ci	rcu_read_lock();
441762306a36Sopenharmony_ci
441862306a36Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
441962306a36Sopenharmony_ci		head = &net->ipv6.fib_table_hash[h];
442062306a36Sopenharmony_ci		hlist_for_each_entry_rcu(table, head, tb6_hlist) {
442162306a36Sopenharmony_ci			if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
442262306a36Sopenharmony_ci				__rt6_purge_dflt_routers(net, table);
442362306a36Sopenharmony_ci		}
442462306a36Sopenharmony_ci	}
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_ci	rcu_read_unlock();
442762306a36Sopenharmony_ci}
442862306a36Sopenharmony_ci
442962306a36Sopenharmony_cistatic void rtmsg_to_fib6_config(struct net *net,
443062306a36Sopenharmony_ci				 struct in6_rtmsg *rtmsg,
443162306a36Sopenharmony_ci				 struct fib6_config *cfg)
443262306a36Sopenharmony_ci{
443362306a36Sopenharmony_ci	*cfg = (struct fib6_config){
443462306a36Sopenharmony_ci		.fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
443562306a36Sopenharmony_ci			 : RT6_TABLE_MAIN,
443662306a36Sopenharmony_ci		.fc_ifindex = rtmsg->rtmsg_ifindex,
443762306a36Sopenharmony_ci		.fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
443862306a36Sopenharmony_ci		.fc_expires = rtmsg->rtmsg_info,
443962306a36Sopenharmony_ci		.fc_dst_len = rtmsg->rtmsg_dst_len,
444062306a36Sopenharmony_ci		.fc_src_len = rtmsg->rtmsg_src_len,
444162306a36Sopenharmony_ci		.fc_flags = rtmsg->rtmsg_flags,
444262306a36Sopenharmony_ci		.fc_type = rtmsg->rtmsg_type,
444362306a36Sopenharmony_ci
444462306a36Sopenharmony_ci		.fc_nlinfo.nl_net = net,
444562306a36Sopenharmony_ci
444662306a36Sopenharmony_ci		.fc_dst = rtmsg->rtmsg_dst,
444762306a36Sopenharmony_ci		.fc_src = rtmsg->rtmsg_src,
444862306a36Sopenharmony_ci		.fc_gateway = rtmsg->rtmsg_gateway,
444962306a36Sopenharmony_ci	};
445062306a36Sopenharmony_ci}
445162306a36Sopenharmony_ci
445262306a36Sopenharmony_ciint ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg)
445362306a36Sopenharmony_ci{
445462306a36Sopenharmony_ci	struct fib6_config cfg;
445562306a36Sopenharmony_ci	int err;
445662306a36Sopenharmony_ci
445762306a36Sopenharmony_ci	if (cmd != SIOCADDRT && cmd != SIOCDELRT)
445862306a36Sopenharmony_ci		return -EINVAL;
445962306a36Sopenharmony_ci	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
446062306a36Sopenharmony_ci		return -EPERM;
446162306a36Sopenharmony_ci
446262306a36Sopenharmony_ci	rtmsg_to_fib6_config(net, rtmsg, &cfg);
446362306a36Sopenharmony_ci
446462306a36Sopenharmony_ci	rtnl_lock();
446562306a36Sopenharmony_ci	switch (cmd) {
446662306a36Sopenharmony_ci	case SIOCADDRT:
446762306a36Sopenharmony_ci		err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
446862306a36Sopenharmony_ci		break;
446962306a36Sopenharmony_ci	case SIOCDELRT:
447062306a36Sopenharmony_ci		err = ip6_route_del(&cfg, NULL);
447162306a36Sopenharmony_ci		break;
447262306a36Sopenharmony_ci	}
447362306a36Sopenharmony_ci	rtnl_unlock();
447462306a36Sopenharmony_ci	return err;
447562306a36Sopenharmony_ci}
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_ci/*
447862306a36Sopenharmony_ci *	Drop the packet on the floor
447962306a36Sopenharmony_ci */
448062306a36Sopenharmony_ci
448162306a36Sopenharmony_cistatic int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
448262306a36Sopenharmony_ci{
448362306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
448462306a36Sopenharmony_ci	struct net *net = dev_net(dst->dev);
448562306a36Sopenharmony_ci	struct inet6_dev *idev;
448662306a36Sopenharmony_ci	SKB_DR(reason);
448762306a36Sopenharmony_ci	int type;
448862306a36Sopenharmony_ci
448962306a36Sopenharmony_ci	if (netif_is_l3_master(skb->dev) ||
449062306a36Sopenharmony_ci	    dst->dev == net->loopback_dev)
449162306a36Sopenharmony_ci		idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
449262306a36Sopenharmony_ci	else
449362306a36Sopenharmony_ci		idev = ip6_dst_idev(dst);
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_ci	switch (ipstats_mib_noroutes) {
449662306a36Sopenharmony_ci	case IPSTATS_MIB_INNOROUTES:
449762306a36Sopenharmony_ci		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
449862306a36Sopenharmony_ci		if (type == IPV6_ADDR_ANY) {
449962306a36Sopenharmony_ci			SKB_DR_SET(reason, IP_INADDRERRORS);
450062306a36Sopenharmony_ci			IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
450162306a36Sopenharmony_ci			break;
450262306a36Sopenharmony_ci		}
450362306a36Sopenharmony_ci		SKB_DR_SET(reason, IP_INNOROUTES);
450462306a36Sopenharmony_ci		fallthrough;
450562306a36Sopenharmony_ci	case IPSTATS_MIB_OUTNOROUTES:
450662306a36Sopenharmony_ci		SKB_DR_OR(reason, IP_OUTNOROUTES);
450762306a36Sopenharmony_ci		IP6_INC_STATS(net, idev, ipstats_mib_noroutes);
450862306a36Sopenharmony_ci		break;
450962306a36Sopenharmony_ci	}
451062306a36Sopenharmony_ci
451162306a36Sopenharmony_ci	/* Start over by dropping the dst for l3mdev case */
451262306a36Sopenharmony_ci	if (netif_is_l3_master(skb->dev))
451362306a36Sopenharmony_ci		skb_dst_drop(skb);
451462306a36Sopenharmony_ci
451562306a36Sopenharmony_ci	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
451662306a36Sopenharmony_ci	kfree_skb_reason(skb, reason);
451762306a36Sopenharmony_ci	return 0;
451862306a36Sopenharmony_ci}
451962306a36Sopenharmony_ci
452062306a36Sopenharmony_cistatic int ip6_pkt_discard(struct sk_buff *skb)
452162306a36Sopenharmony_ci{
452262306a36Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
452362306a36Sopenharmony_ci}
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_cistatic int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
452662306a36Sopenharmony_ci{
452762306a36Sopenharmony_ci	skb->dev = skb_dst(skb)->dev;
452862306a36Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
452962306a36Sopenharmony_ci}
453062306a36Sopenharmony_ci
453162306a36Sopenharmony_cistatic int ip6_pkt_prohibit(struct sk_buff *skb)
453262306a36Sopenharmony_ci{
453362306a36Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
453462306a36Sopenharmony_ci}
453562306a36Sopenharmony_ci
453662306a36Sopenharmony_cistatic int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
453762306a36Sopenharmony_ci{
453862306a36Sopenharmony_ci	skb->dev = skb_dst(skb)->dev;
453962306a36Sopenharmony_ci	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
454062306a36Sopenharmony_ci}
454162306a36Sopenharmony_ci
454262306a36Sopenharmony_ci/*
454362306a36Sopenharmony_ci *	Allocate a dst for local (unicast / anycast) address.
454462306a36Sopenharmony_ci */
454562306a36Sopenharmony_ci
454662306a36Sopenharmony_cistruct fib6_info *addrconf_f6i_alloc(struct net *net,
454762306a36Sopenharmony_ci				     struct inet6_dev *idev,
454862306a36Sopenharmony_ci				     const struct in6_addr *addr,
454962306a36Sopenharmony_ci				     bool anycast, gfp_t gfp_flags,
455062306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
455162306a36Sopenharmony_ci{
455262306a36Sopenharmony_ci	struct fib6_config cfg = {
455362306a36Sopenharmony_ci		.fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
455462306a36Sopenharmony_ci		.fc_ifindex = idev->dev->ifindex,
455562306a36Sopenharmony_ci		.fc_flags = RTF_UP | RTF_NONEXTHOP,
455662306a36Sopenharmony_ci		.fc_dst = *addr,
455762306a36Sopenharmony_ci		.fc_dst_len = 128,
455862306a36Sopenharmony_ci		.fc_protocol = RTPROT_KERNEL,
455962306a36Sopenharmony_ci		.fc_nlinfo.nl_net = net,
456062306a36Sopenharmony_ci		.fc_ignore_dev_down = true,
456162306a36Sopenharmony_ci	};
456262306a36Sopenharmony_ci	struct fib6_info *f6i;
456362306a36Sopenharmony_ci
456462306a36Sopenharmony_ci	if (anycast) {
456562306a36Sopenharmony_ci		cfg.fc_type = RTN_ANYCAST;
456662306a36Sopenharmony_ci		cfg.fc_flags |= RTF_ANYCAST;
456762306a36Sopenharmony_ci	} else {
456862306a36Sopenharmony_ci		cfg.fc_type = RTN_LOCAL;
456962306a36Sopenharmony_ci		cfg.fc_flags |= RTF_LOCAL;
457062306a36Sopenharmony_ci	}
457162306a36Sopenharmony_ci
457262306a36Sopenharmony_ci	f6i = ip6_route_info_create(&cfg, gfp_flags, extack);
457362306a36Sopenharmony_ci	if (!IS_ERR(f6i)) {
457462306a36Sopenharmony_ci		f6i->dst_nocount = true;
457562306a36Sopenharmony_ci
457662306a36Sopenharmony_ci		if (!anycast &&
457762306a36Sopenharmony_ci		    (net->ipv6.devconf_all->disable_policy ||
457862306a36Sopenharmony_ci		     idev->cnf.disable_policy))
457962306a36Sopenharmony_ci			f6i->dst_nopolicy = true;
458062306a36Sopenharmony_ci	}
458162306a36Sopenharmony_ci
458262306a36Sopenharmony_ci	return f6i;
458362306a36Sopenharmony_ci}
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci/* remove deleted ip from prefsrc entries */
458662306a36Sopenharmony_cistruct arg_dev_net_ip {
458762306a36Sopenharmony_ci	struct net *net;
458862306a36Sopenharmony_ci	struct in6_addr *addr;
458962306a36Sopenharmony_ci};
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_cistatic int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
459262306a36Sopenharmony_ci{
459362306a36Sopenharmony_ci	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
459462306a36Sopenharmony_ci	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
459562306a36Sopenharmony_ci
459662306a36Sopenharmony_ci	if (!rt->nh &&
459762306a36Sopenharmony_ci	    rt != net->ipv6.fib6_null_entry &&
459862306a36Sopenharmony_ci	    ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr) &&
459962306a36Sopenharmony_ci	    !ipv6_chk_addr(net, addr, rt->fib6_nh->fib_nh_dev, 0)) {
460062306a36Sopenharmony_ci		spin_lock_bh(&rt6_exception_lock);
460162306a36Sopenharmony_ci		/* remove prefsrc entry */
460262306a36Sopenharmony_ci		rt->fib6_prefsrc.plen = 0;
460362306a36Sopenharmony_ci		spin_unlock_bh(&rt6_exception_lock);
460462306a36Sopenharmony_ci	}
460562306a36Sopenharmony_ci	return 0;
460662306a36Sopenharmony_ci}
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_civoid rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
460962306a36Sopenharmony_ci{
461062306a36Sopenharmony_ci	struct net *net = dev_net(ifp->idev->dev);
461162306a36Sopenharmony_ci	struct arg_dev_net_ip adni = {
461262306a36Sopenharmony_ci		.net = net,
461362306a36Sopenharmony_ci		.addr = &ifp->addr,
461462306a36Sopenharmony_ci	};
461562306a36Sopenharmony_ci	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
461662306a36Sopenharmony_ci}
461762306a36Sopenharmony_ci
461862306a36Sopenharmony_ci#define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT)
461962306a36Sopenharmony_ci
462062306a36Sopenharmony_ci/* Remove routers and update dst entries when gateway turn into host. */
462162306a36Sopenharmony_cistatic int fib6_clean_tohost(struct fib6_info *rt, void *arg)
462262306a36Sopenharmony_ci{
462362306a36Sopenharmony_ci	struct in6_addr *gateway = (struct in6_addr *)arg;
462462306a36Sopenharmony_ci	struct fib6_nh *nh;
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ci	/* RA routes do not use nexthops */
462762306a36Sopenharmony_ci	if (rt->nh)
462862306a36Sopenharmony_ci		return 0;
462962306a36Sopenharmony_ci
463062306a36Sopenharmony_ci	nh = rt->fib6_nh;
463162306a36Sopenharmony_ci	if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
463262306a36Sopenharmony_ci	    nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6))
463362306a36Sopenharmony_ci		return -1;
463462306a36Sopenharmony_ci
463562306a36Sopenharmony_ci	/* Further clean up cached routes in exception table.
463662306a36Sopenharmony_ci	 * This is needed because cached route may have a different
463762306a36Sopenharmony_ci	 * gateway than its 'parent' in the case of an ip redirect.
463862306a36Sopenharmony_ci	 */
463962306a36Sopenharmony_ci	fib6_nh_exceptions_clean_tohost(nh, gateway);
464062306a36Sopenharmony_ci
464162306a36Sopenharmony_ci	return 0;
464262306a36Sopenharmony_ci}
464362306a36Sopenharmony_ci
464462306a36Sopenharmony_civoid rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
464562306a36Sopenharmony_ci{
464662306a36Sopenharmony_ci	fib6_clean_all(net, fib6_clean_tohost, gateway);
464762306a36Sopenharmony_ci}
464862306a36Sopenharmony_ci
464962306a36Sopenharmony_cistruct arg_netdev_event {
465062306a36Sopenharmony_ci	const struct net_device *dev;
465162306a36Sopenharmony_ci	union {
465262306a36Sopenharmony_ci		unsigned char nh_flags;
465362306a36Sopenharmony_ci		unsigned long event;
465462306a36Sopenharmony_ci	};
465562306a36Sopenharmony_ci};
465662306a36Sopenharmony_ci
465762306a36Sopenharmony_cistatic struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
465862306a36Sopenharmony_ci{
465962306a36Sopenharmony_ci	struct fib6_info *iter;
466062306a36Sopenharmony_ci	struct fib6_node *fn;
466162306a36Sopenharmony_ci
466262306a36Sopenharmony_ci	fn = rcu_dereference_protected(rt->fib6_node,
466362306a36Sopenharmony_ci			lockdep_is_held(&rt->fib6_table->tb6_lock));
466462306a36Sopenharmony_ci	iter = rcu_dereference_protected(fn->leaf,
466562306a36Sopenharmony_ci			lockdep_is_held(&rt->fib6_table->tb6_lock));
466662306a36Sopenharmony_ci	while (iter) {
466762306a36Sopenharmony_ci		if (iter->fib6_metric == rt->fib6_metric &&
466862306a36Sopenharmony_ci		    rt6_qualify_for_ecmp(iter))
466962306a36Sopenharmony_ci			return iter;
467062306a36Sopenharmony_ci		iter = rcu_dereference_protected(iter->fib6_next,
467162306a36Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock));
467262306a36Sopenharmony_ci	}
467362306a36Sopenharmony_ci
467462306a36Sopenharmony_ci	return NULL;
467562306a36Sopenharmony_ci}
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_ci/* only called for fib entries with builtin fib6_nh */
467862306a36Sopenharmony_cistatic bool rt6_is_dead(const struct fib6_info *rt)
467962306a36Sopenharmony_ci{
468062306a36Sopenharmony_ci	if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD ||
468162306a36Sopenharmony_ci	    (rt->fib6_nh->fib_nh_flags & RTNH_F_LINKDOWN &&
468262306a36Sopenharmony_ci	     ip6_ignore_linkdown(rt->fib6_nh->fib_nh_dev)))
468362306a36Sopenharmony_ci		return true;
468462306a36Sopenharmony_ci
468562306a36Sopenharmony_ci	return false;
468662306a36Sopenharmony_ci}
468762306a36Sopenharmony_ci
468862306a36Sopenharmony_cistatic int rt6_multipath_total_weight(const struct fib6_info *rt)
468962306a36Sopenharmony_ci{
469062306a36Sopenharmony_ci	struct fib6_info *iter;
469162306a36Sopenharmony_ci	int total = 0;
469262306a36Sopenharmony_ci
469362306a36Sopenharmony_ci	if (!rt6_is_dead(rt))
469462306a36Sopenharmony_ci		total += rt->fib6_nh->fib_nh_weight;
469562306a36Sopenharmony_ci
469662306a36Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
469762306a36Sopenharmony_ci		if (!rt6_is_dead(iter))
469862306a36Sopenharmony_ci			total += iter->fib6_nh->fib_nh_weight;
469962306a36Sopenharmony_ci	}
470062306a36Sopenharmony_ci
470162306a36Sopenharmony_ci	return total;
470262306a36Sopenharmony_ci}
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_cistatic void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
470562306a36Sopenharmony_ci{
470662306a36Sopenharmony_ci	int upper_bound = -1;
470762306a36Sopenharmony_ci
470862306a36Sopenharmony_ci	if (!rt6_is_dead(rt)) {
470962306a36Sopenharmony_ci		*weight += rt->fib6_nh->fib_nh_weight;
471062306a36Sopenharmony_ci		upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
471162306a36Sopenharmony_ci						    total) - 1;
471262306a36Sopenharmony_ci	}
471362306a36Sopenharmony_ci	atomic_set(&rt->fib6_nh->fib_nh_upper_bound, upper_bound);
471462306a36Sopenharmony_ci}
471562306a36Sopenharmony_ci
471662306a36Sopenharmony_cistatic void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
471762306a36Sopenharmony_ci{
471862306a36Sopenharmony_ci	struct fib6_info *iter;
471962306a36Sopenharmony_ci	int weight = 0;
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_ci	rt6_upper_bound_set(rt, &weight, total);
472262306a36Sopenharmony_ci
472362306a36Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
472462306a36Sopenharmony_ci		rt6_upper_bound_set(iter, &weight, total);
472562306a36Sopenharmony_ci}
472662306a36Sopenharmony_ci
472762306a36Sopenharmony_civoid rt6_multipath_rebalance(struct fib6_info *rt)
472862306a36Sopenharmony_ci{
472962306a36Sopenharmony_ci	struct fib6_info *first;
473062306a36Sopenharmony_ci	int total;
473162306a36Sopenharmony_ci
473262306a36Sopenharmony_ci	/* In case the entire multipath route was marked for flushing,
473362306a36Sopenharmony_ci	 * then there is no need to rebalance upon the removal of every
473462306a36Sopenharmony_ci	 * sibling route.
473562306a36Sopenharmony_ci	 */
473662306a36Sopenharmony_ci	if (!rt->fib6_nsiblings || rt->should_flush)
473762306a36Sopenharmony_ci		return;
473862306a36Sopenharmony_ci
473962306a36Sopenharmony_ci	/* During lookup routes are evaluated in order, so we need to
474062306a36Sopenharmony_ci	 * make sure upper bounds are assigned from the first sibling
474162306a36Sopenharmony_ci	 * onwards.
474262306a36Sopenharmony_ci	 */
474362306a36Sopenharmony_ci	first = rt6_multipath_first_sibling(rt);
474462306a36Sopenharmony_ci	if (WARN_ON_ONCE(!first))
474562306a36Sopenharmony_ci		return;
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci	total = rt6_multipath_total_weight(first);
474862306a36Sopenharmony_ci	rt6_multipath_upper_bound_set(first, total);
474962306a36Sopenharmony_ci}
475062306a36Sopenharmony_ci
475162306a36Sopenharmony_cistatic int fib6_ifup(struct fib6_info *rt, void *p_arg)
475262306a36Sopenharmony_ci{
475362306a36Sopenharmony_ci	const struct arg_netdev_event *arg = p_arg;
475462306a36Sopenharmony_ci	struct net *net = dev_net(arg->dev);
475562306a36Sopenharmony_ci
475662306a36Sopenharmony_ci	if (rt != net->ipv6.fib6_null_entry && !rt->nh &&
475762306a36Sopenharmony_ci	    rt->fib6_nh->fib_nh_dev == arg->dev) {
475862306a36Sopenharmony_ci		rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags;
475962306a36Sopenharmony_ci		fib6_update_sernum_upto_root(net, rt);
476062306a36Sopenharmony_ci		rt6_multipath_rebalance(rt);
476162306a36Sopenharmony_ci	}
476262306a36Sopenharmony_ci
476362306a36Sopenharmony_ci	return 0;
476462306a36Sopenharmony_ci}
476562306a36Sopenharmony_ci
476662306a36Sopenharmony_civoid rt6_sync_up(struct net_device *dev, unsigned char nh_flags)
476762306a36Sopenharmony_ci{
476862306a36Sopenharmony_ci	struct arg_netdev_event arg = {
476962306a36Sopenharmony_ci		.dev = dev,
477062306a36Sopenharmony_ci		{
477162306a36Sopenharmony_ci			.nh_flags = nh_flags,
477262306a36Sopenharmony_ci		},
477362306a36Sopenharmony_ci	};
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
477662306a36Sopenharmony_ci		arg.nh_flags |= RTNH_F_LINKDOWN;
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_ci	fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
477962306a36Sopenharmony_ci}
478062306a36Sopenharmony_ci
478162306a36Sopenharmony_ci/* only called for fib entries with inline fib6_nh */
478262306a36Sopenharmony_cistatic bool rt6_multipath_uses_dev(const struct fib6_info *rt,
478362306a36Sopenharmony_ci				   const struct net_device *dev)
478462306a36Sopenharmony_ci{
478562306a36Sopenharmony_ci	struct fib6_info *iter;
478662306a36Sopenharmony_ci
478762306a36Sopenharmony_ci	if (rt->fib6_nh->fib_nh_dev == dev)
478862306a36Sopenharmony_ci		return true;
478962306a36Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
479062306a36Sopenharmony_ci		if (iter->fib6_nh->fib_nh_dev == dev)
479162306a36Sopenharmony_ci			return true;
479262306a36Sopenharmony_ci
479362306a36Sopenharmony_ci	return false;
479462306a36Sopenharmony_ci}
479562306a36Sopenharmony_ci
479662306a36Sopenharmony_cistatic void rt6_multipath_flush(struct fib6_info *rt)
479762306a36Sopenharmony_ci{
479862306a36Sopenharmony_ci	struct fib6_info *iter;
479962306a36Sopenharmony_ci
480062306a36Sopenharmony_ci	rt->should_flush = 1;
480162306a36Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
480262306a36Sopenharmony_ci		iter->should_flush = 1;
480362306a36Sopenharmony_ci}
480462306a36Sopenharmony_ci
480562306a36Sopenharmony_cistatic unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
480662306a36Sopenharmony_ci					     const struct net_device *down_dev)
480762306a36Sopenharmony_ci{
480862306a36Sopenharmony_ci	struct fib6_info *iter;
480962306a36Sopenharmony_ci	unsigned int dead = 0;
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	if (rt->fib6_nh->fib_nh_dev == down_dev ||
481262306a36Sopenharmony_ci	    rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
481362306a36Sopenharmony_ci		dead++;
481462306a36Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
481562306a36Sopenharmony_ci		if (iter->fib6_nh->fib_nh_dev == down_dev ||
481662306a36Sopenharmony_ci		    iter->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
481762306a36Sopenharmony_ci			dead++;
481862306a36Sopenharmony_ci
481962306a36Sopenharmony_ci	return dead;
482062306a36Sopenharmony_ci}
482162306a36Sopenharmony_ci
482262306a36Sopenharmony_cistatic void rt6_multipath_nh_flags_set(struct fib6_info *rt,
482362306a36Sopenharmony_ci				       const struct net_device *dev,
482462306a36Sopenharmony_ci				       unsigned char nh_flags)
482562306a36Sopenharmony_ci{
482662306a36Sopenharmony_ci	struct fib6_info *iter;
482762306a36Sopenharmony_ci
482862306a36Sopenharmony_ci	if (rt->fib6_nh->fib_nh_dev == dev)
482962306a36Sopenharmony_ci		rt->fib6_nh->fib_nh_flags |= nh_flags;
483062306a36Sopenharmony_ci	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
483162306a36Sopenharmony_ci		if (iter->fib6_nh->fib_nh_dev == dev)
483262306a36Sopenharmony_ci			iter->fib6_nh->fib_nh_flags |= nh_flags;
483362306a36Sopenharmony_ci}
483462306a36Sopenharmony_ci
483562306a36Sopenharmony_ci/* called with write lock held for table with rt */
483662306a36Sopenharmony_cistatic int fib6_ifdown(struct fib6_info *rt, void *p_arg)
483762306a36Sopenharmony_ci{
483862306a36Sopenharmony_ci	const struct arg_netdev_event *arg = p_arg;
483962306a36Sopenharmony_ci	const struct net_device *dev = arg->dev;
484062306a36Sopenharmony_ci	struct net *net = dev_net(dev);
484162306a36Sopenharmony_ci
484262306a36Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry || rt->nh)
484362306a36Sopenharmony_ci		return 0;
484462306a36Sopenharmony_ci
484562306a36Sopenharmony_ci	switch (arg->event) {
484662306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
484762306a36Sopenharmony_ci		return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
484862306a36Sopenharmony_ci	case NETDEV_DOWN:
484962306a36Sopenharmony_ci		if (rt->should_flush)
485062306a36Sopenharmony_ci			return -1;
485162306a36Sopenharmony_ci		if (!rt->fib6_nsiblings)
485262306a36Sopenharmony_ci			return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
485362306a36Sopenharmony_ci		if (rt6_multipath_uses_dev(rt, dev)) {
485462306a36Sopenharmony_ci			unsigned int count;
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_ci			count = rt6_multipath_dead_count(rt, dev);
485762306a36Sopenharmony_ci			if (rt->fib6_nsiblings + 1 == count) {
485862306a36Sopenharmony_ci				rt6_multipath_flush(rt);
485962306a36Sopenharmony_ci				return -1;
486062306a36Sopenharmony_ci			}
486162306a36Sopenharmony_ci			rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
486262306a36Sopenharmony_ci						   RTNH_F_LINKDOWN);
486362306a36Sopenharmony_ci			fib6_update_sernum(net, rt);
486462306a36Sopenharmony_ci			rt6_multipath_rebalance(rt);
486562306a36Sopenharmony_ci		}
486662306a36Sopenharmony_ci		return -2;
486762306a36Sopenharmony_ci	case NETDEV_CHANGE:
486862306a36Sopenharmony_ci		if (rt->fib6_nh->fib_nh_dev != dev ||
486962306a36Sopenharmony_ci		    rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
487062306a36Sopenharmony_ci			break;
487162306a36Sopenharmony_ci		rt->fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
487262306a36Sopenharmony_ci		rt6_multipath_rebalance(rt);
487362306a36Sopenharmony_ci		break;
487462306a36Sopenharmony_ci	}
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci	return 0;
487762306a36Sopenharmony_ci}
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_civoid rt6_sync_down_dev(struct net_device *dev, unsigned long event)
488062306a36Sopenharmony_ci{
488162306a36Sopenharmony_ci	struct arg_netdev_event arg = {
488262306a36Sopenharmony_ci		.dev = dev,
488362306a36Sopenharmony_ci		{
488462306a36Sopenharmony_ci			.event = event,
488562306a36Sopenharmony_ci		},
488662306a36Sopenharmony_ci	};
488762306a36Sopenharmony_ci	struct net *net = dev_net(dev);
488862306a36Sopenharmony_ci
488962306a36Sopenharmony_ci	if (net->ipv6.sysctl.skip_notify_on_dev_down)
489062306a36Sopenharmony_ci		fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
489162306a36Sopenharmony_ci	else
489262306a36Sopenharmony_ci		fib6_clean_all(net, fib6_ifdown, &arg);
489362306a36Sopenharmony_ci}
489462306a36Sopenharmony_ci
489562306a36Sopenharmony_civoid rt6_disable_ip(struct net_device *dev, unsigned long event)
489662306a36Sopenharmony_ci{
489762306a36Sopenharmony_ci	rt6_sync_down_dev(dev, event);
489862306a36Sopenharmony_ci	rt6_uncached_list_flush_dev(dev);
489962306a36Sopenharmony_ci	neigh_ifdown(&nd_tbl, dev);
490062306a36Sopenharmony_ci}
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_cistruct rt6_mtu_change_arg {
490362306a36Sopenharmony_ci	struct net_device *dev;
490462306a36Sopenharmony_ci	unsigned int mtu;
490562306a36Sopenharmony_ci	struct fib6_info *f6i;
490662306a36Sopenharmony_ci};
490762306a36Sopenharmony_ci
490862306a36Sopenharmony_cistatic int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg)
490962306a36Sopenharmony_ci{
491062306a36Sopenharmony_ci	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *)_arg;
491162306a36Sopenharmony_ci	struct fib6_info *f6i = arg->f6i;
491262306a36Sopenharmony_ci
491362306a36Sopenharmony_ci	/* For administrative MTU increase, there is no way to discover
491462306a36Sopenharmony_ci	 * IPv6 PMTU increase, so PMTU increase should be updated here.
491562306a36Sopenharmony_ci	 * Since RFC 1981 doesn't include administrative MTU increase
491662306a36Sopenharmony_ci	 * update PMTU increase is a MUST. (i.e. jumbo frame)
491762306a36Sopenharmony_ci	 */
491862306a36Sopenharmony_ci	if (nh->fib_nh_dev == arg->dev) {
491962306a36Sopenharmony_ci		struct inet6_dev *idev = __in6_dev_get(arg->dev);
492062306a36Sopenharmony_ci		u32 mtu = f6i->fib6_pmtu;
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_ci		if (mtu >= arg->mtu ||
492362306a36Sopenharmony_ci		    (mtu < arg->mtu && mtu == idev->cnf.mtu6))
492462306a36Sopenharmony_ci			fib6_metric_set(f6i, RTAX_MTU, arg->mtu);
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci		spin_lock_bh(&rt6_exception_lock);
492762306a36Sopenharmony_ci		rt6_exceptions_update_pmtu(idev, nh, arg->mtu);
492862306a36Sopenharmony_ci		spin_unlock_bh(&rt6_exception_lock);
492962306a36Sopenharmony_ci	}
493062306a36Sopenharmony_ci
493162306a36Sopenharmony_ci	return 0;
493262306a36Sopenharmony_ci}
493362306a36Sopenharmony_ci
493462306a36Sopenharmony_cistatic int rt6_mtu_change_route(struct fib6_info *f6i, void *p_arg)
493562306a36Sopenharmony_ci{
493662306a36Sopenharmony_ci	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
493762306a36Sopenharmony_ci	struct inet6_dev *idev;
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	/* In IPv6 pmtu discovery is not optional,
494062306a36Sopenharmony_ci	   so that RTAX_MTU lock cannot disable it.
494162306a36Sopenharmony_ci	   We still use this lock to block changes
494262306a36Sopenharmony_ci	   caused by addrconf/ndisc.
494362306a36Sopenharmony_ci	*/
494462306a36Sopenharmony_ci
494562306a36Sopenharmony_ci	idev = __in6_dev_get(arg->dev);
494662306a36Sopenharmony_ci	if (!idev)
494762306a36Sopenharmony_ci		return 0;
494862306a36Sopenharmony_ci
494962306a36Sopenharmony_ci	if (fib6_metric_locked(f6i, RTAX_MTU))
495062306a36Sopenharmony_ci		return 0;
495162306a36Sopenharmony_ci
495262306a36Sopenharmony_ci	arg->f6i = f6i;
495362306a36Sopenharmony_ci	if (f6i->nh) {
495462306a36Sopenharmony_ci		/* fib6_nh_mtu_change only returns 0, so this is safe */
495562306a36Sopenharmony_ci		return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_mtu_change,
495662306a36Sopenharmony_ci						arg);
495762306a36Sopenharmony_ci	}
495862306a36Sopenharmony_ci
495962306a36Sopenharmony_ci	return fib6_nh_mtu_change(f6i->fib6_nh, arg);
496062306a36Sopenharmony_ci}
496162306a36Sopenharmony_ci
496262306a36Sopenharmony_civoid rt6_mtu_change(struct net_device *dev, unsigned int mtu)
496362306a36Sopenharmony_ci{
496462306a36Sopenharmony_ci	struct rt6_mtu_change_arg arg = {
496562306a36Sopenharmony_ci		.dev = dev,
496662306a36Sopenharmony_ci		.mtu = mtu,
496762306a36Sopenharmony_ci	};
496862306a36Sopenharmony_ci
496962306a36Sopenharmony_ci	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
497062306a36Sopenharmony_ci}
497162306a36Sopenharmony_ci
497262306a36Sopenharmony_cistatic const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
497362306a36Sopenharmony_ci	[RTA_UNSPEC]		= { .strict_start_type = RTA_DPORT + 1 },
497462306a36Sopenharmony_ci	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
497562306a36Sopenharmony_ci	[RTA_PREFSRC]		= { .len = sizeof(struct in6_addr) },
497662306a36Sopenharmony_ci	[RTA_OIF]               = { .type = NLA_U32 },
497762306a36Sopenharmony_ci	[RTA_IIF]		= { .type = NLA_U32 },
497862306a36Sopenharmony_ci	[RTA_PRIORITY]          = { .type = NLA_U32 },
497962306a36Sopenharmony_ci	[RTA_METRICS]           = { .type = NLA_NESTED },
498062306a36Sopenharmony_ci	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
498162306a36Sopenharmony_ci	[RTA_PREF]              = { .type = NLA_U8 },
498262306a36Sopenharmony_ci	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
498362306a36Sopenharmony_ci	[RTA_ENCAP]		= { .type = NLA_NESTED },
498462306a36Sopenharmony_ci	[RTA_EXPIRES]		= { .type = NLA_U32 },
498562306a36Sopenharmony_ci	[RTA_UID]		= { .type = NLA_U32 },
498662306a36Sopenharmony_ci	[RTA_MARK]		= { .type = NLA_U32 },
498762306a36Sopenharmony_ci	[RTA_TABLE]		= { .type = NLA_U32 },
498862306a36Sopenharmony_ci	[RTA_IP_PROTO]		= { .type = NLA_U8 },
498962306a36Sopenharmony_ci	[RTA_SPORT]		= { .type = NLA_U16 },
499062306a36Sopenharmony_ci	[RTA_DPORT]		= { .type = NLA_U16 },
499162306a36Sopenharmony_ci	[RTA_NH_ID]		= { .type = NLA_U32 },
499262306a36Sopenharmony_ci};
499362306a36Sopenharmony_ci
499462306a36Sopenharmony_cistatic int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
499562306a36Sopenharmony_ci			      struct fib6_config *cfg,
499662306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
499762306a36Sopenharmony_ci{
499862306a36Sopenharmony_ci	struct rtmsg *rtm;
499962306a36Sopenharmony_ci	struct nlattr *tb[RTA_MAX+1];
500062306a36Sopenharmony_ci	unsigned int pref;
500162306a36Sopenharmony_ci	int err;
500262306a36Sopenharmony_ci
500362306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
500462306a36Sopenharmony_ci				     rtm_ipv6_policy, extack);
500562306a36Sopenharmony_ci	if (err < 0)
500662306a36Sopenharmony_ci		goto errout;
500762306a36Sopenharmony_ci
500862306a36Sopenharmony_ci	err = -EINVAL;
500962306a36Sopenharmony_ci	rtm = nlmsg_data(nlh);
501062306a36Sopenharmony_ci
501162306a36Sopenharmony_ci	if (rtm->rtm_tos) {
501262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
501362306a36Sopenharmony_ci			       "Invalid dsfield (tos): option not available for IPv6");
501462306a36Sopenharmony_ci		goto errout;
501562306a36Sopenharmony_ci	}
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_ci	*cfg = (struct fib6_config){
501862306a36Sopenharmony_ci		.fc_table = rtm->rtm_table,
501962306a36Sopenharmony_ci		.fc_dst_len = rtm->rtm_dst_len,
502062306a36Sopenharmony_ci		.fc_src_len = rtm->rtm_src_len,
502162306a36Sopenharmony_ci		.fc_flags = RTF_UP,
502262306a36Sopenharmony_ci		.fc_protocol = rtm->rtm_protocol,
502362306a36Sopenharmony_ci		.fc_type = rtm->rtm_type,
502462306a36Sopenharmony_ci
502562306a36Sopenharmony_ci		.fc_nlinfo.portid = NETLINK_CB(skb).portid,
502662306a36Sopenharmony_ci		.fc_nlinfo.nlh = nlh,
502762306a36Sopenharmony_ci		.fc_nlinfo.nl_net = sock_net(skb->sk),
502862306a36Sopenharmony_ci	};
502962306a36Sopenharmony_ci
503062306a36Sopenharmony_ci	if (rtm->rtm_type == RTN_UNREACHABLE ||
503162306a36Sopenharmony_ci	    rtm->rtm_type == RTN_BLACKHOLE ||
503262306a36Sopenharmony_ci	    rtm->rtm_type == RTN_PROHIBIT ||
503362306a36Sopenharmony_ci	    rtm->rtm_type == RTN_THROW)
503462306a36Sopenharmony_ci		cfg->fc_flags |= RTF_REJECT;
503562306a36Sopenharmony_ci
503662306a36Sopenharmony_ci	if (rtm->rtm_type == RTN_LOCAL)
503762306a36Sopenharmony_ci		cfg->fc_flags |= RTF_LOCAL;
503862306a36Sopenharmony_ci
503962306a36Sopenharmony_ci	if (rtm->rtm_flags & RTM_F_CLONED)
504062306a36Sopenharmony_ci		cfg->fc_flags |= RTF_CACHE;
504162306a36Sopenharmony_ci
504262306a36Sopenharmony_ci	cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
504362306a36Sopenharmony_ci
504462306a36Sopenharmony_ci	if (tb[RTA_NH_ID]) {
504562306a36Sopenharmony_ci		if (tb[RTA_GATEWAY]   || tb[RTA_OIF] ||
504662306a36Sopenharmony_ci		    tb[RTA_MULTIPATH] || tb[RTA_ENCAP]) {
504762306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
504862306a36Sopenharmony_ci				       "Nexthop specification and nexthop id are mutually exclusive");
504962306a36Sopenharmony_ci			goto errout;
505062306a36Sopenharmony_ci		}
505162306a36Sopenharmony_ci		cfg->fc_nh_id = nla_get_u32(tb[RTA_NH_ID]);
505262306a36Sopenharmony_ci	}
505362306a36Sopenharmony_ci
505462306a36Sopenharmony_ci	if (tb[RTA_GATEWAY]) {
505562306a36Sopenharmony_ci		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
505662306a36Sopenharmony_ci		cfg->fc_flags |= RTF_GATEWAY;
505762306a36Sopenharmony_ci	}
505862306a36Sopenharmony_ci	if (tb[RTA_VIA]) {
505962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
506062306a36Sopenharmony_ci		goto errout;
506162306a36Sopenharmony_ci	}
506262306a36Sopenharmony_ci
506362306a36Sopenharmony_ci	if (tb[RTA_DST]) {
506462306a36Sopenharmony_ci		int plen = (rtm->rtm_dst_len + 7) >> 3;
506562306a36Sopenharmony_ci
506662306a36Sopenharmony_ci		if (nla_len(tb[RTA_DST]) < plen)
506762306a36Sopenharmony_ci			goto errout;
506862306a36Sopenharmony_ci
506962306a36Sopenharmony_ci		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
507062306a36Sopenharmony_ci	}
507162306a36Sopenharmony_ci
507262306a36Sopenharmony_ci	if (tb[RTA_SRC]) {
507362306a36Sopenharmony_ci		int plen = (rtm->rtm_src_len + 7) >> 3;
507462306a36Sopenharmony_ci
507562306a36Sopenharmony_ci		if (nla_len(tb[RTA_SRC]) < plen)
507662306a36Sopenharmony_ci			goto errout;
507762306a36Sopenharmony_ci
507862306a36Sopenharmony_ci		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
507962306a36Sopenharmony_ci	}
508062306a36Sopenharmony_ci
508162306a36Sopenharmony_ci	if (tb[RTA_PREFSRC])
508262306a36Sopenharmony_ci		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
508362306a36Sopenharmony_ci
508462306a36Sopenharmony_ci	if (tb[RTA_OIF])
508562306a36Sopenharmony_ci		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
508662306a36Sopenharmony_ci
508762306a36Sopenharmony_ci	if (tb[RTA_PRIORITY])
508862306a36Sopenharmony_ci		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
508962306a36Sopenharmony_ci
509062306a36Sopenharmony_ci	if (tb[RTA_METRICS]) {
509162306a36Sopenharmony_ci		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
509262306a36Sopenharmony_ci		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
509362306a36Sopenharmony_ci	}
509462306a36Sopenharmony_ci
509562306a36Sopenharmony_ci	if (tb[RTA_TABLE])
509662306a36Sopenharmony_ci		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
509762306a36Sopenharmony_ci
509862306a36Sopenharmony_ci	if (tb[RTA_MULTIPATH]) {
509962306a36Sopenharmony_ci		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
510062306a36Sopenharmony_ci		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
510162306a36Sopenharmony_ci
510262306a36Sopenharmony_ci		err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
510362306a36Sopenharmony_ci						     cfg->fc_mp_len, extack);
510462306a36Sopenharmony_ci		if (err < 0)
510562306a36Sopenharmony_ci			goto errout;
510662306a36Sopenharmony_ci	}
510762306a36Sopenharmony_ci
510862306a36Sopenharmony_ci	if (tb[RTA_PREF]) {
510962306a36Sopenharmony_ci		pref = nla_get_u8(tb[RTA_PREF]);
511062306a36Sopenharmony_ci		if (pref != ICMPV6_ROUTER_PREF_LOW &&
511162306a36Sopenharmony_ci		    pref != ICMPV6_ROUTER_PREF_HIGH)
511262306a36Sopenharmony_ci			pref = ICMPV6_ROUTER_PREF_MEDIUM;
511362306a36Sopenharmony_ci		cfg->fc_flags |= RTF_PREF(pref);
511462306a36Sopenharmony_ci	}
511562306a36Sopenharmony_ci
511662306a36Sopenharmony_ci	if (tb[RTA_ENCAP])
511762306a36Sopenharmony_ci		cfg->fc_encap = tb[RTA_ENCAP];
511862306a36Sopenharmony_ci
511962306a36Sopenharmony_ci	if (tb[RTA_ENCAP_TYPE]) {
512062306a36Sopenharmony_ci		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_ci		err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
512362306a36Sopenharmony_ci		if (err < 0)
512462306a36Sopenharmony_ci			goto errout;
512562306a36Sopenharmony_ci	}
512662306a36Sopenharmony_ci
512762306a36Sopenharmony_ci	if (tb[RTA_EXPIRES]) {
512862306a36Sopenharmony_ci		unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
512962306a36Sopenharmony_ci
513062306a36Sopenharmony_ci		if (addrconf_finite_timeout(timeout)) {
513162306a36Sopenharmony_ci			cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
513262306a36Sopenharmony_ci			cfg->fc_flags |= RTF_EXPIRES;
513362306a36Sopenharmony_ci		}
513462306a36Sopenharmony_ci	}
513562306a36Sopenharmony_ci
513662306a36Sopenharmony_ci	err = 0;
513762306a36Sopenharmony_cierrout:
513862306a36Sopenharmony_ci	return err;
513962306a36Sopenharmony_ci}
514062306a36Sopenharmony_ci
514162306a36Sopenharmony_cistruct rt6_nh {
514262306a36Sopenharmony_ci	struct fib6_info *fib6_info;
514362306a36Sopenharmony_ci	struct fib6_config r_cfg;
514462306a36Sopenharmony_ci	struct list_head next;
514562306a36Sopenharmony_ci};
514662306a36Sopenharmony_ci
514762306a36Sopenharmony_cistatic int ip6_route_info_append(struct net *net,
514862306a36Sopenharmony_ci				 struct list_head *rt6_nh_list,
514962306a36Sopenharmony_ci				 struct fib6_info *rt,
515062306a36Sopenharmony_ci				 struct fib6_config *r_cfg)
515162306a36Sopenharmony_ci{
515262306a36Sopenharmony_ci	struct rt6_nh *nh;
515362306a36Sopenharmony_ci	int err = -EEXIST;
515462306a36Sopenharmony_ci
515562306a36Sopenharmony_ci	list_for_each_entry(nh, rt6_nh_list, next) {
515662306a36Sopenharmony_ci		/* check if fib6_info already exists */
515762306a36Sopenharmony_ci		if (rt6_duplicate_nexthop(nh->fib6_info, rt))
515862306a36Sopenharmony_ci			return err;
515962306a36Sopenharmony_ci	}
516062306a36Sopenharmony_ci
516162306a36Sopenharmony_ci	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
516262306a36Sopenharmony_ci	if (!nh)
516362306a36Sopenharmony_ci		return -ENOMEM;
516462306a36Sopenharmony_ci	nh->fib6_info = rt;
516562306a36Sopenharmony_ci	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
516662306a36Sopenharmony_ci	list_add_tail(&nh->next, rt6_nh_list);
516762306a36Sopenharmony_ci
516862306a36Sopenharmony_ci	return 0;
516962306a36Sopenharmony_ci}
517062306a36Sopenharmony_ci
517162306a36Sopenharmony_cistatic void ip6_route_mpath_notify(struct fib6_info *rt,
517262306a36Sopenharmony_ci				   struct fib6_info *rt_last,
517362306a36Sopenharmony_ci				   struct nl_info *info,
517462306a36Sopenharmony_ci				   __u16 nlflags)
517562306a36Sopenharmony_ci{
517662306a36Sopenharmony_ci	/* if this is an APPEND route, then rt points to the first route
517762306a36Sopenharmony_ci	 * inserted and rt_last points to last route inserted. Userspace
517862306a36Sopenharmony_ci	 * wants a consistent dump of the route which starts at the first
517962306a36Sopenharmony_ci	 * nexthop. Since sibling routes are always added at the end of
518062306a36Sopenharmony_ci	 * the list, find the first sibling of the last route appended
518162306a36Sopenharmony_ci	 */
518262306a36Sopenharmony_ci	if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
518362306a36Sopenharmony_ci		rt = list_first_entry(&rt_last->fib6_siblings,
518462306a36Sopenharmony_ci				      struct fib6_info,
518562306a36Sopenharmony_ci				      fib6_siblings);
518662306a36Sopenharmony_ci	}
518762306a36Sopenharmony_ci
518862306a36Sopenharmony_ci	if (rt)
518962306a36Sopenharmony_ci		inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
519062306a36Sopenharmony_ci}
519162306a36Sopenharmony_ci
519262306a36Sopenharmony_cistatic bool ip6_route_mpath_should_notify(const struct fib6_info *rt)
519362306a36Sopenharmony_ci{
519462306a36Sopenharmony_ci	bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
519562306a36Sopenharmony_ci	bool should_notify = false;
519662306a36Sopenharmony_ci	struct fib6_info *leaf;
519762306a36Sopenharmony_ci	struct fib6_node *fn;
519862306a36Sopenharmony_ci
519962306a36Sopenharmony_ci	rcu_read_lock();
520062306a36Sopenharmony_ci	fn = rcu_dereference(rt->fib6_node);
520162306a36Sopenharmony_ci	if (!fn)
520262306a36Sopenharmony_ci		goto out;
520362306a36Sopenharmony_ci
520462306a36Sopenharmony_ci	leaf = rcu_dereference(fn->leaf);
520562306a36Sopenharmony_ci	if (!leaf)
520662306a36Sopenharmony_ci		goto out;
520762306a36Sopenharmony_ci
520862306a36Sopenharmony_ci	if (rt == leaf ||
520962306a36Sopenharmony_ci	    (rt_can_ecmp && rt->fib6_metric == leaf->fib6_metric &&
521062306a36Sopenharmony_ci	     rt6_qualify_for_ecmp(leaf)))
521162306a36Sopenharmony_ci		should_notify = true;
521262306a36Sopenharmony_ciout:
521362306a36Sopenharmony_ci	rcu_read_unlock();
521462306a36Sopenharmony_ci
521562306a36Sopenharmony_ci	return should_notify;
521662306a36Sopenharmony_ci}
521762306a36Sopenharmony_ci
521862306a36Sopenharmony_cistatic int fib6_gw_from_attr(struct in6_addr *gw, struct nlattr *nla,
521962306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
522062306a36Sopenharmony_ci{
522162306a36Sopenharmony_ci	if (nla_len(nla) < sizeof(*gw)) {
522262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_GATEWAY");
522362306a36Sopenharmony_ci		return -EINVAL;
522462306a36Sopenharmony_ci	}
522562306a36Sopenharmony_ci
522662306a36Sopenharmony_ci	*gw = nla_get_in6_addr(nla);
522762306a36Sopenharmony_ci
522862306a36Sopenharmony_ci	return 0;
522962306a36Sopenharmony_ci}
523062306a36Sopenharmony_ci
523162306a36Sopenharmony_cistatic int ip6_route_multipath_add(struct fib6_config *cfg,
523262306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
523362306a36Sopenharmony_ci{
523462306a36Sopenharmony_ci	struct fib6_info *rt_notif = NULL, *rt_last = NULL;
523562306a36Sopenharmony_ci	struct nl_info *info = &cfg->fc_nlinfo;
523662306a36Sopenharmony_ci	struct fib6_config r_cfg;
523762306a36Sopenharmony_ci	struct rtnexthop *rtnh;
523862306a36Sopenharmony_ci	struct fib6_info *rt;
523962306a36Sopenharmony_ci	struct rt6_nh *err_nh;
524062306a36Sopenharmony_ci	struct rt6_nh *nh, *nh_safe;
524162306a36Sopenharmony_ci	__u16 nlflags;
524262306a36Sopenharmony_ci	int remaining;
524362306a36Sopenharmony_ci	int attrlen;
524462306a36Sopenharmony_ci	int err = 1;
524562306a36Sopenharmony_ci	int nhn = 0;
524662306a36Sopenharmony_ci	int replace = (cfg->fc_nlinfo.nlh &&
524762306a36Sopenharmony_ci		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
524862306a36Sopenharmony_ci	LIST_HEAD(rt6_nh_list);
524962306a36Sopenharmony_ci
525062306a36Sopenharmony_ci	nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
525162306a36Sopenharmony_ci	if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
525262306a36Sopenharmony_ci		nlflags |= NLM_F_APPEND;
525362306a36Sopenharmony_ci
525462306a36Sopenharmony_ci	remaining = cfg->fc_mp_len;
525562306a36Sopenharmony_ci	rtnh = (struct rtnexthop *)cfg->fc_mp;
525662306a36Sopenharmony_ci
525762306a36Sopenharmony_ci	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
525862306a36Sopenharmony_ci	 * fib6_info structs per nexthop
525962306a36Sopenharmony_ci	 */
526062306a36Sopenharmony_ci	while (rtnh_ok(rtnh, remaining)) {
526162306a36Sopenharmony_ci		memcpy(&r_cfg, cfg, sizeof(*cfg));
526262306a36Sopenharmony_ci		if (rtnh->rtnh_ifindex)
526362306a36Sopenharmony_ci			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
526462306a36Sopenharmony_ci
526562306a36Sopenharmony_ci		attrlen = rtnh_attrlen(rtnh);
526662306a36Sopenharmony_ci		if (attrlen > 0) {
526762306a36Sopenharmony_ci			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
526862306a36Sopenharmony_ci
526962306a36Sopenharmony_ci			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
527062306a36Sopenharmony_ci			if (nla) {
527162306a36Sopenharmony_ci				err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
527262306a36Sopenharmony_ci							extack);
527362306a36Sopenharmony_ci				if (err)
527462306a36Sopenharmony_ci					goto cleanup;
527562306a36Sopenharmony_ci
527662306a36Sopenharmony_ci				r_cfg.fc_flags |= RTF_GATEWAY;
527762306a36Sopenharmony_ci			}
527862306a36Sopenharmony_ci			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
527962306a36Sopenharmony_ci
528062306a36Sopenharmony_ci			/* RTA_ENCAP_TYPE length checked in
528162306a36Sopenharmony_ci			 * lwtunnel_valid_encap_type_attr
528262306a36Sopenharmony_ci			 */
528362306a36Sopenharmony_ci			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
528462306a36Sopenharmony_ci			if (nla)
528562306a36Sopenharmony_ci				r_cfg.fc_encap_type = nla_get_u16(nla);
528662306a36Sopenharmony_ci		}
528762306a36Sopenharmony_ci
528862306a36Sopenharmony_ci		r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
528962306a36Sopenharmony_ci		rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
529062306a36Sopenharmony_ci		if (IS_ERR(rt)) {
529162306a36Sopenharmony_ci			err = PTR_ERR(rt);
529262306a36Sopenharmony_ci			rt = NULL;
529362306a36Sopenharmony_ci			goto cleanup;
529462306a36Sopenharmony_ci		}
529562306a36Sopenharmony_ci		if (!rt6_qualify_for_ecmp(rt)) {
529662306a36Sopenharmony_ci			err = -EINVAL;
529762306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
529862306a36Sopenharmony_ci				       "Device only routes can not be added for IPv6 using the multipath API.");
529962306a36Sopenharmony_ci			fib6_info_release(rt);
530062306a36Sopenharmony_ci			goto cleanup;
530162306a36Sopenharmony_ci		}
530262306a36Sopenharmony_ci
530362306a36Sopenharmony_ci		rt->fib6_nh->fib_nh_weight = rtnh->rtnh_hops + 1;
530462306a36Sopenharmony_ci
530562306a36Sopenharmony_ci		err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
530662306a36Sopenharmony_ci					    rt, &r_cfg);
530762306a36Sopenharmony_ci		if (err) {
530862306a36Sopenharmony_ci			fib6_info_release(rt);
530962306a36Sopenharmony_ci			goto cleanup;
531062306a36Sopenharmony_ci		}
531162306a36Sopenharmony_ci
531262306a36Sopenharmony_ci		rtnh = rtnh_next(rtnh, &remaining);
531362306a36Sopenharmony_ci	}
531462306a36Sopenharmony_ci
531562306a36Sopenharmony_ci	if (list_empty(&rt6_nh_list)) {
531662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
531762306a36Sopenharmony_ci			       "Invalid nexthop configuration - no valid nexthops");
531862306a36Sopenharmony_ci		return -EINVAL;
531962306a36Sopenharmony_ci	}
532062306a36Sopenharmony_ci
532162306a36Sopenharmony_ci	/* for add and replace send one notification with all nexthops.
532262306a36Sopenharmony_ci	 * Skip the notification in fib6_add_rt2node and send one with
532362306a36Sopenharmony_ci	 * the full route when done
532462306a36Sopenharmony_ci	 */
532562306a36Sopenharmony_ci	info->skip_notify = 1;
532662306a36Sopenharmony_ci
532762306a36Sopenharmony_ci	/* For add and replace, send one notification with all nexthops. For
532862306a36Sopenharmony_ci	 * append, send one notification with all appended nexthops.
532962306a36Sopenharmony_ci	 */
533062306a36Sopenharmony_ci	info->skip_notify_kernel = 1;
533162306a36Sopenharmony_ci
533262306a36Sopenharmony_ci	err_nh = NULL;
533362306a36Sopenharmony_ci	list_for_each_entry(nh, &rt6_nh_list, next) {
533462306a36Sopenharmony_ci		err = __ip6_ins_rt(nh->fib6_info, info, extack);
533562306a36Sopenharmony_ci
533662306a36Sopenharmony_ci		if (err) {
533762306a36Sopenharmony_ci			if (replace && nhn)
533862306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
533962306a36Sopenharmony_ci						   "multipath route replace failed (check consistency of installed routes)");
534062306a36Sopenharmony_ci			err_nh = nh;
534162306a36Sopenharmony_ci			goto add_errout;
534262306a36Sopenharmony_ci		}
534362306a36Sopenharmony_ci		/* save reference to last route successfully inserted */
534462306a36Sopenharmony_ci		rt_last = nh->fib6_info;
534562306a36Sopenharmony_ci
534662306a36Sopenharmony_ci		/* save reference to first route for notification */
534762306a36Sopenharmony_ci		if (!rt_notif)
534862306a36Sopenharmony_ci			rt_notif = nh->fib6_info;
534962306a36Sopenharmony_ci
535062306a36Sopenharmony_ci		/* Because each route is added like a single route we remove
535162306a36Sopenharmony_ci		 * these flags after the first nexthop: if there is a collision,
535262306a36Sopenharmony_ci		 * we have already failed to add the first nexthop:
535362306a36Sopenharmony_ci		 * fib6_add_rt2node() has rejected it; when replacing, old
535462306a36Sopenharmony_ci		 * nexthops have been replaced by first new, the rest should
535562306a36Sopenharmony_ci		 * be added to it.
535662306a36Sopenharmony_ci		 */
535762306a36Sopenharmony_ci		if (cfg->fc_nlinfo.nlh) {
535862306a36Sopenharmony_ci			cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
535962306a36Sopenharmony_ci							     NLM_F_REPLACE);
536062306a36Sopenharmony_ci			cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_CREATE;
536162306a36Sopenharmony_ci		}
536262306a36Sopenharmony_ci		nhn++;
536362306a36Sopenharmony_ci	}
536462306a36Sopenharmony_ci
536562306a36Sopenharmony_ci	/* An in-kernel notification should only be sent in case the new
536662306a36Sopenharmony_ci	 * multipath route is added as the first route in the node, or if
536762306a36Sopenharmony_ci	 * it was appended to it. We pass 'rt_notif' since it is the first
536862306a36Sopenharmony_ci	 * sibling and might allow us to skip some checks in the replace case.
536962306a36Sopenharmony_ci	 */
537062306a36Sopenharmony_ci	if (ip6_route_mpath_should_notify(rt_notif)) {
537162306a36Sopenharmony_ci		enum fib_event_type fib_event;
537262306a36Sopenharmony_ci
537362306a36Sopenharmony_ci		if (rt_notif->fib6_nsiblings != nhn - 1)
537462306a36Sopenharmony_ci			fib_event = FIB_EVENT_ENTRY_APPEND;
537562306a36Sopenharmony_ci		else
537662306a36Sopenharmony_ci			fib_event = FIB_EVENT_ENTRY_REPLACE;
537762306a36Sopenharmony_ci
537862306a36Sopenharmony_ci		err = call_fib6_multipath_entry_notifiers(info->nl_net,
537962306a36Sopenharmony_ci							  fib_event, rt_notif,
538062306a36Sopenharmony_ci							  nhn - 1, extack);
538162306a36Sopenharmony_ci		if (err) {
538262306a36Sopenharmony_ci			/* Delete all the siblings that were just added */
538362306a36Sopenharmony_ci			err_nh = NULL;
538462306a36Sopenharmony_ci			goto add_errout;
538562306a36Sopenharmony_ci		}
538662306a36Sopenharmony_ci	}
538762306a36Sopenharmony_ci
538862306a36Sopenharmony_ci	/* success ... tell user about new route */
538962306a36Sopenharmony_ci	ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
539062306a36Sopenharmony_ci	goto cleanup;
539162306a36Sopenharmony_ci
539262306a36Sopenharmony_ciadd_errout:
539362306a36Sopenharmony_ci	/* send notification for routes that were added so that
539462306a36Sopenharmony_ci	 * the delete notifications sent by ip6_route_del are
539562306a36Sopenharmony_ci	 * coherent
539662306a36Sopenharmony_ci	 */
539762306a36Sopenharmony_ci	if (rt_notif)
539862306a36Sopenharmony_ci		ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
539962306a36Sopenharmony_ci
540062306a36Sopenharmony_ci	/* Delete routes that were already added */
540162306a36Sopenharmony_ci	list_for_each_entry(nh, &rt6_nh_list, next) {
540262306a36Sopenharmony_ci		if (err_nh == nh)
540362306a36Sopenharmony_ci			break;
540462306a36Sopenharmony_ci		ip6_route_del(&nh->r_cfg, extack);
540562306a36Sopenharmony_ci	}
540662306a36Sopenharmony_ci
540762306a36Sopenharmony_cicleanup:
540862306a36Sopenharmony_ci	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
540962306a36Sopenharmony_ci		fib6_info_release(nh->fib6_info);
541062306a36Sopenharmony_ci		list_del(&nh->next);
541162306a36Sopenharmony_ci		kfree(nh);
541262306a36Sopenharmony_ci	}
541362306a36Sopenharmony_ci
541462306a36Sopenharmony_ci	return err;
541562306a36Sopenharmony_ci}
541662306a36Sopenharmony_ci
541762306a36Sopenharmony_cistatic int ip6_route_multipath_del(struct fib6_config *cfg,
541862306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
541962306a36Sopenharmony_ci{
542062306a36Sopenharmony_ci	struct fib6_config r_cfg;
542162306a36Sopenharmony_ci	struct rtnexthop *rtnh;
542262306a36Sopenharmony_ci	int last_err = 0;
542362306a36Sopenharmony_ci	int remaining;
542462306a36Sopenharmony_ci	int attrlen;
542562306a36Sopenharmony_ci	int err;
542662306a36Sopenharmony_ci
542762306a36Sopenharmony_ci	remaining = cfg->fc_mp_len;
542862306a36Sopenharmony_ci	rtnh = (struct rtnexthop *)cfg->fc_mp;
542962306a36Sopenharmony_ci
543062306a36Sopenharmony_ci	/* Parse a Multipath Entry */
543162306a36Sopenharmony_ci	while (rtnh_ok(rtnh, remaining)) {
543262306a36Sopenharmony_ci		memcpy(&r_cfg, cfg, sizeof(*cfg));
543362306a36Sopenharmony_ci		if (rtnh->rtnh_ifindex)
543462306a36Sopenharmony_ci			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
543562306a36Sopenharmony_ci
543662306a36Sopenharmony_ci		attrlen = rtnh_attrlen(rtnh);
543762306a36Sopenharmony_ci		if (attrlen > 0) {
543862306a36Sopenharmony_ci			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
543962306a36Sopenharmony_ci
544062306a36Sopenharmony_ci			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
544162306a36Sopenharmony_ci			if (nla) {
544262306a36Sopenharmony_ci				err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
544362306a36Sopenharmony_ci							extack);
544462306a36Sopenharmony_ci				if (err) {
544562306a36Sopenharmony_ci					last_err = err;
544662306a36Sopenharmony_ci					goto next_rtnh;
544762306a36Sopenharmony_ci				}
544862306a36Sopenharmony_ci
544962306a36Sopenharmony_ci				r_cfg.fc_flags |= RTF_GATEWAY;
545062306a36Sopenharmony_ci			}
545162306a36Sopenharmony_ci		}
545262306a36Sopenharmony_ci		err = ip6_route_del(&r_cfg, extack);
545362306a36Sopenharmony_ci		if (err)
545462306a36Sopenharmony_ci			last_err = err;
545562306a36Sopenharmony_ci
545662306a36Sopenharmony_cinext_rtnh:
545762306a36Sopenharmony_ci		rtnh = rtnh_next(rtnh, &remaining);
545862306a36Sopenharmony_ci	}
545962306a36Sopenharmony_ci
546062306a36Sopenharmony_ci	return last_err;
546162306a36Sopenharmony_ci}
546262306a36Sopenharmony_ci
546362306a36Sopenharmony_cistatic int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
546462306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
546562306a36Sopenharmony_ci{
546662306a36Sopenharmony_ci	struct fib6_config cfg;
546762306a36Sopenharmony_ci	int err;
546862306a36Sopenharmony_ci
546962306a36Sopenharmony_ci	err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
547062306a36Sopenharmony_ci	if (err < 0)
547162306a36Sopenharmony_ci		return err;
547262306a36Sopenharmony_ci
547362306a36Sopenharmony_ci	if (cfg.fc_nh_id &&
547462306a36Sopenharmony_ci	    !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) {
547562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
547662306a36Sopenharmony_ci		return -EINVAL;
547762306a36Sopenharmony_ci	}
547862306a36Sopenharmony_ci
547962306a36Sopenharmony_ci	if (cfg.fc_mp)
548062306a36Sopenharmony_ci		return ip6_route_multipath_del(&cfg, extack);
548162306a36Sopenharmony_ci	else {
548262306a36Sopenharmony_ci		cfg.fc_delete_all_nh = 1;
548362306a36Sopenharmony_ci		return ip6_route_del(&cfg, extack);
548462306a36Sopenharmony_ci	}
548562306a36Sopenharmony_ci}
548662306a36Sopenharmony_ci
548762306a36Sopenharmony_cistatic int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
548862306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
548962306a36Sopenharmony_ci{
549062306a36Sopenharmony_ci	struct fib6_config cfg;
549162306a36Sopenharmony_ci	int err;
549262306a36Sopenharmony_ci
549362306a36Sopenharmony_ci	err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
549462306a36Sopenharmony_ci	if (err < 0)
549562306a36Sopenharmony_ci		return err;
549662306a36Sopenharmony_ci
549762306a36Sopenharmony_ci	if (cfg.fc_metric == 0)
549862306a36Sopenharmony_ci		cfg.fc_metric = IP6_RT_PRIO_USER;
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_ci	if (cfg.fc_mp)
550162306a36Sopenharmony_ci		return ip6_route_multipath_add(&cfg, extack);
550262306a36Sopenharmony_ci	else
550362306a36Sopenharmony_ci		return ip6_route_add(&cfg, GFP_KERNEL, extack);
550462306a36Sopenharmony_ci}
550562306a36Sopenharmony_ci
550662306a36Sopenharmony_ci/* add the overhead of this fib6_nh to nexthop_len */
550762306a36Sopenharmony_cistatic int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg)
550862306a36Sopenharmony_ci{
550962306a36Sopenharmony_ci	int *nexthop_len = arg;
551062306a36Sopenharmony_ci
551162306a36Sopenharmony_ci	*nexthop_len += nla_total_size(0)	 /* RTA_MULTIPATH */
551262306a36Sopenharmony_ci		     + NLA_ALIGN(sizeof(struct rtnexthop))
551362306a36Sopenharmony_ci		     + nla_total_size(16); /* RTA_GATEWAY */
551462306a36Sopenharmony_ci
551562306a36Sopenharmony_ci	if (nh->fib_nh_lws) {
551662306a36Sopenharmony_ci		/* RTA_ENCAP_TYPE */
551762306a36Sopenharmony_ci		*nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
551862306a36Sopenharmony_ci		/* RTA_ENCAP */
551962306a36Sopenharmony_ci		*nexthop_len += nla_total_size(2);
552062306a36Sopenharmony_ci	}
552162306a36Sopenharmony_ci
552262306a36Sopenharmony_ci	return 0;
552362306a36Sopenharmony_ci}
552462306a36Sopenharmony_ci
552562306a36Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i)
552662306a36Sopenharmony_ci{
552762306a36Sopenharmony_ci	int nexthop_len;
552862306a36Sopenharmony_ci
552962306a36Sopenharmony_ci	if (f6i->nh) {
553062306a36Sopenharmony_ci		nexthop_len = nla_total_size(4); /* RTA_NH_ID */
553162306a36Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size,
553262306a36Sopenharmony_ci					 &nexthop_len);
553362306a36Sopenharmony_ci	} else {
553462306a36Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
553562306a36Sopenharmony_ci		struct fib6_nh *nh = f6i->fib6_nh;
553662306a36Sopenharmony_ci
553762306a36Sopenharmony_ci		nexthop_len = 0;
553862306a36Sopenharmony_ci		if (f6i->fib6_nsiblings) {
553962306a36Sopenharmony_ci			rt6_nh_nlmsg_size(nh, &nexthop_len);
554062306a36Sopenharmony_ci
554162306a36Sopenharmony_ci			list_for_each_entry_safe(sibling, next_sibling,
554262306a36Sopenharmony_ci						 &f6i->fib6_siblings, fib6_siblings) {
554362306a36Sopenharmony_ci				rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len);
554462306a36Sopenharmony_ci			}
554562306a36Sopenharmony_ci		}
554662306a36Sopenharmony_ci		nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
554762306a36Sopenharmony_ci	}
554862306a36Sopenharmony_ci
554962306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct rtmsg))
555062306a36Sopenharmony_ci	       + nla_total_size(16) /* RTA_SRC */
555162306a36Sopenharmony_ci	       + nla_total_size(16) /* RTA_DST */
555262306a36Sopenharmony_ci	       + nla_total_size(16) /* RTA_GATEWAY */
555362306a36Sopenharmony_ci	       + nla_total_size(16) /* RTA_PREFSRC */
555462306a36Sopenharmony_ci	       + nla_total_size(4) /* RTA_TABLE */
555562306a36Sopenharmony_ci	       + nla_total_size(4) /* RTA_IIF */
555662306a36Sopenharmony_ci	       + nla_total_size(4) /* RTA_OIF */
555762306a36Sopenharmony_ci	       + nla_total_size(4) /* RTA_PRIORITY */
555862306a36Sopenharmony_ci	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
555962306a36Sopenharmony_ci	       + nla_total_size(sizeof(struct rta_cacheinfo))
556062306a36Sopenharmony_ci	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
556162306a36Sopenharmony_ci	       + nla_total_size(1) /* RTA_PREF */
556262306a36Sopenharmony_ci	       + nexthop_len;
556362306a36Sopenharmony_ci}
556462306a36Sopenharmony_ci
556562306a36Sopenharmony_cistatic int rt6_fill_node_nexthop(struct sk_buff *skb, struct nexthop *nh,
556662306a36Sopenharmony_ci				 unsigned char *flags)
556762306a36Sopenharmony_ci{
556862306a36Sopenharmony_ci	if (nexthop_is_multipath(nh)) {
556962306a36Sopenharmony_ci		struct nlattr *mp;
557062306a36Sopenharmony_ci
557162306a36Sopenharmony_ci		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
557262306a36Sopenharmony_ci		if (!mp)
557362306a36Sopenharmony_ci			goto nla_put_failure;
557462306a36Sopenharmony_ci
557562306a36Sopenharmony_ci		if (nexthop_mpath_fill_node(skb, nh, AF_INET6))
557662306a36Sopenharmony_ci			goto nla_put_failure;
557762306a36Sopenharmony_ci
557862306a36Sopenharmony_ci		nla_nest_end(skb, mp);
557962306a36Sopenharmony_ci	} else {
558062306a36Sopenharmony_ci		struct fib6_nh *fib6_nh;
558162306a36Sopenharmony_ci
558262306a36Sopenharmony_ci		fib6_nh = nexthop_fib6_nh(nh);
558362306a36Sopenharmony_ci		if (fib_nexthop_info(skb, &fib6_nh->nh_common, AF_INET6,
558462306a36Sopenharmony_ci				     flags, false) < 0)
558562306a36Sopenharmony_ci			goto nla_put_failure;
558662306a36Sopenharmony_ci	}
558762306a36Sopenharmony_ci
558862306a36Sopenharmony_ci	return 0;
558962306a36Sopenharmony_ci
559062306a36Sopenharmony_cinla_put_failure:
559162306a36Sopenharmony_ci	return -EMSGSIZE;
559262306a36Sopenharmony_ci}
559362306a36Sopenharmony_ci
559462306a36Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb,
559562306a36Sopenharmony_ci			 struct fib6_info *rt, struct dst_entry *dst,
559662306a36Sopenharmony_ci			 struct in6_addr *dest, struct in6_addr *src,
559762306a36Sopenharmony_ci			 int iif, int type, u32 portid, u32 seq,
559862306a36Sopenharmony_ci			 unsigned int flags)
559962306a36Sopenharmony_ci{
560062306a36Sopenharmony_ci	struct rt6_info *rt6 = (struct rt6_info *)dst;
560162306a36Sopenharmony_ci	struct rt6key *rt6_dst, *rt6_src;
560262306a36Sopenharmony_ci	u32 *pmetrics, table, rt6_flags;
560362306a36Sopenharmony_ci	unsigned char nh_flags = 0;
560462306a36Sopenharmony_ci	struct nlmsghdr *nlh;
560562306a36Sopenharmony_ci	struct rtmsg *rtm;
560662306a36Sopenharmony_ci	long expires = 0;
560762306a36Sopenharmony_ci
560862306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
560962306a36Sopenharmony_ci	if (!nlh)
561062306a36Sopenharmony_ci		return -EMSGSIZE;
561162306a36Sopenharmony_ci
561262306a36Sopenharmony_ci	if (rt6) {
561362306a36Sopenharmony_ci		rt6_dst = &rt6->rt6i_dst;
561462306a36Sopenharmony_ci		rt6_src = &rt6->rt6i_src;
561562306a36Sopenharmony_ci		rt6_flags = rt6->rt6i_flags;
561662306a36Sopenharmony_ci	} else {
561762306a36Sopenharmony_ci		rt6_dst = &rt->fib6_dst;
561862306a36Sopenharmony_ci		rt6_src = &rt->fib6_src;
561962306a36Sopenharmony_ci		rt6_flags = rt->fib6_flags;
562062306a36Sopenharmony_ci	}
562162306a36Sopenharmony_ci
562262306a36Sopenharmony_ci	rtm = nlmsg_data(nlh);
562362306a36Sopenharmony_ci	rtm->rtm_family = AF_INET6;
562462306a36Sopenharmony_ci	rtm->rtm_dst_len = rt6_dst->plen;
562562306a36Sopenharmony_ci	rtm->rtm_src_len = rt6_src->plen;
562662306a36Sopenharmony_ci	rtm->rtm_tos = 0;
562762306a36Sopenharmony_ci	if (rt->fib6_table)
562862306a36Sopenharmony_ci		table = rt->fib6_table->tb6_id;
562962306a36Sopenharmony_ci	else
563062306a36Sopenharmony_ci		table = RT6_TABLE_UNSPEC;
563162306a36Sopenharmony_ci	rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
563262306a36Sopenharmony_ci	if (nla_put_u32(skb, RTA_TABLE, table))
563362306a36Sopenharmony_ci		goto nla_put_failure;
563462306a36Sopenharmony_ci
563562306a36Sopenharmony_ci	rtm->rtm_type = rt->fib6_type;
563662306a36Sopenharmony_ci	rtm->rtm_flags = 0;
563762306a36Sopenharmony_ci	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
563862306a36Sopenharmony_ci	rtm->rtm_protocol = rt->fib6_protocol;
563962306a36Sopenharmony_ci
564062306a36Sopenharmony_ci	if (rt6_flags & RTF_CACHE)
564162306a36Sopenharmony_ci		rtm->rtm_flags |= RTM_F_CLONED;
564262306a36Sopenharmony_ci
564362306a36Sopenharmony_ci	if (dest) {
564462306a36Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_DST, dest))
564562306a36Sopenharmony_ci			goto nla_put_failure;
564662306a36Sopenharmony_ci		rtm->rtm_dst_len = 128;
564762306a36Sopenharmony_ci	} else if (rtm->rtm_dst_len)
564862306a36Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
564962306a36Sopenharmony_ci			goto nla_put_failure;
565062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
565162306a36Sopenharmony_ci	if (src) {
565262306a36Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_SRC, src))
565362306a36Sopenharmony_ci			goto nla_put_failure;
565462306a36Sopenharmony_ci		rtm->rtm_src_len = 128;
565562306a36Sopenharmony_ci	} else if (rtm->rtm_src_len &&
565662306a36Sopenharmony_ci		   nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
565762306a36Sopenharmony_ci		goto nla_put_failure;
565862306a36Sopenharmony_ci#endif
565962306a36Sopenharmony_ci	if (iif) {
566062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE
566162306a36Sopenharmony_ci		if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
566262306a36Sopenharmony_ci			int err = ip6mr_get_route(net, skb, rtm, portid);
566362306a36Sopenharmony_ci
566462306a36Sopenharmony_ci			if (err == 0)
566562306a36Sopenharmony_ci				return 0;
566662306a36Sopenharmony_ci			if (err < 0)
566762306a36Sopenharmony_ci				goto nla_put_failure;
566862306a36Sopenharmony_ci		} else
566962306a36Sopenharmony_ci#endif
567062306a36Sopenharmony_ci			if (nla_put_u32(skb, RTA_IIF, iif))
567162306a36Sopenharmony_ci				goto nla_put_failure;
567262306a36Sopenharmony_ci	} else if (dest) {
567362306a36Sopenharmony_ci		struct in6_addr saddr_buf;
567462306a36Sopenharmony_ci		if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
567562306a36Sopenharmony_ci		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
567662306a36Sopenharmony_ci			goto nla_put_failure;
567762306a36Sopenharmony_ci	}
567862306a36Sopenharmony_ci
567962306a36Sopenharmony_ci	if (rt->fib6_prefsrc.plen) {
568062306a36Sopenharmony_ci		struct in6_addr saddr_buf;
568162306a36Sopenharmony_ci		saddr_buf = rt->fib6_prefsrc.addr;
568262306a36Sopenharmony_ci		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
568362306a36Sopenharmony_ci			goto nla_put_failure;
568462306a36Sopenharmony_ci	}
568562306a36Sopenharmony_ci
568662306a36Sopenharmony_ci	pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
568762306a36Sopenharmony_ci	if (rtnetlink_put_metrics(skb, pmetrics) < 0)
568862306a36Sopenharmony_ci		goto nla_put_failure;
568962306a36Sopenharmony_ci
569062306a36Sopenharmony_ci	if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
569162306a36Sopenharmony_ci		goto nla_put_failure;
569262306a36Sopenharmony_ci
569362306a36Sopenharmony_ci	/* For multipath routes, walk the siblings list and add
569462306a36Sopenharmony_ci	 * each as a nexthop within RTA_MULTIPATH.
569562306a36Sopenharmony_ci	 */
569662306a36Sopenharmony_ci	if (rt6) {
569762306a36Sopenharmony_ci		if (rt6_flags & RTF_GATEWAY &&
569862306a36Sopenharmony_ci		    nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
569962306a36Sopenharmony_ci			goto nla_put_failure;
570062306a36Sopenharmony_ci
570162306a36Sopenharmony_ci		if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
570262306a36Sopenharmony_ci			goto nla_put_failure;
570362306a36Sopenharmony_ci
570462306a36Sopenharmony_ci		if (dst->lwtstate &&
570562306a36Sopenharmony_ci		    lwtunnel_fill_encap(skb, dst->lwtstate, RTA_ENCAP, RTA_ENCAP_TYPE) < 0)
570662306a36Sopenharmony_ci			goto nla_put_failure;
570762306a36Sopenharmony_ci	} else if (rt->fib6_nsiblings) {
570862306a36Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
570962306a36Sopenharmony_ci		struct nlattr *mp;
571062306a36Sopenharmony_ci
571162306a36Sopenharmony_ci		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
571262306a36Sopenharmony_ci		if (!mp)
571362306a36Sopenharmony_ci			goto nla_put_failure;
571462306a36Sopenharmony_ci
571562306a36Sopenharmony_ci		if (fib_add_nexthop(skb, &rt->fib6_nh->nh_common,
571662306a36Sopenharmony_ci				    rt->fib6_nh->fib_nh_weight, AF_INET6,
571762306a36Sopenharmony_ci				    0) < 0)
571862306a36Sopenharmony_ci			goto nla_put_failure;
571962306a36Sopenharmony_ci
572062306a36Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
572162306a36Sopenharmony_ci					 &rt->fib6_siblings, fib6_siblings) {
572262306a36Sopenharmony_ci			if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common,
572362306a36Sopenharmony_ci					    sibling->fib6_nh->fib_nh_weight,
572462306a36Sopenharmony_ci					    AF_INET6, 0) < 0)
572562306a36Sopenharmony_ci				goto nla_put_failure;
572662306a36Sopenharmony_ci		}
572762306a36Sopenharmony_ci
572862306a36Sopenharmony_ci		nla_nest_end(skb, mp);
572962306a36Sopenharmony_ci	} else if (rt->nh) {
573062306a36Sopenharmony_ci		if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id))
573162306a36Sopenharmony_ci			goto nla_put_failure;
573262306a36Sopenharmony_ci
573362306a36Sopenharmony_ci		if (nexthop_is_blackhole(rt->nh))
573462306a36Sopenharmony_ci			rtm->rtm_type = RTN_BLACKHOLE;
573562306a36Sopenharmony_ci
573662306a36Sopenharmony_ci		if (READ_ONCE(net->ipv4.sysctl_nexthop_compat_mode) &&
573762306a36Sopenharmony_ci		    rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0)
573862306a36Sopenharmony_ci			goto nla_put_failure;
573962306a36Sopenharmony_ci
574062306a36Sopenharmony_ci		rtm->rtm_flags |= nh_flags;
574162306a36Sopenharmony_ci	} else {
574262306a36Sopenharmony_ci		if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, AF_INET6,
574362306a36Sopenharmony_ci				     &nh_flags, false) < 0)
574462306a36Sopenharmony_ci			goto nla_put_failure;
574562306a36Sopenharmony_ci
574662306a36Sopenharmony_ci		rtm->rtm_flags |= nh_flags;
574762306a36Sopenharmony_ci	}
574862306a36Sopenharmony_ci
574962306a36Sopenharmony_ci	if (rt6_flags & RTF_EXPIRES) {
575062306a36Sopenharmony_ci		expires = dst ? dst->expires : rt->expires;
575162306a36Sopenharmony_ci		expires -= jiffies;
575262306a36Sopenharmony_ci	}
575362306a36Sopenharmony_ci
575462306a36Sopenharmony_ci	if (!dst) {
575562306a36Sopenharmony_ci		if (READ_ONCE(rt->offload))
575662306a36Sopenharmony_ci			rtm->rtm_flags |= RTM_F_OFFLOAD;
575762306a36Sopenharmony_ci		if (READ_ONCE(rt->trap))
575862306a36Sopenharmony_ci			rtm->rtm_flags |= RTM_F_TRAP;
575962306a36Sopenharmony_ci		if (READ_ONCE(rt->offload_failed))
576062306a36Sopenharmony_ci			rtm->rtm_flags |= RTM_F_OFFLOAD_FAILED;
576162306a36Sopenharmony_ci	}
576262306a36Sopenharmony_ci
576362306a36Sopenharmony_ci	if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
576462306a36Sopenharmony_ci		goto nla_put_failure;
576562306a36Sopenharmony_ci
576662306a36Sopenharmony_ci	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
576762306a36Sopenharmony_ci		goto nla_put_failure;
576862306a36Sopenharmony_ci
576962306a36Sopenharmony_ci
577062306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
577162306a36Sopenharmony_ci	return 0;
577262306a36Sopenharmony_ci
577362306a36Sopenharmony_cinla_put_failure:
577462306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
577562306a36Sopenharmony_ci	return -EMSGSIZE;
577662306a36Sopenharmony_ci}
577762306a36Sopenharmony_ci
577862306a36Sopenharmony_cistatic int fib6_info_nh_uses_dev(struct fib6_nh *nh, void *arg)
577962306a36Sopenharmony_ci{
578062306a36Sopenharmony_ci	const struct net_device *dev = arg;
578162306a36Sopenharmony_ci
578262306a36Sopenharmony_ci	if (nh->fib_nh_dev == dev)
578362306a36Sopenharmony_ci		return 1;
578462306a36Sopenharmony_ci
578562306a36Sopenharmony_ci	return 0;
578662306a36Sopenharmony_ci}
578762306a36Sopenharmony_ci
578862306a36Sopenharmony_cistatic bool fib6_info_uses_dev(const struct fib6_info *f6i,
578962306a36Sopenharmony_ci			       const struct net_device *dev)
579062306a36Sopenharmony_ci{
579162306a36Sopenharmony_ci	if (f6i->nh) {
579262306a36Sopenharmony_ci		struct net_device *_dev = (struct net_device *)dev;
579362306a36Sopenharmony_ci
579462306a36Sopenharmony_ci		return !!nexthop_for_each_fib6_nh(f6i->nh,
579562306a36Sopenharmony_ci						  fib6_info_nh_uses_dev,
579662306a36Sopenharmony_ci						  _dev);
579762306a36Sopenharmony_ci	}
579862306a36Sopenharmony_ci
579962306a36Sopenharmony_ci	if (f6i->fib6_nh->fib_nh_dev == dev)
580062306a36Sopenharmony_ci		return true;
580162306a36Sopenharmony_ci
580262306a36Sopenharmony_ci	if (f6i->fib6_nsiblings) {
580362306a36Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
580462306a36Sopenharmony_ci
580562306a36Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
580662306a36Sopenharmony_ci					 &f6i->fib6_siblings, fib6_siblings) {
580762306a36Sopenharmony_ci			if (sibling->fib6_nh->fib_nh_dev == dev)
580862306a36Sopenharmony_ci				return true;
580962306a36Sopenharmony_ci		}
581062306a36Sopenharmony_ci	}
581162306a36Sopenharmony_ci
581262306a36Sopenharmony_ci	return false;
581362306a36Sopenharmony_ci}
581462306a36Sopenharmony_ci
581562306a36Sopenharmony_cistruct fib6_nh_exception_dump_walker {
581662306a36Sopenharmony_ci	struct rt6_rtnl_dump_arg *dump;
581762306a36Sopenharmony_ci	struct fib6_info *rt;
581862306a36Sopenharmony_ci	unsigned int flags;
581962306a36Sopenharmony_ci	unsigned int skip;
582062306a36Sopenharmony_ci	unsigned int count;
582162306a36Sopenharmony_ci};
582262306a36Sopenharmony_ci
582362306a36Sopenharmony_cistatic int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg)
582462306a36Sopenharmony_ci{
582562306a36Sopenharmony_ci	struct fib6_nh_exception_dump_walker *w = arg;
582662306a36Sopenharmony_ci	struct rt6_rtnl_dump_arg *dump = w->dump;
582762306a36Sopenharmony_ci	struct rt6_exception_bucket *bucket;
582862306a36Sopenharmony_ci	struct rt6_exception *rt6_ex;
582962306a36Sopenharmony_ci	int i, err;
583062306a36Sopenharmony_ci
583162306a36Sopenharmony_ci	bucket = fib6_nh_get_excptn_bucket(nh, NULL);
583262306a36Sopenharmony_ci	if (!bucket)
583362306a36Sopenharmony_ci		return 0;
583462306a36Sopenharmony_ci
583562306a36Sopenharmony_ci	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
583662306a36Sopenharmony_ci		hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
583762306a36Sopenharmony_ci			if (w->skip) {
583862306a36Sopenharmony_ci				w->skip--;
583962306a36Sopenharmony_ci				continue;
584062306a36Sopenharmony_ci			}
584162306a36Sopenharmony_ci
584262306a36Sopenharmony_ci			/* Expiration of entries doesn't bump sernum, insertion
584362306a36Sopenharmony_ci			 * does. Removal is triggered by insertion, so we can
584462306a36Sopenharmony_ci			 * rely on the fact that if entries change between two
584562306a36Sopenharmony_ci			 * partial dumps, this node is scanned again completely,
584662306a36Sopenharmony_ci			 * see rt6_insert_exception() and fib6_dump_table().
584762306a36Sopenharmony_ci			 *
584862306a36Sopenharmony_ci			 * Count expired entries we go through as handled
584962306a36Sopenharmony_ci			 * entries that we'll skip next time, in case of partial
585062306a36Sopenharmony_ci			 * node dump. Otherwise, if entries expire meanwhile,
585162306a36Sopenharmony_ci			 * we'll skip the wrong amount.
585262306a36Sopenharmony_ci			 */
585362306a36Sopenharmony_ci			if (rt6_check_expired(rt6_ex->rt6i)) {
585462306a36Sopenharmony_ci				w->count++;
585562306a36Sopenharmony_ci				continue;
585662306a36Sopenharmony_ci			}
585762306a36Sopenharmony_ci
585862306a36Sopenharmony_ci			err = rt6_fill_node(dump->net, dump->skb, w->rt,
585962306a36Sopenharmony_ci					    &rt6_ex->rt6i->dst, NULL, NULL, 0,
586062306a36Sopenharmony_ci					    RTM_NEWROUTE,
586162306a36Sopenharmony_ci					    NETLINK_CB(dump->cb->skb).portid,
586262306a36Sopenharmony_ci					    dump->cb->nlh->nlmsg_seq, w->flags);
586362306a36Sopenharmony_ci			if (err)
586462306a36Sopenharmony_ci				return err;
586562306a36Sopenharmony_ci
586662306a36Sopenharmony_ci			w->count++;
586762306a36Sopenharmony_ci		}
586862306a36Sopenharmony_ci		bucket++;
586962306a36Sopenharmony_ci	}
587062306a36Sopenharmony_ci
587162306a36Sopenharmony_ci	return 0;
587262306a36Sopenharmony_ci}
587362306a36Sopenharmony_ci
587462306a36Sopenharmony_ci/* Return -1 if done with node, number of handled routes on partial dump */
587562306a36Sopenharmony_ciint rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip)
587662306a36Sopenharmony_ci{
587762306a36Sopenharmony_ci	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
587862306a36Sopenharmony_ci	struct fib_dump_filter *filter = &arg->filter;
587962306a36Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
588062306a36Sopenharmony_ci	struct net *net = arg->net;
588162306a36Sopenharmony_ci	int count = 0;
588262306a36Sopenharmony_ci
588362306a36Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry)
588462306a36Sopenharmony_ci		return -1;
588562306a36Sopenharmony_ci
588662306a36Sopenharmony_ci	if ((filter->flags & RTM_F_PREFIX) &&
588762306a36Sopenharmony_ci	    !(rt->fib6_flags & RTF_PREFIX_RT)) {
588862306a36Sopenharmony_ci		/* success since this is not a prefix route */
588962306a36Sopenharmony_ci		return -1;
589062306a36Sopenharmony_ci	}
589162306a36Sopenharmony_ci	if (filter->filter_set &&
589262306a36Sopenharmony_ci	    ((filter->rt_type  && rt->fib6_type != filter->rt_type) ||
589362306a36Sopenharmony_ci	     (filter->dev      && !fib6_info_uses_dev(rt, filter->dev)) ||
589462306a36Sopenharmony_ci	     (filter->protocol && rt->fib6_protocol != filter->protocol))) {
589562306a36Sopenharmony_ci		return -1;
589662306a36Sopenharmony_ci	}
589762306a36Sopenharmony_ci
589862306a36Sopenharmony_ci	if (filter->filter_set ||
589962306a36Sopenharmony_ci	    !filter->dump_routes || !filter->dump_exceptions) {
590062306a36Sopenharmony_ci		flags |= NLM_F_DUMP_FILTERED;
590162306a36Sopenharmony_ci	}
590262306a36Sopenharmony_ci
590362306a36Sopenharmony_ci	if (filter->dump_routes) {
590462306a36Sopenharmony_ci		if (skip) {
590562306a36Sopenharmony_ci			skip--;
590662306a36Sopenharmony_ci		} else {
590762306a36Sopenharmony_ci			if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL,
590862306a36Sopenharmony_ci					  0, RTM_NEWROUTE,
590962306a36Sopenharmony_ci					  NETLINK_CB(arg->cb->skb).portid,
591062306a36Sopenharmony_ci					  arg->cb->nlh->nlmsg_seq, flags)) {
591162306a36Sopenharmony_ci				return 0;
591262306a36Sopenharmony_ci			}
591362306a36Sopenharmony_ci			count++;
591462306a36Sopenharmony_ci		}
591562306a36Sopenharmony_ci	}
591662306a36Sopenharmony_ci
591762306a36Sopenharmony_ci	if (filter->dump_exceptions) {
591862306a36Sopenharmony_ci		struct fib6_nh_exception_dump_walker w = { .dump = arg,
591962306a36Sopenharmony_ci							   .rt = rt,
592062306a36Sopenharmony_ci							   .flags = flags,
592162306a36Sopenharmony_ci							   .skip = skip,
592262306a36Sopenharmony_ci							   .count = 0 };
592362306a36Sopenharmony_ci		int err;
592462306a36Sopenharmony_ci
592562306a36Sopenharmony_ci		rcu_read_lock();
592662306a36Sopenharmony_ci		if (rt->nh) {
592762306a36Sopenharmony_ci			err = nexthop_for_each_fib6_nh(rt->nh,
592862306a36Sopenharmony_ci						       rt6_nh_dump_exceptions,
592962306a36Sopenharmony_ci						       &w);
593062306a36Sopenharmony_ci		} else {
593162306a36Sopenharmony_ci			err = rt6_nh_dump_exceptions(rt->fib6_nh, &w);
593262306a36Sopenharmony_ci		}
593362306a36Sopenharmony_ci		rcu_read_unlock();
593462306a36Sopenharmony_ci
593562306a36Sopenharmony_ci		if (err)
593662306a36Sopenharmony_ci			return count + w.count;
593762306a36Sopenharmony_ci	}
593862306a36Sopenharmony_ci
593962306a36Sopenharmony_ci	return -1;
594062306a36Sopenharmony_ci}
594162306a36Sopenharmony_ci
594262306a36Sopenharmony_cistatic int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
594362306a36Sopenharmony_ci					const struct nlmsghdr *nlh,
594462306a36Sopenharmony_ci					struct nlattr **tb,
594562306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
594662306a36Sopenharmony_ci{
594762306a36Sopenharmony_ci	struct rtmsg *rtm;
594862306a36Sopenharmony_ci	int i, err;
594962306a36Sopenharmony_ci
595062306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
595162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
595262306a36Sopenharmony_ci				   "Invalid header for get route request");
595362306a36Sopenharmony_ci		return -EINVAL;
595462306a36Sopenharmony_ci	}
595562306a36Sopenharmony_ci
595662306a36Sopenharmony_ci	if (!netlink_strict_get_check(skb))
595762306a36Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
595862306a36Sopenharmony_ci					      rtm_ipv6_policy, extack);
595962306a36Sopenharmony_ci
596062306a36Sopenharmony_ci	rtm = nlmsg_data(nlh);
596162306a36Sopenharmony_ci	if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
596262306a36Sopenharmony_ci	    (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
596362306a36Sopenharmony_ci	    rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
596462306a36Sopenharmony_ci	    rtm->rtm_type) {
596562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
596662306a36Sopenharmony_ci		return -EINVAL;
596762306a36Sopenharmony_ci	}
596862306a36Sopenharmony_ci	if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
596962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
597062306a36Sopenharmony_ci				   "Invalid flags for get route request");
597162306a36Sopenharmony_ci		return -EINVAL;
597262306a36Sopenharmony_ci	}
597362306a36Sopenharmony_ci
597462306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
597562306a36Sopenharmony_ci					    rtm_ipv6_policy, extack);
597662306a36Sopenharmony_ci	if (err)
597762306a36Sopenharmony_ci		return err;
597862306a36Sopenharmony_ci
597962306a36Sopenharmony_ci	if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
598062306a36Sopenharmony_ci	    (tb[RTA_DST] && !rtm->rtm_dst_len)) {
598162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
598262306a36Sopenharmony_ci		return -EINVAL;
598362306a36Sopenharmony_ci	}
598462306a36Sopenharmony_ci
598562306a36Sopenharmony_ci	for (i = 0; i <= RTA_MAX; i++) {
598662306a36Sopenharmony_ci		if (!tb[i])
598762306a36Sopenharmony_ci			continue;
598862306a36Sopenharmony_ci
598962306a36Sopenharmony_ci		switch (i) {
599062306a36Sopenharmony_ci		case RTA_SRC:
599162306a36Sopenharmony_ci		case RTA_DST:
599262306a36Sopenharmony_ci		case RTA_IIF:
599362306a36Sopenharmony_ci		case RTA_OIF:
599462306a36Sopenharmony_ci		case RTA_MARK:
599562306a36Sopenharmony_ci		case RTA_UID:
599662306a36Sopenharmony_ci		case RTA_SPORT:
599762306a36Sopenharmony_ci		case RTA_DPORT:
599862306a36Sopenharmony_ci		case RTA_IP_PROTO:
599962306a36Sopenharmony_ci			break;
600062306a36Sopenharmony_ci		default:
600162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
600262306a36Sopenharmony_ci			return -EINVAL;
600362306a36Sopenharmony_ci		}
600462306a36Sopenharmony_ci	}
600562306a36Sopenharmony_ci
600662306a36Sopenharmony_ci	return 0;
600762306a36Sopenharmony_ci}
600862306a36Sopenharmony_ci
600962306a36Sopenharmony_cistatic int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
601062306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
601162306a36Sopenharmony_ci{
601262306a36Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
601362306a36Sopenharmony_ci	struct nlattr *tb[RTA_MAX+1];
601462306a36Sopenharmony_ci	int err, iif = 0, oif = 0;
601562306a36Sopenharmony_ci	struct fib6_info *from;
601662306a36Sopenharmony_ci	struct dst_entry *dst;
601762306a36Sopenharmony_ci	struct rt6_info *rt;
601862306a36Sopenharmony_ci	struct sk_buff *skb;
601962306a36Sopenharmony_ci	struct rtmsg *rtm;
602062306a36Sopenharmony_ci	struct flowi6 fl6 = {};
602162306a36Sopenharmony_ci	bool fibmatch;
602262306a36Sopenharmony_ci
602362306a36Sopenharmony_ci	err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
602462306a36Sopenharmony_ci	if (err < 0)
602562306a36Sopenharmony_ci		goto errout;
602662306a36Sopenharmony_ci
602762306a36Sopenharmony_ci	err = -EINVAL;
602862306a36Sopenharmony_ci	rtm = nlmsg_data(nlh);
602962306a36Sopenharmony_ci	fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
603062306a36Sopenharmony_ci	fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
603162306a36Sopenharmony_ci
603262306a36Sopenharmony_ci	if (tb[RTA_SRC]) {
603362306a36Sopenharmony_ci		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
603462306a36Sopenharmony_ci			goto errout;
603562306a36Sopenharmony_ci
603662306a36Sopenharmony_ci		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
603762306a36Sopenharmony_ci	}
603862306a36Sopenharmony_ci
603962306a36Sopenharmony_ci	if (tb[RTA_DST]) {
604062306a36Sopenharmony_ci		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
604162306a36Sopenharmony_ci			goto errout;
604262306a36Sopenharmony_ci
604362306a36Sopenharmony_ci		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
604462306a36Sopenharmony_ci	}
604562306a36Sopenharmony_ci
604662306a36Sopenharmony_ci	if (tb[RTA_IIF])
604762306a36Sopenharmony_ci		iif = nla_get_u32(tb[RTA_IIF]);
604862306a36Sopenharmony_ci
604962306a36Sopenharmony_ci	if (tb[RTA_OIF])
605062306a36Sopenharmony_ci		oif = nla_get_u32(tb[RTA_OIF]);
605162306a36Sopenharmony_ci
605262306a36Sopenharmony_ci	if (tb[RTA_MARK])
605362306a36Sopenharmony_ci		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
605462306a36Sopenharmony_ci
605562306a36Sopenharmony_ci	if (tb[RTA_UID])
605662306a36Sopenharmony_ci		fl6.flowi6_uid = make_kuid(current_user_ns(),
605762306a36Sopenharmony_ci					   nla_get_u32(tb[RTA_UID]));
605862306a36Sopenharmony_ci	else
605962306a36Sopenharmony_ci		fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
606062306a36Sopenharmony_ci
606162306a36Sopenharmony_ci	if (tb[RTA_SPORT])
606262306a36Sopenharmony_ci		fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
606362306a36Sopenharmony_ci
606462306a36Sopenharmony_ci	if (tb[RTA_DPORT])
606562306a36Sopenharmony_ci		fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
606662306a36Sopenharmony_ci
606762306a36Sopenharmony_ci	if (tb[RTA_IP_PROTO]) {
606862306a36Sopenharmony_ci		err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
606962306a36Sopenharmony_ci						  &fl6.flowi6_proto, AF_INET6,
607062306a36Sopenharmony_ci						  extack);
607162306a36Sopenharmony_ci		if (err)
607262306a36Sopenharmony_ci			goto errout;
607362306a36Sopenharmony_ci	}
607462306a36Sopenharmony_ci
607562306a36Sopenharmony_ci	if (iif) {
607662306a36Sopenharmony_ci		struct net_device *dev;
607762306a36Sopenharmony_ci		int flags = 0;
607862306a36Sopenharmony_ci
607962306a36Sopenharmony_ci		rcu_read_lock();
608062306a36Sopenharmony_ci
608162306a36Sopenharmony_ci		dev = dev_get_by_index_rcu(net, iif);
608262306a36Sopenharmony_ci		if (!dev) {
608362306a36Sopenharmony_ci			rcu_read_unlock();
608462306a36Sopenharmony_ci			err = -ENODEV;
608562306a36Sopenharmony_ci			goto errout;
608662306a36Sopenharmony_ci		}
608762306a36Sopenharmony_ci
608862306a36Sopenharmony_ci		fl6.flowi6_iif = iif;
608962306a36Sopenharmony_ci
609062306a36Sopenharmony_ci		if (!ipv6_addr_any(&fl6.saddr))
609162306a36Sopenharmony_ci			flags |= RT6_LOOKUP_F_HAS_SADDR;
609262306a36Sopenharmony_ci
609362306a36Sopenharmony_ci		dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
609462306a36Sopenharmony_ci
609562306a36Sopenharmony_ci		rcu_read_unlock();
609662306a36Sopenharmony_ci	} else {
609762306a36Sopenharmony_ci		fl6.flowi6_oif = oif;
609862306a36Sopenharmony_ci
609962306a36Sopenharmony_ci		dst = ip6_route_output(net, NULL, &fl6);
610062306a36Sopenharmony_ci	}
610162306a36Sopenharmony_ci
610262306a36Sopenharmony_ci
610362306a36Sopenharmony_ci	rt = container_of(dst, struct rt6_info, dst);
610462306a36Sopenharmony_ci	if (rt->dst.error) {
610562306a36Sopenharmony_ci		err = rt->dst.error;
610662306a36Sopenharmony_ci		ip6_rt_put(rt);
610762306a36Sopenharmony_ci		goto errout;
610862306a36Sopenharmony_ci	}
610962306a36Sopenharmony_ci
611062306a36Sopenharmony_ci	if (rt == net->ipv6.ip6_null_entry) {
611162306a36Sopenharmony_ci		err = rt->dst.error;
611262306a36Sopenharmony_ci		ip6_rt_put(rt);
611362306a36Sopenharmony_ci		goto errout;
611462306a36Sopenharmony_ci	}
611562306a36Sopenharmony_ci
611662306a36Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
611762306a36Sopenharmony_ci	if (!skb) {
611862306a36Sopenharmony_ci		ip6_rt_put(rt);
611962306a36Sopenharmony_ci		err = -ENOBUFS;
612062306a36Sopenharmony_ci		goto errout;
612162306a36Sopenharmony_ci	}
612262306a36Sopenharmony_ci
612362306a36Sopenharmony_ci	skb_dst_set(skb, &rt->dst);
612462306a36Sopenharmony_ci
612562306a36Sopenharmony_ci	rcu_read_lock();
612662306a36Sopenharmony_ci	from = rcu_dereference(rt->from);
612762306a36Sopenharmony_ci	if (from) {
612862306a36Sopenharmony_ci		if (fibmatch)
612962306a36Sopenharmony_ci			err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
613062306a36Sopenharmony_ci					    iif, RTM_NEWROUTE,
613162306a36Sopenharmony_ci					    NETLINK_CB(in_skb).portid,
613262306a36Sopenharmony_ci					    nlh->nlmsg_seq, 0);
613362306a36Sopenharmony_ci		else
613462306a36Sopenharmony_ci			err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
613562306a36Sopenharmony_ci					    &fl6.saddr, iif, RTM_NEWROUTE,
613662306a36Sopenharmony_ci					    NETLINK_CB(in_skb).portid,
613762306a36Sopenharmony_ci					    nlh->nlmsg_seq, 0);
613862306a36Sopenharmony_ci	} else {
613962306a36Sopenharmony_ci		err = -ENETUNREACH;
614062306a36Sopenharmony_ci	}
614162306a36Sopenharmony_ci	rcu_read_unlock();
614262306a36Sopenharmony_ci
614362306a36Sopenharmony_ci	if (err < 0) {
614462306a36Sopenharmony_ci		kfree_skb(skb);
614562306a36Sopenharmony_ci		goto errout;
614662306a36Sopenharmony_ci	}
614762306a36Sopenharmony_ci
614862306a36Sopenharmony_ci	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
614962306a36Sopenharmony_cierrout:
615062306a36Sopenharmony_ci	return err;
615162306a36Sopenharmony_ci}
615262306a36Sopenharmony_ci
615362306a36Sopenharmony_civoid inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
615462306a36Sopenharmony_ci		     unsigned int nlm_flags)
615562306a36Sopenharmony_ci{
615662306a36Sopenharmony_ci	struct sk_buff *skb;
615762306a36Sopenharmony_ci	struct net *net = info->nl_net;
615862306a36Sopenharmony_ci	u32 seq;
615962306a36Sopenharmony_ci	int err;
616062306a36Sopenharmony_ci
616162306a36Sopenharmony_ci	err = -ENOBUFS;
616262306a36Sopenharmony_ci	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
616362306a36Sopenharmony_ci
616462306a36Sopenharmony_ci	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
616562306a36Sopenharmony_ci	if (!skb)
616662306a36Sopenharmony_ci		goto errout;
616762306a36Sopenharmony_ci
616862306a36Sopenharmony_ci	err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
616962306a36Sopenharmony_ci			    event, info->portid, seq, nlm_flags);
617062306a36Sopenharmony_ci	if (err < 0) {
617162306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
617262306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
617362306a36Sopenharmony_ci		kfree_skb(skb);
617462306a36Sopenharmony_ci		goto errout;
617562306a36Sopenharmony_ci	}
617662306a36Sopenharmony_ci	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
617762306a36Sopenharmony_ci		    info->nlh, gfp_any());
617862306a36Sopenharmony_ci	return;
617962306a36Sopenharmony_cierrout:
618062306a36Sopenharmony_ci	if (err < 0)
618162306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
618262306a36Sopenharmony_ci}
618362306a36Sopenharmony_ci
618462306a36Sopenharmony_civoid fib6_rt_update(struct net *net, struct fib6_info *rt,
618562306a36Sopenharmony_ci		    struct nl_info *info)
618662306a36Sopenharmony_ci{
618762306a36Sopenharmony_ci	u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
618862306a36Sopenharmony_ci	struct sk_buff *skb;
618962306a36Sopenharmony_ci	int err = -ENOBUFS;
619062306a36Sopenharmony_ci
619162306a36Sopenharmony_ci	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
619262306a36Sopenharmony_ci	if (!skb)
619362306a36Sopenharmony_ci		goto errout;
619462306a36Sopenharmony_ci
619562306a36Sopenharmony_ci	err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
619662306a36Sopenharmony_ci			    RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE);
619762306a36Sopenharmony_ci	if (err < 0) {
619862306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
619962306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
620062306a36Sopenharmony_ci		kfree_skb(skb);
620162306a36Sopenharmony_ci		goto errout;
620262306a36Sopenharmony_ci	}
620362306a36Sopenharmony_ci	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
620462306a36Sopenharmony_ci		    info->nlh, gfp_any());
620562306a36Sopenharmony_ci	return;
620662306a36Sopenharmony_cierrout:
620762306a36Sopenharmony_ci	if (err < 0)
620862306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
620962306a36Sopenharmony_ci}
621062306a36Sopenharmony_ci
621162306a36Sopenharmony_civoid fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
621262306a36Sopenharmony_ci			    bool offload, bool trap, bool offload_failed)
621362306a36Sopenharmony_ci{
621462306a36Sopenharmony_ci	struct sk_buff *skb;
621562306a36Sopenharmony_ci	int err;
621662306a36Sopenharmony_ci
621762306a36Sopenharmony_ci	if (READ_ONCE(f6i->offload) == offload &&
621862306a36Sopenharmony_ci	    READ_ONCE(f6i->trap) == trap &&
621962306a36Sopenharmony_ci	    READ_ONCE(f6i->offload_failed) == offload_failed)
622062306a36Sopenharmony_ci		return;
622162306a36Sopenharmony_ci
622262306a36Sopenharmony_ci	WRITE_ONCE(f6i->offload, offload);
622362306a36Sopenharmony_ci	WRITE_ONCE(f6i->trap, trap);
622462306a36Sopenharmony_ci
622562306a36Sopenharmony_ci	/* 2 means send notifications only if offload_failed was changed. */
622662306a36Sopenharmony_ci	if (net->ipv6.sysctl.fib_notify_on_flag_change == 2 &&
622762306a36Sopenharmony_ci	    READ_ONCE(f6i->offload_failed) == offload_failed)
622862306a36Sopenharmony_ci		return;
622962306a36Sopenharmony_ci
623062306a36Sopenharmony_ci	WRITE_ONCE(f6i->offload_failed, offload_failed);
623162306a36Sopenharmony_ci
623262306a36Sopenharmony_ci	if (!rcu_access_pointer(f6i->fib6_node))
623362306a36Sopenharmony_ci		/* The route was removed from the tree, do not send
623462306a36Sopenharmony_ci		 * notification.
623562306a36Sopenharmony_ci		 */
623662306a36Sopenharmony_ci		return;
623762306a36Sopenharmony_ci
623862306a36Sopenharmony_ci	if (!net->ipv6.sysctl.fib_notify_on_flag_change)
623962306a36Sopenharmony_ci		return;
624062306a36Sopenharmony_ci
624162306a36Sopenharmony_ci	skb = nlmsg_new(rt6_nlmsg_size(f6i), GFP_KERNEL);
624262306a36Sopenharmony_ci	if (!skb) {
624362306a36Sopenharmony_ci		err = -ENOBUFS;
624462306a36Sopenharmony_ci		goto errout;
624562306a36Sopenharmony_ci	}
624662306a36Sopenharmony_ci
624762306a36Sopenharmony_ci	err = rt6_fill_node(net, skb, f6i, NULL, NULL, NULL, 0, RTM_NEWROUTE, 0,
624862306a36Sopenharmony_ci			    0, 0);
624962306a36Sopenharmony_ci	if (err < 0) {
625062306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
625162306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
625262306a36Sopenharmony_ci		kfree_skb(skb);
625362306a36Sopenharmony_ci		goto errout;
625462306a36Sopenharmony_ci	}
625562306a36Sopenharmony_ci
625662306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_ROUTE, NULL, GFP_KERNEL);
625762306a36Sopenharmony_ci	return;
625862306a36Sopenharmony_ci
625962306a36Sopenharmony_cierrout:
626062306a36Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
626162306a36Sopenharmony_ci}
626262306a36Sopenharmony_ciEXPORT_SYMBOL(fib6_info_hw_flags_set);
626362306a36Sopenharmony_ci
626462306a36Sopenharmony_cistatic int ip6_route_dev_notify(struct notifier_block *this,
626562306a36Sopenharmony_ci				unsigned long event, void *ptr)
626662306a36Sopenharmony_ci{
626762306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
626862306a36Sopenharmony_ci	struct net *net = dev_net(dev);
626962306a36Sopenharmony_ci
627062306a36Sopenharmony_ci	if (!(dev->flags & IFF_LOOPBACK))
627162306a36Sopenharmony_ci		return NOTIFY_OK;
627262306a36Sopenharmony_ci
627362306a36Sopenharmony_ci	if (event == NETDEV_REGISTER) {
627462306a36Sopenharmony_ci		net->ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = dev;
627562306a36Sopenharmony_ci		net->ipv6.ip6_null_entry->dst.dev = dev;
627662306a36Sopenharmony_ci		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
627762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
627862306a36Sopenharmony_ci		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
627962306a36Sopenharmony_ci		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
628062306a36Sopenharmony_ci		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
628162306a36Sopenharmony_ci		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
628262306a36Sopenharmony_ci#endif
628362306a36Sopenharmony_ci	 } else if (event == NETDEV_UNREGISTER &&
628462306a36Sopenharmony_ci		    dev->reg_state != NETREG_UNREGISTERED) {
628562306a36Sopenharmony_ci		/* NETDEV_UNREGISTER could be fired for multiple times by
628662306a36Sopenharmony_ci		 * netdev_wait_allrefs(). Make sure we only call this once.
628762306a36Sopenharmony_ci		 */
628862306a36Sopenharmony_ci		in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
628962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
629062306a36Sopenharmony_ci		in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
629162306a36Sopenharmony_ci		in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
629262306a36Sopenharmony_ci#endif
629362306a36Sopenharmony_ci	}
629462306a36Sopenharmony_ci
629562306a36Sopenharmony_ci	return NOTIFY_OK;
629662306a36Sopenharmony_ci}
629762306a36Sopenharmony_ci
629862306a36Sopenharmony_ci/*
629962306a36Sopenharmony_ci *	/proc
630062306a36Sopenharmony_ci */
630162306a36Sopenharmony_ci
630262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
630362306a36Sopenharmony_cistatic int rt6_stats_seq_show(struct seq_file *seq, void *v)
630462306a36Sopenharmony_ci{
630562306a36Sopenharmony_ci	struct net *net = (struct net *)seq->private;
630662306a36Sopenharmony_ci	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
630762306a36Sopenharmony_ci		   net->ipv6.rt6_stats->fib_nodes,
630862306a36Sopenharmony_ci		   net->ipv6.rt6_stats->fib_route_nodes,
630962306a36Sopenharmony_ci		   atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
631062306a36Sopenharmony_ci		   net->ipv6.rt6_stats->fib_rt_entries,
631162306a36Sopenharmony_ci		   net->ipv6.rt6_stats->fib_rt_cache,
631262306a36Sopenharmony_ci		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
631362306a36Sopenharmony_ci		   net->ipv6.rt6_stats->fib_discarded_routes);
631462306a36Sopenharmony_ci
631562306a36Sopenharmony_ci	return 0;
631662306a36Sopenharmony_ci}
631762306a36Sopenharmony_ci#endif	/* CONFIG_PROC_FS */
631862306a36Sopenharmony_ci
631962306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
632062306a36Sopenharmony_ci
632162306a36Sopenharmony_cistatic int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
632262306a36Sopenharmony_ci			      void *buffer, size_t *lenp, loff_t *ppos)
632362306a36Sopenharmony_ci{
632462306a36Sopenharmony_ci	struct net *net;
632562306a36Sopenharmony_ci	int delay;
632662306a36Sopenharmony_ci	int ret;
632762306a36Sopenharmony_ci	if (!write)
632862306a36Sopenharmony_ci		return -EINVAL;
632962306a36Sopenharmony_ci
633062306a36Sopenharmony_ci	net = (struct net *)ctl->extra1;
633162306a36Sopenharmony_ci	delay = net->ipv6.sysctl.flush_delay;
633262306a36Sopenharmony_ci	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
633362306a36Sopenharmony_ci	if (ret)
633462306a36Sopenharmony_ci		return ret;
633562306a36Sopenharmony_ci
633662306a36Sopenharmony_ci	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
633762306a36Sopenharmony_ci	return 0;
633862306a36Sopenharmony_ci}
633962306a36Sopenharmony_ci
634062306a36Sopenharmony_cistatic struct ctl_table ipv6_route_table_template[] = {
634162306a36Sopenharmony_ci	{
634262306a36Sopenharmony_ci		.procname	=	"max_size",
634362306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
634462306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
634562306a36Sopenharmony_ci		.mode		=	0644,
634662306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec,
634762306a36Sopenharmony_ci	},
634862306a36Sopenharmony_ci	{
634962306a36Sopenharmony_ci		.procname	=	"gc_thresh",
635062306a36Sopenharmony_ci		.data		=	&ip6_dst_ops_template.gc_thresh,
635162306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
635262306a36Sopenharmony_ci		.mode		=	0644,
635362306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec,
635462306a36Sopenharmony_ci	},
635562306a36Sopenharmony_ci	{
635662306a36Sopenharmony_ci		.procname	=	"flush",
635762306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.flush_delay,
635862306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
635962306a36Sopenharmony_ci		.mode		=	0200,
636062306a36Sopenharmony_ci		.proc_handler	=	ipv6_sysctl_rtcache_flush
636162306a36Sopenharmony_ci	},
636262306a36Sopenharmony_ci	{
636362306a36Sopenharmony_ci		.procname	=	"gc_min_interval",
636462306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
636562306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
636662306a36Sopenharmony_ci		.mode		=	0644,
636762306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
636862306a36Sopenharmony_ci	},
636962306a36Sopenharmony_ci	{
637062306a36Sopenharmony_ci		.procname	=	"gc_timeout",
637162306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
637262306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
637362306a36Sopenharmony_ci		.mode		=	0644,
637462306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
637562306a36Sopenharmony_ci	},
637662306a36Sopenharmony_ci	{
637762306a36Sopenharmony_ci		.procname	=	"gc_interval",
637862306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
637962306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
638062306a36Sopenharmony_ci		.mode		=	0644,
638162306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
638262306a36Sopenharmony_ci	},
638362306a36Sopenharmony_ci	{
638462306a36Sopenharmony_ci		.procname	=	"gc_elasticity",
638562306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
638662306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
638762306a36Sopenharmony_ci		.mode		=	0644,
638862306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec,
638962306a36Sopenharmony_ci	},
639062306a36Sopenharmony_ci	{
639162306a36Sopenharmony_ci		.procname	=	"mtu_expires",
639262306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
639362306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
639462306a36Sopenharmony_ci		.mode		=	0644,
639562306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec_jiffies,
639662306a36Sopenharmony_ci	},
639762306a36Sopenharmony_ci	{
639862306a36Sopenharmony_ci		.procname	=	"min_adv_mss",
639962306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
640062306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
640162306a36Sopenharmony_ci		.mode		=	0644,
640262306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec,
640362306a36Sopenharmony_ci	},
640462306a36Sopenharmony_ci	{
640562306a36Sopenharmony_ci		.procname	=	"gc_min_interval_ms",
640662306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
640762306a36Sopenharmony_ci		.maxlen		=	sizeof(int),
640862306a36Sopenharmony_ci		.mode		=	0644,
640962306a36Sopenharmony_ci		.proc_handler	=	proc_dointvec_ms_jiffies,
641062306a36Sopenharmony_ci	},
641162306a36Sopenharmony_ci	{
641262306a36Sopenharmony_ci		.procname	=	"skip_notify_on_dev_down",
641362306a36Sopenharmony_ci		.data		=	&init_net.ipv6.sysctl.skip_notify_on_dev_down,
641462306a36Sopenharmony_ci		.maxlen		=	sizeof(u8),
641562306a36Sopenharmony_ci		.mode		=	0644,
641662306a36Sopenharmony_ci		.proc_handler	=	proc_dou8vec_minmax,
641762306a36Sopenharmony_ci		.extra1		=	SYSCTL_ZERO,
641862306a36Sopenharmony_ci		.extra2		=	SYSCTL_ONE,
641962306a36Sopenharmony_ci	},
642062306a36Sopenharmony_ci	{ }
642162306a36Sopenharmony_ci};
642262306a36Sopenharmony_ci
642362306a36Sopenharmony_cistruct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
642462306a36Sopenharmony_ci{
642562306a36Sopenharmony_ci	struct ctl_table *table;
642662306a36Sopenharmony_ci
642762306a36Sopenharmony_ci	table = kmemdup(ipv6_route_table_template,
642862306a36Sopenharmony_ci			sizeof(ipv6_route_table_template),
642962306a36Sopenharmony_ci			GFP_KERNEL);
643062306a36Sopenharmony_ci
643162306a36Sopenharmony_ci	if (table) {
643262306a36Sopenharmony_ci		table[0].data = &net->ipv6.sysctl.ip6_rt_max_size;
643362306a36Sopenharmony_ci		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
643462306a36Sopenharmony_ci		table[2].data = &net->ipv6.sysctl.flush_delay;
643562306a36Sopenharmony_ci		table[2].extra1 = net;
643662306a36Sopenharmony_ci		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
643762306a36Sopenharmony_ci		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
643862306a36Sopenharmony_ci		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
643962306a36Sopenharmony_ci		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
644062306a36Sopenharmony_ci		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
644162306a36Sopenharmony_ci		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
644262306a36Sopenharmony_ci		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
644362306a36Sopenharmony_ci		table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
644462306a36Sopenharmony_ci
644562306a36Sopenharmony_ci		/* Don't export sysctls to unprivileged users */
644662306a36Sopenharmony_ci		if (net->user_ns != &init_user_ns)
644762306a36Sopenharmony_ci			table[1].procname = NULL;
644862306a36Sopenharmony_ci	}
644962306a36Sopenharmony_ci
645062306a36Sopenharmony_ci	return table;
645162306a36Sopenharmony_ci}
645262306a36Sopenharmony_ci
645362306a36Sopenharmony_cisize_t ipv6_route_sysctl_table_size(struct net *net)
645462306a36Sopenharmony_ci{
645562306a36Sopenharmony_ci	/* Don't export sysctls to unprivileged users */
645662306a36Sopenharmony_ci	if (net->user_ns != &init_user_ns)
645762306a36Sopenharmony_ci		return 1;
645862306a36Sopenharmony_ci
645962306a36Sopenharmony_ci	return ARRAY_SIZE(ipv6_route_table_template);
646062306a36Sopenharmony_ci}
646162306a36Sopenharmony_ci#endif
646262306a36Sopenharmony_ci
646362306a36Sopenharmony_cistatic int __net_init ip6_route_net_init(struct net *net)
646462306a36Sopenharmony_ci{
646562306a36Sopenharmony_ci	int ret = -ENOMEM;
646662306a36Sopenharmony_ci
646762306a36Sopenharmony_ci	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
646862306a36Sopenharmony_ci	       sizeof(net->ipv6.ip6_dst_ops));
646962306a36Sopenharmony_ci
647062306a36Sopenharmony_ci	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
647162306a36Sopenharmony_ci		goto out_ip6_dst_ops;
647262306a36Sopenharmony_ci
647362306a36Sopenharmony_ci	net->ipv6.fib6_null_entry = fib6_info_alloc(GFP_KERNEL, true);
647462306a36Sopenharmony_ci	if (!net->ipv6.fib6_null_entry)
647562306a36Sopenharmony_ci		goto out_ip6_dst_entries;
647662306a36Sopenharmony_ci	memcpy(net->ipv6.fib6_null_entry, &fib6_null_entry_template,
647762306a36Sopenharmony_ci	       sizeof(*net->ipv6.fib6_null_entry));
647862306a36Sopenharmony_ci
647962306a36Sopenharmony_ci	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
648062306a36Sopenharmony_ci					   sizeof(*net->ipv6.ip6_null_entry),
648162306a36Sopenharmony_ci					   GFP_KERNEL);
648262306a36Sopenharmony_ci	if (!net->ipv6.ip6_null_entry)
648362306a36Sopenharmony_ci		goto out_fib6_null_entry;
648462306a36Sopenharmony_ci	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
648562306a36Sopenharmony_ci	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
648662306a36Sopenharmony_ci			 ip6_template_metrics, true);
648762306a36Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.ip6_null_entry->dst.rt_uncached);
648862306a36Sopenharmony_ci
648962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
649062306a36Sopenharmony_ci	net->ipv6.fib6_has_custom_rules = false;
649162306a36Sopenharmony_ci	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
649262306a36Sopenharmony_ci					       sizeof(*net->ipv6.ip6_prohibit_entry),
649362306a36Sopenharmony_ci					       GFP_KERNEL);
649462306a36Sopenharmony_ci	if (!net->ipv6.ip6_prohibit_entry)
649562306a36Sopenharmony_ci		goto out_ip6_null_entry;
649662306a36Sopenharmony_ci	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
649762306a36Sopenharmony_ci	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
649862306a36Sopenharmony_ci			 ip6_template_metrics, true);
649962306a36Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->dst.rt_uncached);
650062306a36Sopenharmony_ci
650162306a36Sopenharmony_ci	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
650262306a36Sopenharmony_ci					       sizeof(*net->ipv6.ip6_blk_hole_entry),
650362306a36Sopenharmony_ci					       GFP_KERNEL);
650462306a36Sopenharmony_ci	if (!net->ipv6.ip6_blk_hole_entry)
650562306a36Sopenharmony_ci		goto out_ip6_prohibit_entry;
650662306a36Sopenharmony_ci	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
650762306a36Sopenharmony_ci	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
650862306a36Sopenharmony_ci			 ip6_template_metrics, true);
650962306a36Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.ip6_blk_hole_entry->dst.rt_uncached);
651062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
651162306a36Sopenharmony_ci	net->ipv6.fib6_routes_require_src = 0;
651262306a36Sopenharmony_ci#endif
651362306a36Sopenharmony_ci#endif
651462306a36Sopenharmony_ci
651562306a36Sopenharmony_ci	net->ipv6.sysctl.flush_delay = 0;
651662306a36Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_max_size = INT_MAX;
651762306a36Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
651862306a36Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
651962306a36Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
652062306a36Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
652162306a36Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
652262306a36Sopenharmony_ci	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
652362306a36Sopenharmony_ci	net->ipv6.sysctl.skip_notify_on_dev_down = 0;
652462306a36Sopenharmony_ci
652562306a36Sopenharmony_ci	atomic_set(&net->ipv6.ip6_rt_gc_expire, 30*HZ);
652662306a36Sopenharmony_ci
652762306a36Sopenharmony_ci	ret = 0;
652862306a36Sopenharmony_ciout:
652962306a36Sopenharmony_ci	return ret;
653062306a36Sopenharmony_ci
653162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
653262306a36Sopenharmony_ciout_ip6_prohibit_entry:
653362306a36Sopenharmony_ci	kfree(net->ipv6.ip6_prohibit_entry);
653462306a36Sopenharmony_ciout_ip6_null_entry:
653562306a36Sopenharmony_ci	kfree(net->ipv6.ip6_null_entry);
653662306a36Sopenharmony_ci#endif
653762306a36Sopenharmony_ciout_fib6_null_entry:
653862306a36Sopenharmony_ci	kfree(net->ipv6.fib6_null_entry);
653962306a36Sopenharmony_ciout_ip6_dst_entries:
654062306a36Sopenharmony_ci	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
654162306a36Sopenharmony_ciout_ip6_dst_ops:
654262306a36Sopenharmony_ci	goto out;
654362306a36Sopenharmony_ci}
654462306a36Sopenharmony_ci
654562306a36Sopenharmony_cistatic void __net_exit ip6_route_net_exit(struct net *net)
654662306a36Sopenharmony_ci{
654762306a36Sopenharmony_ci	kfree(net->ipv6.fib6_null_entry);
654862306a36Sopenharmony_ci	kfree(net->ipv6.ip6_null_entry);
654962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
655062306a36Sopenharmony_ci	kfree(net->ipv6.ip6_prohibit_entry);
655162306a36Sopenharmony_ci	kfree(net->ipv6.ip6_blk_hole_entry);
655262306a36Sopenharmony_ci#endif
655362306a36Sopenharmony_ci	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
655462306a36Sopenharmony_ci}
655562306a36Sopenharmony_ci
655662306a36Sopenharmony_cistatic int __net_init ip6_route_net_init_late(struct net *net)
655762306a36Sopenharmony_ci{
655862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
655962306a36Sopenharmony_ci	if (!proc_create_net("ipv6_route", 0, net->proc_net,
656062306a36Sopenharmony_ci			     &ipv6_route_seq_ops,
656162306a36Sopenharmony_ci			     sizeof(struct ipv6_route_iter)))
656262306a36Sopenharmony_ci		return -ENOMEM;
656362306a36Sopenharmony_ci
656462306a36Sopenharmony_ci	if (!proc_create_net_single("rt6_stats", 0444, net->proc_net,
656562306a36Sopenharmony_ci				    rt6_stats_seq_show, NULL)) {
656662306a36Sopenharmony_ci		remove_proc_entry("ipv6_route", net->proc_net);
656762306a36Sopenharmony_ci		return -ENOMEM;
656862306a36Sopenharmony_ci	}
656962306a36Sopenharmony_ci#endif
657062306a36Sopenharmony_ci	return 0;
657162306a36Sopenharmony_ci}
657262306a36Sopenharmony_ci
657362306a36Sopenharmony_cistatic void __net_exit ip6_route_net_exit_late(struct net *net)
657462306a36Sopenharmony_ci{
657562306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
657662306a36Sopenharmony_ci	remove_proc_entry("ipv6_route", net->proc_net);
657762306a36Sopenharmony_ci	remove_proc_entry("rt6_stats", net->proc_net);
657862306a36Sopenharmony_ci#endif
657962306a36Sopenharmony_ci}
658062306a36Sopenharmony_ci
658162306a36Sopenharmony_cistatic struct pernet_operations ip6_route_net_ops = {
658262306a36Sopenharmony_ci	.init = ip6_route_net_init,
658362306a36Sopenharmony_ci	.exit = ip6_route_net_exit,
658462306a36Sopenharmony_ci};
658562306a36Sopenharmony_ci
658662306a36Sopenharmony_cistatic int __net_init ipv6_inetpeer_init(struct net *net)
658762306a36Sopenharmony_ci{
658862306a36Sopenharmony_ci	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
658962306a36Sopenharmony_ci
659062306a36Sopenharmony_ci	if (!bp)
659162306a36Sopenharmony_ci		return -ENOMEM;
659262306a36Sopenharmony_ci	inet_peer_base_init(bp);
659362306a36Sopenharmony_ci	net->ipv6.peers = bp;
659462306a36Sopenharmony_ci	return 0;
659562306a36Sopenharmony_ci}
659662306a36Sopenharmony_ci
659762306a36Sopenharmony_cistatic void __net_exit ipv6_inetpeer_exit(struct net *net)
659862306a36Sopenharmony_ci{
659962306a36Sopenharmony_ci	struct inet_peer_base *bp = net->ipv6.peers;
660062306a36Sopenharmony_ci
660162306a36Sopenharmony_ci	net->ipv6.peers = NULL;
660262306a36Sopenharmony_ci	inetpeer_invalidate_tree(bp);
660362306a36Sopenharmony_ci	kfree(bp);
660462306a36Sopenharmony_ci}
660562306a36Sopenharmony_ci
660662306a36Sopenharmony_cistatic struct pernet_operations ipv6_inetpeer_ops = {
660762306a36Sopenharmony_ci	.init	=	ipv6_inetpeer_init,
660862306a36Sopenharmony_ci	.exit	=	ipv6_inetpeer_exit,
660962306a36Sopenharmony_ci};
661062306a36Sopenharmony_ci
661162306a36Sopenharmony_cistatic struct pernet_operations ip6_route_net_late_ops = {
661262306a36Sopenharmony_ci	.init = ip6_route_net_init_late,
661362306a36Sopenharmony_ci	.exit = ip6_route_net_exit_late,
661462306a36Sopenharmony_ci};
661562306a36Sopenharmony_ci
661662306a36Sopenharmony_cistatic struct notifier_block ip6_route_dev_notifier = {
661762306a36Sopenharmony_ci	.notifier_call = ip6_route_dev_notify,
661862306a36Sopenharmony_ci	.priority = ADDRCONF_NOTIFY_PRIORITY - 10,
661962306a36Sopenharmony_ci};
662062306a36Sopenharmony_ci
662162306a36Sopenharmony_civoid __init ip6_route_init_special_entries(void)
662262306a36Sopenharmony_ci{
662362306a36Sopenharmony_ci	/* Registering of the loopback is done before this portion of code,
662462306a36Sopenharmony_ci	 * the loopback reference in rt6_info will not be taken, do it
662562306a36Sopenharmony_ci	 * manually for init_net */
662662306a36Sopenharmony_ci	init_net.ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = init_net.loopback_dev;
662762306a36Sopenharmony_ci	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
662862306a36Sopenharmony_ci	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
662962306a36Sopenharmony_ci  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
663062306a36Sopenharmony_ci	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
663162306a36Sopenharmony_ci	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
663262306a36Sopenharmony_ci	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
663362306a36Sopenharmony_ci	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
663462306a36Sopenharmony_ci  #endif
663562306a36Sopenharmony_ci}
663662306a36Sopenharmony_ci
663762306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6)
663862306a36Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
663962306a36Sopenharmony_ciDEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt)
664062306a36Sopenharmony_ci
664162306a36Sopenharmony_ciBTF_ID_LIST(btf_fib6_info_id)
664262306a36Sopenharmony_ciBTF_ID(struct, fib6_info)
664362306a36Sopenharmony_ci
664462306a36Sopenharmony_cistatic const struct bpf_iter_seq_info ipv6_route_seq_info = {
664562306a36Sopenharmony_ci	.seq_ops		= &ipv6_route_seq_ops,
664662306a36Sopenharmony_ci	.init_seq_private	= bpf_iter_init_seq_net,
664762306a36Sopenharmony_ci	.fini_seq_private	= bpf_iter_fini_seq_net,
664862306a36Sopenharmony_ci	.seq_priv_size		= sizeof(struct ipv6_route_iter),
664962306a36Sopenharmony_ci};
665062306a36Sopenharmony_ci
665162306a36Sopenharmony_cistatic struct bpf_iter_reg ipv6_route_reg_info = {
665262306a36Sopenharmony_ci	.target			= "ipv6_route",
665362306a36Sopenharmony_ci	.ctx_arg_info_size	= 1,
665462306a36Sopenharmony_ci	.ctx_arg_info		= {
665562306a36Sopenharmony_ci		{ offsetof(struct bpf_iter__ipv6_route, rt),
665662306a36Sopenharmony_ci		  PTR_TO_BTF_ID_OR_NULL },
665762306a36Sopenharmony_ci	},
665862306a36Sopenharmony_ci	.seq_info		= &ipv6_route_seq_info,
665962306a36Sopenharmony_ci};
666062306a36Sopenharmony_ci
666162306a36Sopenharmony_cistatic int __init bpf_iter_register(void)
666262306a36Sopenharmony_ci{
666362306a36Sopenharmony_ci	ipv6_route_reg_info.ctx_arg_info[0].btf_id = *btf_fib6_info_id;
666462306a36Sopenharmony_ci	return bpf_iter_reg_target(&ipv6_route_reg_info);
666562306a36Sopenharmony_ci}
666662306a36Sopenharmony_ci
666762306a36Sopenharmony_cistatic void bpf_iter_unregister(void)
666862306a36Sopenharmony_ci{
666962306a36Sopenharmony_ci	bpf_iter_unreg_target(&ipv6_route_reg_info);
667062306a36Sopenharmony_ci}
667162306a36Sopenharmony_ci#endif
667262306a36Sopenharmony_ci#endif
667362306a36Sopenharmony_ci
667462306a36Sopenharmony_ciint __init ip6_route_init(void)
667562306a36Sopenharmony_ci{
667662306a36Sopenharmony_ci	int ret;
667762306a36Sopenharmony_ci	int cpu;
667862306a36Sopenharmony_ci
667962306a36Sopenharmony_ci	ret = -ENOMEM;
668062306a36Sopenharmony_ci	ip6_dst_ops_template.kmem_cachep =
668162306a36Sopenharmony_ci		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
668262306a36Sopenharmony_ci				  SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);
668362306a36Sopenharmony_ci	if (!ip6_dst_ops_template.kmem_cachep)
668462306a36Sopenharmony_ci		goto out;
668562306a36Sopenharmony_ci
668662306a36Sopenharmony_ci	ret = dst_entries_init(&ip6_dst_blackhole_ops);
668762306a36Sopenharmony_ci	if (ret)
668862306a36Sopenharmony_ci		goto out_kmem_cache;
668962306a36Sopenharmony_ci
669062306a36Sopenharmony_ci	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
669162306a36Sopenharmony_ci	if (ret)
669262306a36Sopenharmony_ci		goto out_dst_entries;
669362306a36Sopenharmony_ci
669462306a36Sopenharmony_ci	ret = register_pernet_subsys(&ip6_route_net_ops);
669562306a36Sopenharmony_ci	if (ret)
669662306a36Sopenharmony_ci		goto out_register_inetpeer;
669762306a36Sopenharmony_ci
669862306a36Sopenharmony_ci	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
669962306a36Sopenharmony_ci
670062306a36Sopenharmony_ci	ret = fib6_init();
670162306a36Sopenharmony_ci	if (ret)
670262306a36Sopenharmony_ci		goto out_register_subsys;
670362306a36Sopenharmony_ci
670462306a36Sopenharmony_ci	ret = xfrm6_init();
670562306a36Sopenharmony_ci	if (ret)
670662306a36Sopenharmony_ci		goto out_fib6_init;
670762306a36Sopenharmony_ci
670862306a36Sopenharmony_ci	ret = fib6_rules_init();
670962306a36Sopenharmony_ci	if (ret)
671062306a36Sopenharmony_ci		goto xfrm6_init;
671162306a36Sopenharmony_ci
671262306a36Sopenharmony_ci	ret = register_pernet_subsys(&ip6_route_net_late_ops);
671362306a36Sopenharmony_ci	if (ret)
671462306a36Sopenharmony_ci		goto fib6_rules_init;
671562306a36Sopenharmony_ci
671662306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
671762306a36Sopenharmony_ci				   inet6_rtm_newroute, NULL, 0);
671862306a36Sopenharmony_ci	if (ret < 0)
671962306a36Sopenharmony_ci		goto out_register_late_subsys;
672062306a36Sopenharmony_ci
672162306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
672262306a36Sopenharmony_ci				   inet6_rtm_delroute, NULL, 0);
672362306a36Sopenharmony_ci	if (ret < 0)
672462306a36Sopenharmony_ci		goto out_register_late_subsys;
672562306a36Sopenharmony_ci
672662306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
672762306a36Sopenharmony_ci				   inet6_rtm_getroute, NULL,
672862306a36Sopenharmony_ci				   RTNL_FLAG_DOIT_UNLOCKED);
672962306a36Sopenharmony_ci	if (ret < 0)
673062306a36Sopenharmony_ci		goto out_register_late_subsys;
673162306a36Sopenharmony_ci
673262306a36Sopenharmony_ci	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
673362306a36Sopenharmony_ci	if (ret)
673462306a36Sopenharmony_ci		goto out_register_late_subsys;
673562306a36Sopenharmony_ci
673662306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6)
673762306a36Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
673862306a36Sopenharmony_ci	ret = bpf_iter_register();
673962306a36Sopenharmony_ci	if (ret)
674062306a36Sopenharmony_ci		goto out_register_late_subsys;
674162306a36Sopenharmony_ci#endif
674262306a36Sopenharmony_ci#endif
674362306a36Sopenharmony_ci
674462306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
674562306a36Sopenharmony_ci		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
674662306a36Sopenharmony_ci
674762306a36Sopenharmony_ci		INIT_LIST_HEAD(&ul->head);
674862306a36Sopenharmony_ci		INIT_LIST_HEAD(&ul->quarantine);
674962306a36Sopenharmony_ci		spin_lock_init(&ul->lock);
675062306a36Sopenharmony_ci	}
675162306a36Sopenharmony_ci
675262306a36Sopenharmony_ciout:
675362306a36Sopenharmony_ci	return ret;
675462306a36Sopenharmony_ci
675562306a36Sopenharmony_ciout_register_late_subsys:
675662306a36Sopenharmony_ci	rtnl_unregister_all(PF_INET6);
675762306a36Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_late_ops);
675862306a36Sopenharmony_cifib6_rules_init:
675962306a36Sopenharmony_ci	fib6_rules_cleanup();
676062306a36Sopenharmony_cixfrm6_init:
676162306a36Sopenharmony_ci	xfrm6_fini();
676262306a36Sopenharmony_ciout_fib6_init:
676362306a36Sopenharmony_ci	fib6_gc_cleanup();
676462306a36Sopenharmony_ciout_register_subsys:
676562306a36Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_ops);
676662306a36Sopenharmony_ciout_register_inetpeer:
676762306a36Sopenharmony_ci	unregister_pernet_subsys(&ipv6_inetpeer_ops);
676862306a36Sopenharmony_ciout_dst_entries:
676962306a36Sopenharmony_ci	dst_entries_destroy(&ip6_dst_blackhole_ops);
677062306a36Sopenharmony_ciout_kmem_cache:
677162306a36Sopenharmony_ci	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
677262306a36Sopenharmony_ci	goto out;
677362306a36Sopenharmony_ci}
677462306a36Sopenharmony_ci
677562306a36Sopenharmony_civoid ip6_route_cleanup(void)
677662306a36Sopenharmony_ci{
677762306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6)
677862306a36Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
677962306a36Sopenharmony_ci	bpf_iter_unregister();
678062306a36Sopenharmony_ci#endif
678162306a36Sopenharmony_ci#endif
678262306a36Sopenharmony_ci	unregister_netdevice_notifier(&ip6_route_dev_notifier);
678362306a36Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_late_ops);
678462306a36Sopenharmony_ci	fib6_rules_cleanup();
678562306a36Sopenharmony_ci	xfrm6_fini();
678662306a36Sopenharmony_ci	fib6_gc_cleanup();
678762306a36Sopenharmony_ci	unregister_pernet_subsys(&ipv6_inetpeer_ops);
678862306a36Sopenharmony_ci	unregister_pernet_subsys(&ip6_route_net_ops);
678962306a36Sopenharmony_ci	dst_entries_destroy(&ip6_dst_blackhole_ops);
679062306a36Sopenharmony_ci	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
679162306a36Sopenharmony_ci}
6792