xref: /openbmc/linux/net/ipv6/route.c (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	Linux INET6 implementation
41da177e4SLinus Torvalds  *	FIB front-end.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	Authors:
71da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds /*	Changes:
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki @USAGI
131da177e4SLinus Torvalds  *		reworked default router selection.
141da177e4SLinus Torvalds  *		- respect outgoing interface
151da177e4SLinus Torvalds  *		- select from (probably) reachable routers (i.e.
161da177e4SLinus Torvalds  *		routers in REACHABLE, STALE, DELAY or PROBE states).
171da177e4SLinus Torvalds  *		- always select the same router if it is (probably)
181da177e4SLinus Torvalds  *		reachable.  otherwise, round-robin the list.
19c0bece9fSYOSHIFUJI Hideaki  *	Ville Nuorvala
20c0bece9fSYOSHIFUJI Hideaki  *		Fixed routing subtrees.
211da177e4SLinus Torvalds  */
221da177e4SLinus Torvalds 
23f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt
24f3213831SJoe Perches 
254fc268d2SRandy Dunlap #include <linux/capability.h>
261da177e4SLinus Torvalds #include <linux/errno.h>
27bc3b2d7fSPaul Gortmaker #include <linux/export.h>
281da177e4SLinus Torvalds #include <linux/types.h>
291da177e4SLinus Torvalds #include <linux/times.h>
301da177e4SLinus Torvalds #include <linux/socket.h>
311da177e4SLinus Torvalds #include <linux/sockios.h>
321da177e4SLinus Torvalds #include <linux/net.h>
331da177e4SLinus Torvalds #include <linux/route.h>
341da177e4SLinus Torvalds #include <linux/netdevice.h>
351da177e4SLinus Torvalds #include <linux/in6.h>
367bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <linux/if_arp.h>
391da177e4SLinus Torvalds #include <linux/proc_fs.h>
401da177e4SLinus Torvalds #include <linux/seq_file.h>
415b7c931dSDaniel Lezcano #include <linux/nsproxy.h>
425a0e3ad6STejun Heo #include <linux/slab.h>
4335732d01SWei Wang #include <linux/jhash.h>
444785305cSEric Dumazet #include <linux/siphash.h>
45457c4cbcSEric W. Biederman #include <net/net_namespace.h>
461da177e4SLinus Torvalds #include <net/snmp.h>
471da177e4SLinus Torvalds #include <net/ipv6.h>
481da177e4SLinus Torvalds #include <net/ip6_fib.h>
491da177e4SLinus Torvalds #include <net/ip6_route.h>
501da177e4SLinus Torvalds #include <net/ndisc.h>
511da177e4SLinus Torvalds #include <net/addrconf.h>
521da177e4SLinus Torvalds #include <net/tcp.h>
531da177e4SLinus Torvalds #include <linux/rtnetlink.h>
541da177e4SLinus Torvalds #include <net/dst.h>
55904af04dSJiri Benc #include <net/dst_metadata.h>
561da177e4SLinus Torvalds #include <net/xfrm.h>
578d71740cSTom Tucker #include <net/netevent.h>
5821713ebcSThomas Graf #include <net/netlink.h>
593c618c1dSDavid Ahern #include <net/rtnh.h>
6019e42e45SRoopa Prabhu #include <net/lwtunnel.h>
61904af04dSJiri Benc #include <net/ip_tunnels.h>
62ca254490SDavid Ahern #include <net/l3mdev.h>
63eacb9384SRoopa Prabhu #include <net/ip.h>
647c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
65951cf368SYonghong Song #include <linux/btf_ids.h>
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
681da177e4SLinus Torvalds #include <linux/sysctl.h>
691da177e4SLinus Torvalds #endif
701da177e4SLinus Torvalds 
7130d444d3SDavid Ahern static int ip6_rt_type_to_error(u8 fib6_type);
7230d444d3SDavid Ahern 
7330d444d3SDavid Ahern #define CREATE_TRACE_POINTS
7430d444d3SDavid Ahern #include <trace/events/fib6.h>
7530d444d3SDavid Ahern EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
7630d444d3SDavid Ahern #undef CREATE_TRACE_POINTS
7730d444d3SDavid Ahern 
78afc154e9SHannes Frederic Sowa enum rt6_nud_state {
797e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
807e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
817e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
82afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
83afc154e9SHannes Frederic Sowa };
84afc154e9SHannes Frederic Sowa 
85bbd807dfSBrian Vazquez INDIRECT_CALLABLE_SCOPE
86bbd807dfSBrian Vazquez struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
870dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
88f67fbeaeSBrian Vazquez INDIRECT_CALLABLE_SCOPE
89f67fbeaeSBrian Vazquez unsigned int		ip6_mtu(const struct dst_entry *dst);
905af198c3SEric Dumazet static void		ip6_negative_advice(struct sock *sk,
915af198c3SEric Dumazet 					    struct dst_entry *dst);
921da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
931da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
9443c28172SZhengchao Shao 				       struct net_device *dev);
95af6d1034SJon Maxwell static void		 ip6_dst_gc(struct dst_ops *ops);
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
98ede2059dSEric W. Biederman static int		ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
997150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
100ede2059dSEric W. Biederman static int		ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
1011da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
1026700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
103bd085ef6SHangbin Liu 					   struct sk_buff *skb, u32 mtu,
104bd085ef6SHangbin Liu 					   bool confirm_neigh);
1056700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
1066700c270SDavid S. Miller 					struct sk_buff *skb);
107702cea56SDavid Ahern static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
108702cea56SDavid Ahern 			   int strict);
109a1b7a1f0SDavid Ahern static size_t rt6_nlmsg_size(struct fib6_info *f6i);
110d4ead6b3SDavid Ahern static int rt6_fill_node(struct net *net, struct sk_buff *skb,
1118d1c802bSDavid Ahern 			 struct fib6_info *rt, struct dst_entry *dst,
112d4ead6b3SDavid Ahern 			 struct in6_addr *dest, struct in6_addr *src,
11316a16cd3SDavid Ahern 			 int iif, int type, u32 portid, u32 seq,
11416a16cd3SDavid Ahern 			 unsigned int flags);
1157e4b5128SDavid Ahern static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
116510e2cedSWei Wang 					   const struct in6_addr *daddr,
117510e2cedSWei Wang 					   const struct in6_addr *saddr);
1181da177e4SLinus Torvalds 
11970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
1208d1c802bSDavid Ahern static struct fib6_info *rt6_add_route_info(struct net *net,
121b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
122830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
123830218c1SDavid Ahern 					   struct net_device *dev,
12495c96174SEric Dumazet 					   unsigned int pref);
1258d1c802bSDavid Ahern static struct fib6_info *rt6_get_route_info(struct net *net,
126b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
127830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
128830218c1SDavid Ahern 					   struct net_device *dev);
12970ceb4f5SYOSHIFUJI Hideaki #endif
13070ceb4f5SYOSHIFUJI Hideaki 
1318d0b94afSMartin KaFai Lau struct uncached_list {
1328d0b94afSMartin KaFai Lau 	spinlock_t		lock;
1338d0b94afSMartin KaFai Lau 	struct list_head	head;
134ba55ef81SEric Dumazet 	struct list_head	quarantine;
1358d0b94afSMartin KaFai Lau };
1368d0b94afSMartin KaFai Lau 
1378d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1388d0b94afSMartin KaFai Lau 
rt6_uncached_list_add(struct rt6_info * rt)139510c321bSXin Long void rt6_uncached_list_add(struct rt6_info *rt)
1408d0b94afSMartin KaFai Lau {
1418d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1428d0b94afSMartin KaFai Lau 
143d288a162SWangyang Guo 	rt->dst.rt_uncached_list = ul;
1448d0b94afSMartin KaFai Lau 
1458d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
146d288a162SWangyang Guo 	list_add_tail(&rt->dst.rt_uncached, &ul->head);
1478d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
1488d0b94afSMartin KaFai Lau }
1498d0b94afSMartin KaFai Lau 
rt6_uncached_list_del(struct rt6_info * rt)150510c321bSXin Long void rt6_uncached_list_del(struct rt6_info *rt)
1518d0b94afSMartin KaFai Lau {
152d288a162SWangyang Guo 	if (!list_empty(&rt->dst.rt_uncached)) {
153d288a162SWangyang Guo 		struct uncached_list *ul = rt->dst.rt_uncached_list;
1548d0b94afSMartin KaFai Lau 
1558d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
156d288a162SWangyang Guo 		list_del_init(&rt->dst.rt_uncached);
1578d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1588d0b94afSMartin KaFai Lau 	}
1598d0b94afSMartin KaFai Lau }
1608d0b94afSMartin KaFai Lau 
rt6_uncached_list_flush_dev(struct net_device * dev)161e5f80fcfSEric Dumazet static void rt6_uncached_list_flush_dev(struct net_device *dev)
1628d0b94afSMartin KaFai Lau {
1638d0b94afSMartin KaFai Lau 	int cpu;
1648d0b94afSMartin KaFai Lau 
1658d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
1668d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
167ba55ef81SEric Dumazet 		struct rt6_info *rt, *safe;
168ba55ef81SEric Dumazet 
169ba55ef81SEric Dumazet 		if (list_empty(&ul->head))
170ba55ef81SEric Dumazet 			continue;
1718d0b94afSMartin KaFai Lau 
1728d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
173d288a162SWangyang Guo 		list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) {
1748d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
1758d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
176ba55ef81SEric Dumazet 			bool handled = false;
1778d0b94afSMartin KaFai Lau 
1780ceb2f2bSEric Dumazet 			if (rt_idev && rt_idev->dev == dev) {
179e5f80fcfSEric Dumazet 				rt->rt6i_idev = in6_dev_get(blackhole_netdev);
1808d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
181ba55ef81SEric Dumazet 				handled = true;
1828d0b94afSMartin KaFai Lau 			}
1838d0b94afSMartin KaFai Lau 
184e332bc67SEric W. Biederman 			if (rt_dev == dev) {
1858d7017fdSMahesh Bandewar 				rt->dst.dev = blackhole_netdev;
186d62607c3SJakub Kicinski 				netdev_ref_replace(rt_dev, blackhole_netdev,
1879038c320SEric Dumazet 						   &rt->dst.dev_tracker,
1889038c320SEric Dumazet 						   GFP_ATOMIC);
189ba55ef81SEric Dumazet 				handled = true;
1908d0b94afSMartin KaFai Lau 			}
191ba55ef81SEric Dumazet 			if (handled)
192d288a162SWangyang Guo 				list_move(&rt->dst.rt_uncached,
193ba55ef81SEric Dumazet 					  &ul->quarantine);
1948d0b94afSMartin KaFai Lau 		}
1958d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1968d0b94afSMartin KaFai Lau 	}
1978d0b94afSMartin KaFai Lau }
1988d0b94afSMartin KaFai Lau 
choose_neigh_daddr(const struct in6_addr * p,struct sk_buff * skb,const void * daddr)199f8a1b43bSDavid Ahern static inline const void *choose_neigh_daddr(const struct in6_addr *p,
200f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
201f894cbf8SDavid S. Miller 					     const void *daddr)
20239232973SDavid S. Miller {
203a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
20439232973SDavid S. Miller 		return (const void *) p;
205f894cbf8SDavid S. Miller 	else if (skb)
206f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
20739232973SDavid S. Miller 	return daddr;
20839232973SDavid S. Miller }
20939232973SDavid S. Miller 
ip6_neigh_lookup(const struct in6_addr * gw,struct net_device * dev,struct sk_buff * skb,const void * daddr)210f8a1b43bSDavid Ahern struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
211f8a1b43bSDavid Ahern 				   struct net_device *dev,
212f894cbf8SDavid S. Miller 				   struct sk_buff *skb,
213f894cbf8SDavid S. Miller 				   const void *daddr)
214d3aaeb38SDavid S. Miller {
21539232973SDavid S. Miller 	struct neighbour *n;
21639232973SDavid S. Miller 
217f8a1b43bSDavid Ahern 	daddr = choose_neigh_daddr(gw, skb, daddr);
218f8a1b43bSDavid Ahern 	n = __ipv6_neigh_lookup(dev, daddr);
219f83c7790SDavid S. Miller 	if (n)
220f83c7790SDavid S. Miller 		return n;
2217adf3246SStefano Brivio 
2227adf3246SStefano Brivio 	n = neigh_create(&nd_tbl, daddr, dev);
2237adf3246SStefano Brivio 	return IS_ERR(n) ? NULL : n;
224f8a1b43bSDavid Ahern }
225f8a1b43bSDavid Ahern 
ip6_dst_neigh_lookup(const struct dst_entry * dst,struct sk_buff * skb,const void * daddr)226f8a1b43bSDavid Ahern static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
227f8a1b43bSDavid Ahern 					      struct sk_buff *skb,
228f8a1b43bSDavid Ahern 					      const void *daddr)
229f8a1b43bSDavid Ahern {
230797a4c1fSEric Dumazet 	const struct rt6_info *rt = dst_rt6_info(dst);
231f8a1b43bSDavid Ahern 
2322c6b55f4SNicolas Dichtel 	return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any),
2332c6b55f4SNicolas Dichtel 				dst->dev, skb, daddr);
234f83c7790SDavid S. Miller }
235f83c7790SDavid S. Miller 
ip6_confirm_neigh(const struct dst_entry * dst,const void * daddr)23663fca65dSJulian Anastasov static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
23763fca65dSJulian Anastasov {
238797a4c1fSEric Dumazet 	const struct rt6_info *rt = dst_rt6_info(dst);
23963fca65dSJulian Anastasov 	struct net_device *dev = dst->dev;
24063fca65dSJulian Anastasov 
241cbfd6891SStefano Brivio 	daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr);
24263fca65dSJulian Anastasov 	if (!daddr)
24363fca65dSJulian Anastasov 		return;
24463fca65dSJulian Anastasov 	if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
24563fca65dSJulian Anastasov 		return;
24663fca65dSJulian Anastasov 	if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
24763fca65dSJulian Anastasov 		return;
24863fca65dSJulian Anastasov 	__ipv6_confirm_neigh(dev, daddr);
24963fca65dSJulian Anastasov }
25063fca65dSJulian Anastasov 
2519a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2521da177e4SLinus Torvalds 	.family			=	AF_INET6,
2531da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2541da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2551da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2560dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
257ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
258d4ead6b3SDavid Ahern 	.cow_metrics		=	dst_cow_metrics_generic,
2591da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2601da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2611da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2621da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2631da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2646e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2659f8955ccSEric W. Biederman 	.local_out		=	__ip6_local_out,
266f8a1b43bSDavid Ahern 	.neigh_lookup		=	ip6_dst_neigh_lookup,
26763fca65dSJulian Anastasov 	.confirm_neigh		=	ip6_confirm_neigh,
2681da177e4SLinus Torvalds };
2691da177e4SLinus Torvalds 
27014e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
27114e50e57SDavid S. Miller 	.family			= AF_INET6,
272214f45c9SEric Dumazet 	.default_advmss		= ip6_default_advmss,
273f8a1b43bSDavid Ahern 	.neigh_lookup		= ip6_dst_neigh_lookup,
274c4c877b2SDaniel Borkmann 	.check			= ip6_dst_check,
275c4c877b2SDaniel Borkmann 	.destroy		= ip6_dst_destroy,
276c4c877b2SDaniel Borkmann 	.cow_metrics		= dst_cow_metrics_generic,
277c4c877b2SDaniel Borkmann 	.update_pmtu		= dst_blackhole_update_pmtu,
278c4c877b2SDaniel Borkmann 	.redirect		= dst_blackhole_redirect,
279c4c877b2SDaniel Borkmann 	.mtu			= dst_blackhole_mtu,
28014e50e57SDavid S. Miller };
28114e50e57SDavid S. Miller 
28262fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
28314edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
28462fa8a84SDavid S. Miller };
28562fa8a84SDavid S. Miller 
2868d1c802bSDavid Ahern static const struct fib6_info fib6_null_entry_template = {
28793c2fb25SDavid Ahern 	.fib6_flags	= (RTF_REJECT | RTF_NONEXTHOP),
28893c2fb25SDavid Ahern 	.fib6_protocol  = RTPROT_KERNEL,
28993c2fb25SDavid Ahern 	.fib6_metric	= ~(u32)0,
290f05713e0SEric Dumazet 	.fib6_ref	= REFCOUNT_INIT(1),
291421842edSDavid Ahern 	.fib6_type	= RTN_UNREACHABLE,
292421842edSDavid Ahern 	.fib6_metrics	= (struct dst_metrics *)&dst_default_metrics,
293421842edSDavid Ahern };
294421842edSDavid Ahern 
295fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2961da177e4SLinus Torvalds 	.dst = {
297bc9d3a9fSThomas Gleixner 		.__rcuref	= RCUREF_INIT(1),
2981da177e4SLinus Torvalds 		.__use		= 1,
2992c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
3001da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
3011da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
3021da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
3031da177e4SLinus Torvalds 	},
3041da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3051da177e4SLinus Torvalds };
3061da177e4SLinus Torvalds 
307101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
308101367c2SThomas Graf 
309fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
310101367c2SThomas Graf 	.dst = {
311bc9d3a9fSThomas Gleixner 		.__rcuref	= RCUREF_INIT(1),
312101367c2SThomas Graf 		.__use		= 1,
3132c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
314101367c2SThomas Graf 		.error		= -EACCES,
3159ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
3169ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
317101367c2SThomas Graf 	},
318101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
319101367c2SThomas Graf };
320101367c2SThomas Graf 
321fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
322101367c2SThomas Graf 	.dst = {
323bc9d3a9fSThomas Gleixner 		.__rcuref	= RCUREF_INIT(1),
324101367c2SThomas Graf 		.__use		= 1,
3252c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
326101367c2SThomas Graf 		.error		= -EINVAL,
327352e512cSHerbert Xu 		.input		= dst_discard,
328ede2059dSEric W. Biederman 		.output		= dst_discard_out,
329101367c2SThomas Graf 	},
330101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
331101367c2SThomas Graf };
332101367c2SThomas Graf 
333101367c2SThomas Graf #endif
334101367c2SThomas Graf 
rt6_info_init(struct rt6_info * rt)335ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt)
336ebfa45f0SMartin KaFai Lau {
3378f2a83b4SKees Cook 	memset_after(rt, 0, dst);
338ebfa45f0SMartin KaFai Lau }
339ebfa45f0SMartin KaFai Lau 
3401da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
ip6_dst_alloc(struct net * net,struct net_device * dev,int flags)34193531c67SDavid Ahern struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
342ad706862SMartin KaFai Lau 			       int flags)
3431da177e4SLinus Torvalds {
34497bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
345b2a9c0edSWei Wang 					1, DST_OBSOLETE_FORCE_CHK, flags);
346cf911662SDavid S. Miller 
34781eb8447SWei Wang 	if (rt) {
348ebfa45f0SMartin KaFai Lau 		rt6_info_init(rt);
34981eb8447SWei Wang 		atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
35081eb8447SWei Wang 	}
3518104891bSSteffen Klassert 
352cf911662SDavid S. Miller 	return rt;
3531da177e4SLinus Torvalds }
3549ab179d8SDavid Ahern EXPORT_SYMBOL(ip6_dst_alloc);
355d52d3997SMartin KaFai Lau 
ip6_dst_destroy(struct dst_entry * dst)3561da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3571da177e4SLinus Torvalds {
358797a4c1fSEric Dumazet 	struct rt6_info *rt = dst_rt6_info(dst);
359a68886a6SDavid Ahern 	struct fib6_info *from;
3608d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3611da177e4SLinus Torvalds 
3621620a336SDavid Ahern 	ip_dst_metrics_put(dst);
3638d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
3648d0b94afSMartin KaFai Lau 
3658d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
36638308473SDavid S. Miller 	if (idev) {
3671da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3681da177e4SLinus Torvalds 		in6_dev_put(idev);
3691da177e4SLinus Torvalds 	}
3701716a961SGao feng 
37170530a2fSEric Dumazet 	from = unrcu_pointer(xchg(&rt->from, NULL));
37293531c67SDavid Ahern 	fib6_info_release(from);
373b3419363SDavid S. Miller }
374b3419363SDavid S. Miller 
ip6_dst_ifdown(struct dst_entry * dst,struct net_device * dev)37543c28172SZhengchao Shao static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
3761da177e4SLinus Torvalds {
377797a4c1fSEric Dumazet 	struct rt6_info *rt = dst_rt6_info(dst);
3781da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
37943e25adcSPaolo Abeni 	struct fib6_info *from;
3801da177e4SLinus Torvalds 
381e5f80fcfSEric Dumazet 	if (idev && idev->dev != blackhole_netdev) {
382e5f80fcfSEric Dumazet 		struct inet6_dev *blackhole_idev = in6_dev_get(blackhole_netdev);
383e5f80fcfSEric Dumazet 
384e5f80fcfSEric Dumazet 		if (blackhole_idev) {
385e5f80fcfSEric Dumazet 			rt->rt6i_idev = blackhole_idev;
3861da177e4SLinus Torvalds 			in6_dev_put(idev);
3871da177e4SLinus Torvalds 		}
3881da177e4SLinus Torvalds 	}
38943e25adcSPaolo Abeni 	from = unrcu_pointer(xchg(&rt->from, NULL));
39043e25adcSPaolo Abeni 	fib6_info_release(from);
39197cac082SDavid S. Miller }
3921da177e4SLinus Torvalds 
__rt6_check_expired(const struct rt6_info * rt)3935973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt)
3945973fb1eSMartin KaFai Lau {
3955973fb1eSMartin KaFai Lau 	if (rt->rt6i_flags & RTF_EXPIRES)
3965973fb1eSMartin KaFai Lau 		return time_after(jiffies, rt->dst.expires);
3975973fb1eSMartin KaFai Lau 	else
3985973fb1eSMartin KaFai Lau 		return false;
3995973fb1eSMartin KaFai Lau }
4005973fb1eSMartin KaFai Lau 
rt6_check_expired(const struct rt6_info * rt)401a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4021da177e4SLinus Torvalds {
403a68886a6SDavid Ahern 	struct fib6_info *from;
404a68886a6SDavid Ahern 
405a68886a6SDavid Ahern 	from = rcu_dereference(rt->from);
406a68886a6SDavid Ahern 
4071716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4081716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
409a50feda5SEric Dumazet 			return true;
410a68886a6SDavid Ahern 	} else if (from) {
4111e2ea8adSXin Long 		return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
412a68886a6SDavid Ahern 			fib6_check_expired(from);
4131716a961SGao feng 	}
414a50feda5SEric Dumazet 	return false;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
fib6_select_path(const struct net * net,struct fib6_result * res,struct flowi6 * fl6,int oif,bool have_oif_match,const struct sk_buff * skb,int strict)417b1d40991SDavid Ahern void fib6_select_path(const struct net *net, struct fib6_result *res,
418b1d40991SDavid Ahern 		      struct flowi6 *fl6, int oif, bool have_oif_match,
419b1d40991SDavid Ahern 		      const struct sk_buff *skb, int strict)
42051ebd318SNicolas Dichtel {
4218d1c802bSDavid Ahern 	struct fib6_info *sibling, *next_sibling;
422b1d40991SDavid Ahern 	struct fib6_info *match = res->f6i;
423b1d40991SDavid Ahern 
42434fe5a1cSDavid Ahern 	if (!match->nh && (!match->fib6_nsiblings || have_oif_match))
425b1d40991SDavid Ahern 		goto out;
42651ebd318SNicolas Dichtel 
42734fe5a1cSDavid Ahern 	if (match->nh && have_oif_match && res->nh)
42834fe5a1cSDavid Ahern 		return;
42934fe5a1cSDavid Ahern 
4308423be89SSriram Yagnaraman 	if (skb)
4318423be89SSriram Yagnaraman 		IP6CB(skb)->flags |= IP6SKB_MULTIPATH;
4328423be89SSriram Yagnaraman 
433b673d6ccSJakub Sitnicki 	/* We might have already computed the hash for ICMPv6 errors. In such
434b673d6ccSJakub Sitnicki 	 * case it will always be non-zero. Otherwise now is the time to do it.
435b673d6ccSJakub Sitnicki 	 */
436f88d8ea6SDavid Ahern 	if (!fl6->mp_hash &&
437f88d8ea6SDavid Ahern 	    (!match->nh || nexthop_is_multipath(match->nh)))
438b4bac172SDavid Ahern 		fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
439b673d6ccSJakub Sitnicki 
440f88d8ea6SDavid Ahern 	if (unlikely(match->nh)) {
441f88d8ea6SDavid Ahern 		nexthop_path_fib6_result(res, fl6->mp_hash);
442f88d8ea6SDavid Ahern 		return;
443f88d8ea6SDavid Ahern 	}
444f88d8ea6SDavid Ahern 
4451cf844c7SDavid Ahern 	if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound))
446b1d40991SDavid Ahern 		goto out;
447bbfcd776SIdo Schimmel 
44893c2fb25SDavid Ahern 	list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
44993c2fb25SDavid Ahern 				 fib6_siblings) {
4501cf844c7SDavid Ahern 		const struct fib6_nh *nh = sibling->fib6_nh;
4515e670d84SDavid Ahern 		int nh_upper_bound;
4525e670d84SDavid Ahern 
453702cea56SDavid Ahern 		nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
4545e670d84SDavid Ahern 		if (fl6->mp_hash > nh_upper_bound)
4553d709f69SIdo Schimmel 			continue;
456702cea56SDavid Ahern 		if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
45752bd4c0cSNicolas Dichtel 			break;
45851ebd318SNicolas Dichtel 		match = sibling;
45951ebd318SNicolas Dichtel 		break;
46051ebd318SNicolas Dichtel 	}
4613d709f69SIdo Schimmel 
462b1d40991SDavid Ahern out:
463b1d40991SDavid Ahern 	res->f6i = match;
4641cf844c7SDavid Ahern 	res->nh = match->fib6_nh;
46551ebd318SNicolas Dichtel }
46651ebd318SNicolas Dichtel 
4671da177e4SLinus Torvalds /*
46866f5d6ceSWei Wang  *	Route lookup. rcu_read_lock() should be held.
4691da177e4SLinus Torvalds  */
4701da177e4SLinus Torvalds 
__rt6_device_match(struct net * net,const struct fib6_nh * nh,const struct in6_addr * saddr,int oif,int flags)4710c59d006SDavid Ahern static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
4720c59d006SDavid Ahern 			       const struct in6_addr *saddr, int oif, int flags)
4730c59d006SDavid Ahern {
4740c59d006SDavid Ahern 	const struct net_device *dev;
4750c59d006SDavid Ahern 
4760c59d006SDavid Ahern 	if (nh->fib_nh_flags & RTNH_F_DEAD)
4770c59d006SDavid Ahern 		return false;
4780c59d006SDavid Ahern 
4790c59d006SDavid Ahern 	dev = nh->fib_nh_dev;
4800c59d006SDavid Ahern 	if (oif) {
4810c59d006SDavid Ahern 		if (dev->ifindex == oif)
4820c59d006SDavid Ahern 			return true;
4830c59d006SDavid Ahern 	} else {
4840c59d006SDavid Ahern 		if (ipv6_chk_addr(net, saddr, dev,
4850c59d006SDavid Ahern 				  flags & RT6_LOOKUP_F_IFACE))
4860c59d006SDavid Ahern 			return true;
4870c59d006SDavid Ahern 	}
4880c59d006SDavid Ahern 
4890c59d006SDavid Ahern 	return false;
4900c59d006SDavid Ahern }
4910c59d006SDavid Ahern 
492962b6803SDavid Ahern struct fib6_nh_dm_arg {
493962b6803SDavid Ahern 	struct net		*net;
494962b6803SDavid Ahern 	const struct in6_addr	*saddr;
495962b6803SDavid Ahern 	int			oif;
496962b6803SDavid Ahern 	int			flags;
497962b6803SDavid Ahern 	struct fib6_nh		*nh;
498962b6803SDavid Ahern };
499962b6803SDavid Ahern 
__rt6_nh_dev_match(struct fib6_nh * nh,void * _arg)500962b6803SDavid Ahern static int __rt6_nh_dev_match(struct fib6_nh *nh, void *_arg)
501962b6803SDavid Ahern {
502962b6803SDavid Ahern 	struct fib6_nh_dm_arg *arg = _arg;
503962b6803SDavid Ahern 
504962b6803SDavid Ahern 	arg->nh = nh;
505962b6803SDavid Ahern 	return __rt6_device_match(arg->net, nh, arg->saddr, arg->oif,
506962b6803SDavid Ahern 				  arg->flags);
507962b6803SDavid Ahern }
508962b6803SDavid Ahern 
509962b6803SDavid Ahern /* returns fib6_nh from nexthop or NULL */
rt6_nh_dev_match(struct net * net,struct nexthop * nh,struct fib6_result * res,const struct in6_addr * saddr,int oif,int flags)510962b6803SDavid Ahern static struct fib6_nh *rt6_nh_dev_match(struct net *net, struct nexthop *nh,
511962b6803SDavid Ahern 					struct fib6_result *res,
512962b6803SDavid Ahern 					const struct in6_addr *saddr,
513962b6803SDavid Ahern 					int oif, int flags)
514962b6803SDavid Ahern {
515962b6803SDavid Ahern 	struct fib6_nh_dm_arg arg = {
516962b6803SDavid Ahern 		.net   = net,
517962b6803SDavid Ahern 		.saddr = saddr,
518962b6803SDavid Ahern 		.oif   = oif,
519962b6803SDavid Ahern 		.flags = flags,
520962b6803SDavid Ahern 	};
521962b6803SDavid Ahern 
522962b6803SDavid Ahern 	if (nexthop_is_blackhole(nh))
523962b6803SDavid Ahern 		return NULL;
524962b6803SDavid Ahern 
525962b6803SDavid Ahern 	if (nexthop_for_each_fib6_nh(nh, __rt6_nh_dev_match, &arg))
526962b6803SDavid Ahern 		return arg.nh;
527962b6803SDavid Ahern 
528962b6803SDavid Ahern 	return NULL;
529962b6803SDavid Ahern }
530962b6803SDavid Ahern 
rt6_device_match(struct net * net,struct fib6_result * res,const struct in6_addr * saddr,int oif,int flags)53175ef7389SDavid Ahern static void rt6_device_match(struct net *net, struct fib6_result *res,
53275ef7389SDavid Ahern 			     const struct in6_addr *saddr, int oif, int flags)
5331da177e4SLinus Torvalds {
53475ef7389SDavid Ahern 	struct fib6_info *f6i = res->f6i;
53575ef7389SDavid Ahern 	struct fib6_info *spf6i;
53675ef7389SDavid Ahern 	struct fib6_nh *nh;
5371da177e4SLinus Torvalds 
53875ef7389SDavid Ahern 	if (!oif && ipv6_addr_any(saddr)) {
539f88d8ea6SDavid Ahern 		if (unlikely(f6i->nh)) {
540f88d8ea6SDavid Ahern 			nh = nexthop_fib6_nh(f6i->nh);
541f88d8ea6SDavid Ahern 			if (nexthop_is_blackhole(f6i->nh))
542f88d8ea6SDavid Ahern 				goto out_blackhole;
543f88d8ea6SDavid Ahern 		} else {
5441cf844c7SDavid Ahern 			nh = f6i->fib6_nh;
545f88d8ea6SDavid Ahern 		}
5467d21fec9SDavid Ahern 		if (!(nh->fib_nh_flags & RTNH_F_DEAD))
5477d21fec9SDavid Ahern 			goto out;
5481da177e4SLinus Torvalds 	}
5491da177e4SLinus Torvalds 
55075ef7389SDavid Ahern 	for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
551962b6803SDavid Ahern 		bool matched = false;
552962b6803SDavid Ahern 
553962b6803SDavid Ahern 		if (unlikely(spf6i->nh)) {
554962b6803SDavid Ahern 			nh = rt6_nh_dev_match(net, spf6i->nh, res, saddr,
555962b6803SDavid Ahern 					      oif, flags);
556962b6803SDavid Ahern 			if (nh)
557962b6803SDavid Ahern 				matched = true;
558962b6803SDavid Ahern 		} else {
5591cf844c7SDavid Ahern 			nh = spf6i->fib6_nh;
560962b6803SDavid Ahern 			if (__rt6_device_match(net, nh, saddr, oif, flags))
561962b6803SDavid Ahern 				matched = true;
562962b6803SDavid Ahern 		}
563962b6803SDavid Ahern 		if (matched) {
56475ef7389SDavid Ahern 			res->f6i = spf6i;
5657d21fec9SDavid Ahern 			goto out;
56675ef7389SDavid Ahern 		}
56775ef7389SDavid Ahern 	}
5681da177e4SLinus Torvalds 
56975ef7389SDavid Ahern 	if (oif && flags & RT6_LOOKUP_F_IFACE) {
57075ef7389SDavid Ahern 		res->f6i = net->ipv6.fib6_null_entry;
5711cf844c7SDavid Ahern 		nh = res->f6i->fib6_nh;
5727d21fec9SDavid Ahern 		goto out;
57375ef7389SDavid Ahern 	}
57475ef7389SDavid Ahern 
575f88d8ea6SDavid Ahern 	if (unlikely(f6i->nh)) {
576f88d8ea6SDavid Ahern 		nh = nexthop_fib6_nh(f6i->nh);
577f88d8ea6SDavid Ahern 		if (nexthop_is_blackhole(f6i->nh))
578f88d8ea6SDavid Ahern 			goto out_blackhole;
579f88d8ea6SDavid Ahern 	} else {
5801cf844c7SDavid Ahern 		nh = f6i->fib6_nh;
581f88d8ea6SDavid Ahern 	}
582f88d8ea6SDavid Ahern 
5837d21fec9SDavid Ahern 	if (nh->fib_nh_flags & RTNH_F_DEAD) {
58475ef7389SDavid Ahern 		res->f6i = net->ipv6.fib6_null_entry;
5851cf844c7SDavid Ahern 		nh = res->f6i->fib6_nh;
58675ef7389SDavid Ahern 	}
5877d21fec9SDavid Ahern out:
5887d21fec9SDavid Ahern 	res->nh = nh;
5897d21fec9SDavid Ahern 	res->fib6_type = res->f6i->fib6_type;
5907d21fec9SDavid Ahern 	res->fib6_flags = res->f6i->fib6_flags;
591f88d8ea6SDavid Ahern 	return;
592f88d8ea6SDavid Ahern 
593f88d8ea6SDavid Ahern out_blackhole:
594f88d8ea6SDavid Ahern 	res->fib6_flags |= RTF_REJECT;
595f88d8ea6SDavid Ahern 	res->fib6_type = RTN_BLACKHOLE;
596f88d8ea6SDavid Ahern 	res->nh = nh;
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
59927097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
600c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
601c2f17e82SHannes Frederic Sowa 	struct work_struct work;
602c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
603c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
604fb67510bSEric Dumazet 	netdevice_tracker dev_tracker;
605c2f17e82SHannes Frederic Sowa };
606c2f17e82SHannes Frederic Sowa 
rt6_probe_deferred(struct work_struct * w)607c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
608c2f17e82SHannes Frederic Sowa {
609c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
610c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
611c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
612c2f17e82SHannes Frederic Sowa 
613c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
614adc176c5SErik Nordmark 	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
615d62607c3SJakub Kicinski 	netdev_put(work->dev, &work->dev_tracker);
616662f5533SMichael Büsch 	kfree(work);
617c2f17e82SHannes Frederic Sowa }
618c2f17e82SHannes Frederic Sowa 
rt6_probe(struct fib6_nh * fib6_nh)619cc3a86c8SDavid Ahern static void rt6_probe(struct fib6_nh *fib6_nh)
62027097255SYOSHIFUJI Hideaki {
621f547fac6SSabrina Dubroca 	struct __rt6_probe_work *work = NULL;
6225e670d84SDavid Ahern 	const struct in6_addr *nh_gw;
6231bef4c22SEric Dumazet 	unsigned long last_probe;
624f2c31e32SEric Dumazet 	struct neighbour *neigh;
6255e670d84SDavid Ahern 	struct net_device *dev;
626f547fac6SSabrina Dubroca 	struct inet6_dev *idev;
6275e670d84SDavid Ahern 
62827097255SYOSHIFUJI Hideaki 	/*
62927097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
63027097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
63127097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
63227097255SYOSHIFUJI Hideaki 	 *
63327097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
63427097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
63527097255SYOSHIFUJI Hideaki 	 */
636004b3942SHangbin Liu 	if (!fib6_nh->fib_nh_gw_family)
637fdd6681dSAmerigo Wang 		return;
6385e670d84SDavid Ahern 
639cc3a86c8SDavid Ahern 	nh_gw = &fib6_nh->fib_nh_gw6;
640cc3a86c8SDavid Ahern 	dev = fib6_nh->fib_nh_dev;
64109eed119SEric Dumazet 	rcu_read_lock();
6421bef4c22SEric Dumazet 	last_probe = READ_ONCE(fib6_nh->last_probe);
643f547fac6SSabrina Dubroca 	idev = __in6_dev_get(dev);
64473e7c8caSEric Dumazet 	if (!idev)
64573e7c8caSEric Dumazet 		goto out;
6465e670d84SDavid Ahern 	neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
6472152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
648b071af52SEric Dumazet 		if (READ_ONCE(neigh->nud_state) & NUD_VALID)
6498d6c31bfSMartin KaFai Lau 			goto out;
6508d6c31bfSMartin KaFai Lau 
65109eed119SEric Dumazet 		write_lock_bh(&neigh->lock);
652990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
653990edb42SMartin KaFai Lau 		    time_after(jiffies,
654dcd1f572SDavid Ahern 			       neigh->updated + idev->cnf.rtr_probe_interval)) {
655c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
656990edb42SMartin KaFai Lau 			if (work)
6577e980569SJiri Benc 				__neigh_set_probe_once(neigh);
658990edb42SMartin KaFai Lau 		}
65909eed119SEric Dumazet 		write_unlock_bh(&neigh->lock);
6601bef4c22SEric Dumazet 	} else if (time_after(jiffies, last_probe +
661f547fac6SSabrina Dubroca 				       idev->cnf.rtr_probe_interval)) {
662990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
663990edb42SMartin KaFai Lau 	}
664c2f17e82SHannes Frederic Sowa 
6651bef4c22SEric Dumazet 	if (!work || cmpxchg(&fib6_nh->last_probe,
6661bef4c22SEric Dumazet 			     last_probe, jiffies) != last_probe) {
6671bef4c22SEric Dumazet 		kfree(work);
6681bef4c22SEric Dumazet 	} else {
669c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
6705e670d84SDavid Ahern 		work->target = *nh_gw;
671d62607c3SJakub Kicinski 		netdev_hold(dev, &work->dev_tracker, GFP_ATOMIC);
6725e670d84SDavid Ahern 		work->dev = dev;
673c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
674c2f17e82SHannes Frederic Sowa 	}
675990edb42SMartin KaFai Lau 
6768d6c31bfSMartin KaFai Lau out:
67709eed119SEric Dumazet 	rcu_read_unlock();
678f2c31e32SEric Dumazet }
67927097255SYOSHIFUJI Hideaki #else
rt6_probe(struct fib6_nh * fib6_nh)680cc3a86c8SDavid Ahern static inline void rt6_probe(struct fib6_nh *fib6_nh)
68127097255SYOSHIFUJI Hideaki {
68227097255SYOSHIFUJI Hideaki }
68327097255SYOSHIFUJI Hideaki #endif
68427097255SYOSHIFUJI Hideaki 
6851da177e4SLinus Torvalds /*
686554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
6871da177e4SLinus Torvalds  */
rt6_check_neigh(const struct fib6_nh * fib6_nh)6881ba9a895SDavid Ahern static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
6891da177e4SLinus Torvalds {
690afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
6915e670d84SDavid Ahern 	struct neighbour *neigh;
692f2c31e32SEric Dumazet 
69309eed119SEric Dumazet 	rcu_read_lock();
6941ba9a895SDavid Ahern 	neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
6951ba9a895SDavid Ahern 					  &fib6_nh->fib_nh_gw6);
696145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
697c486640aSEric Dumazet 		u8 nud_state = READ_ONCE(neigh->nud_state);
698c486640aSEric Dumazet 
699c486640aSEric Dumazet 		if (nud_state & NUD_VALID)
700afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
701398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
702c486640aSEric Dumazet 		else if (!(nud_state & NUD_FAILED))
703afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
7047e980569SJiri Benc 		else
7057e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
706398bcbebSYOSHIFUJI Hideaki #endif
707afc154e9SHannes Frederic Sowa 	} else {
708afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
7097e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
710a5a81f0bSPaul Marks 	}
71109eed119SEric Dumazet 	rcu_read_unlock();
712145a3621SYOSHIFUJI Hideaki / 吉藤英明 
713a5a81f0bSPaul Marks 	return ret;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds 
rt6_score_route(const struct fib6_nh * nh,u32 fib6_flags,int oif,int strict)716702cea56SDavid Ahern static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
717702cea56SDavid Ahern 			   int strict)
718554cfb7eSYOSHIFUJI Hideaki {
7196e1809a5SDavid Ahern 	int m = 0;
7204d0c5911SYOSHIFUJI Hideaki 
7216e1809a5SDavid Ahern 	if (!oif || nh->fib_nh_dev->ifindex == oif)
7226e1809a5SDavid Ahern 		m = 2;
7236e1809a5SDavid Ahern 
72477d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
725afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
726ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
727702cea56SDavid Ahern 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
728ebacaaa0SYOSHIFUJI Hideaki #endif
7291ba9a895SDavid Ahern 	if ((strict & RT6_LOOKUP_F_REACHABLE) &&
730702cea56SDavid Ahern 	    !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
7311ba9a895SDavid Ahern 		int n = rt6_check_neigh(nh);
732afc154e9SHannes Frederic Sowa 		if (n < 0)
733afc154e9SHannes Frederic Sowa 			return n;
734afc154e9SHannes Frederic Sowa 	}
735554cfb7eSYOSHIFUJI Hideaki 	return m;
736554cfb7eSYOSHIFUJI Hideaki }
737554cfb7eSYOSHIFUJI Hideaki 
find_match(struct fib6_nh * nh,u32 fib6_flags,int oif,int strict,int * mpri,bool * do_rr)73828679ed1SDavid Ahern static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
73928679ed1SDavid Ahern 		       int oif, int strict, int *mpri, bool *do_rr)
740554cfb7eSYOSHIFUJI Hideaki {
741afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
74228679ed1SDavid Ahern 	bool rc = false;
74328679ed1SDavid Ahern 	int m;
74435103d11SAndy Gospodarek 
74528679ed1SDavid Ahern 	if (nh->fib_nh_flags & RTNH_F_DEAD)
7468067bb8cSIdo Schimmel 		goto out;
7478067bb8cSIdo Schimmel 
74828679ed1SDavid Ahern 	if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
74928679ed1SDavid Ahern 	    nh->fib_nh_flags & RTNH_F_LINKDOWN &&
750d5d32e4bSDavid Ahern 	    !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
75135103d11SAndy Gospodarek 		goto out;
752554cfb7eSYOSHIFUJI Hideaki 
75328679ed1SDavid Ahern 	m = rt6_score_route(nh, fib6_flags, oif, strict);
7547e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
755afc154e9SHannes Frederic Sowa 		match_do_rr = true;
756afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
7577e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
758f11e6659SDavid S. Miller 		goto out;
7591da177e4SLinus Torvalds 	}
760f11e6659SDavid S. Miller 
761afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
76228679ed1SDavid Ahern 		rt6_probe(nh);
763afc154e9SHannes Frederic Sowa 
7647e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
765afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
766afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
767afc154e9SHannes Frederic Sowa 		*mpri = m;
76828679ed1SDavid Ahern 		rc = true;
769afc154e9SHannes Frederic Sowa 	}
770f11e6659SDavid S. Miller out:
77128679ed1SDavid Ahern 	return rc;
7721da177e4SLinus Torvalds }
7731da177e4SLinus Torvalds 
77417a5984eSDavid Ahern struct fib6_nh_frl_arg {
77517a5984eSDavid Ahern 	u32		flags;
77617a5984eSDavid Ahern 	int		oif;
77717a5984eSDavid Ahern 	int		strict;
77817a5984eSDavid Ahern 	int		*mpri;
77917a5984eSDavid Ahern 	bool		*do_rr;
78017a5984eSDavid Ahern 	struct fib6_nh	*nh;
78117a5984eSDavid Ahern };
78217a5984eSDavid Ahern 
rt6_nh_find_match(struct fib6_nh * nh,void * _arg)78317a5984eSDavid Ahern static int rt6_nh_find_match(struct fib6_nh *nh, void *_arg)
78417a5984eSDavid Ahern {
78517a5984eSDavid Ahern 	struct fib6_nh_frl_arg *arg = _arg;
78617a5984eSDavid Ahern 
78717a5984eSDavid Ahern 	arg->nh = nh;
78817a5984eSDavid Ahern 	return find_match(nh, arg->flags, arg->oif, arg->strict,
78917a5984eSDavid Ahern 			  arg->mpri, arg->do_rr);
79017a5984eSDavid Ahern }
79117a5984eSDavid Ahern 
__find_rr_leaf(struct fib6_info * f6i_start,struct fib6_info * nomatch,u32 metric,struct fib6_result * res,struct fib6_info ** cont,int oif,int strict,bool * do_rr,int * mpri)792b7bc4b6aSDavid Ahern static void __find_rr_leaf(struct fib6_info *f6i_start,
79330c15f03SDavid Ahern 			   struct fib6_info *nomatch, u32 metric,
794b7bc4b6aSDavid Ahern 			   struct fib6_result *res, struct fib6_info **cont,
79530c15f03SDavid Ahern 			   int oif, int strict, bool *do_rr, int *mpri)
79630c15f03SDavid Ahern {
797b7bc4b6aSDavid Ahern 	struct fib6_info *f6i;
79830c15f03SDavid Ahern 
799b7bc4b6aSDavid Ahern 	for (f6i = f6i_start;
800b7bc4b6aSDavid Ahern 	     f6i && f6i != nomatch;
801b7bc4b6aSDavid Ahern 	     f6i = rcu_dereference(f6i->fib6_next)) {
80217a5984eSDavid Ahern 		bool matched = false;
80330c15f03SDavid Ahern 		struct fib6_nh *nh;
80430c15f03SDavid Ahern 
805b7bc4b6aSDavid Ahern 		if (cont && f6i->fib6_metric != metric) {
806b7bc4b6aSDavid Ahern 			*cont = f6i;
80730c15f03SDavid Ahern 			return;
80830c15f03SDavid Ahern 		}
80930c15f03SDavid Ahern 
810b7bc4b6aSDavid Ahern 		if (fib6_check_expired(f6i))
81130c15f03SDavid Ahern 			continue;
81230c15f03SDavid Ahern 
81317a5984eSDavid Ahern 		if (unlikely(f6i->nh)) {
81417a5984eSDavid Ahern 			struct fib6_nh_frl_arg arg = {
81517a5984eSDavid Ahern 				.flags  = f6i->fib6_flags,
81617a5984eSDavid Ahern 				.oif    = oif,
81717a5984eSDavid Ahern 				.strict = strict,
81817a5984eSDavid Ahern 				.mpri   = mpri,
81917a5984eSDavid Ahern 				.do_rr  = do_rr
82017a5984eSDavid Ahern 			};
82117a5984eSDavid Ahern 
82217a5984eSDavid Ahern 			if (nexthop_is_blackhole(f6i->nh)) {
82317a5984eSDavid Ahern 				res->fib6_flags = RTF_REJECT;
82417a5984eSDavid Ahern 				res->fib6_type = RTN_BLACKHOLE;
82517a5984eSDavid Ahern 				res->f6i = f6i;
82617a5984eSDavid Ahern 				res->nh = nexthop_fib6_nh(f6i->nh);
82717a5984eSDavid Ahern 				return;
82817a5984eSDavid Ahern 			}
82917a5984eSDavid Ahern 			if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match,
83017a5984eSDavid Ahern 						     &arg)) {
83117a5984eSDavid Ahern 				matched = true;
83217a5984eSDavid Ahern 				nh = arg.nh;
83317a5984eSDavid Ahern 			}
83417a5984eSDavid Ahern 		} else {
8351cf844c7SDavid Ahern 			nh = f6i->fib6_nh;
83617a5984eSDavid Ahern 			if (find_match(nh, f6i->fib6_flags, oif, strict,
83717a5984eSDavid Ahern 				       mpri, do_rr))
83817a5984eSDavid Ahern 				matched = true;
83917a5984eSDavid Ahern 		}
84017a5984eSDavid Ahern 		if (matched) {
841b7bc4b6aSDavid Ahern 			res->f6i = f6i;
842b7bc4b6aSDavid Ahern 			res->nh = nh;
8437d21fec9SDavid Ahern 			res->fib6_flags = f6i->fib6_flags;
8447d21fec9SDavid Ahern 			res->fib6_type = f6i->fib6_type;
845b7bc4b6aSDavid Ahern 		}
84630c15f03SDavid Ahern 	}
84730c15f03SDavid Ahern }
84830c15f03SDavid Ahern 
find_rr_leaf(struct fib6_node * fn,struct fib6_info * leaf,struct fib6_info * rr_head,int oif,int strict,bool * do_rr,struct fib6_result * res)849b7bc4b6aSDavid Ahern static void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
850b7bc4b6aSDavid Ahern 			 struct fib6_info *rr_head, int oif, int strict,
851b7bc4b6aSDavid Ahern 			 bool *do_rr, struct fib6_result *res)
852f11e6659SDavid S. Miller {
853b7bc4b6aSDavid Ahern 	u32 metric = rr_head->fib6_metric;
854b7bc4b6aSDavid Ahern 	struct fib6_info *cont = NULL;
855f11e6659SDavid S. Miller 	int mpri = -1;
856f11e6659SDavid S. Miller 
857b7bc4b6aSDavid Ahern 	__find_rr_leaf(rr_head, NULL, metric, res, &cont,
85830c15f03SDavid Ahern 		       oif, strict, do_rr, &mpri);
8599fbdcfafSSteffen Klassert 
860b7bc4b6aSDavid Ahern 	__find_rr_leaf(leaf, rr_head, metric, res, &cont,
86130c15f03SDavid Ahern 		       oif, strict, do_rr, &mpri);
8629fbdcfafSSteffen Klassert 
863b7bc4b6aSDavid Ahern 	if (res->f6i || !cont)
864b7bc4b6aSDavid Ahern 		return;
8659fbdcfafSSteffen Klassert 
866b7bc4b6aSDavid Ahern 	__find_rr_leaf(cont, NULL, metric, res, NULL,
86730c15f03SDavid Ahern 		       oif, strict, do_rr, &mpri);
868f11e6659SDavid S. Miller }
869f11e6659SDavid S. Miller 
rt6_select(struct net * net,struct fib6_node * fn,int oif,struct fib6_result * res,int strict)870b7bc4b6aSDavid Ahern static void rt6_select(struct net *net, struct fib6_node *fn, int oif,
871b7bc4b6aSDavid Ahern 		       struct fib6_result *res, int strict)
872f11e6659SDavid S. Miller {
8738d1c802bSDavid Ahern 	struct fib6_info *leaf = rcu_dereference(fn->leaf);
874b7bc4b6aSDavid Ahern 	struct fib6_info *rt0;
875afc154e9SHannes Frederic Sowa 	bool do_rr = false;
87617ecf590SWei Wang 	int key_plen;
877f11e6659SDavid S. Miller 
878b7bc4b6aSDavid Ahern 	/* make sure this function or its helpers sets f6i */
879b7bc4b6aSDavid Ahern 	res->f6i = NULL;
880b7bc4b6aSDavid Ahern 
881421842edSDavid Ahern 	if (!leaf || leaf == net->ipv6.fib6_null_entry)
882b7bc4b6aSDavid Ahern 		goto out;
8838d1040e8SWei Wang 
88466f5d6ceSWei Wang 	rt0 = rcu_dereference(fn->rr_ptr);
885f11e6659SDavid S. Miller 	if (!rt0)
88666f5d6ceSWei Wang 		rt0 = leaf;
887f11e6659SDavid S. Miller 
88817ecf590SWei Wang 	/* Double check to make sure fn is not an intermediate node
88917ecf590SWei Wang 	 * and fn->leaf does not points to its child's leaf
89017ecf590SWei Wang 	 * (This might happen if all routes under fn are deleted from
89117ecf590SWei Wang 	 * the tree and fib6_repair_tree() is called on the node.)
89217ecf590SWei Wang 	 */
89393c2fb25SDavid Ahern 	key_plen = rt0->fib6_dst.plen;
89417ecf590SWei Wang #ifdef CONFIG_IPV6_SUBTREES
89593c2fb25SDavid Ahern 	if (rt0->fib6_src.plen)
89693c2fb25SDavid Ahern 		key_plen = rt0->fib6_src.plen;
89717ecf590SWei Wang #endif
89817ecf590SWei Wang 	if (fn->fn_bit != key_plen)
899b7bc4b6aSDavid Ahern 		goto out;
90017ecf590SWei Wang 
901b7bc4b6aSDavid Ahern 	find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
902afc154e9SHannes Frederic Sowa 	if (do_rr) {
9038fb11a9aSDavid Ahern 		struct fib6_info *next = rcu_dereference(rt0->fib6_next);
904f11e6659SDavid S. Miller 
905554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
90693c2fb25SDavid Ahern 		if (!next || next->fib6_metric != rt0->fib6_metric)
9078d1040e8SWei Wang 			next = leaf;
908f11e6659SDavid S. Miller 
90966f5d6ceSWei Wang 		if (next != rt0) {
91093c2fb25SDavid Ahern 			spin_lock_bh(&leaf->fib6_table->tb6_lock);
91166f5d6ceSWei Wang 			/* make sure next is not being deleted from the tree */
91293c2fb25SDavid Ahern 			if (next->fib6_node)
91366f5d6ceSWei Wang 				rcu_assign_pointer(fn->rr_ptr, next);
91493c2fb25SDavid Ahern 			spin_unlock_bh(&leaf->fib6_table->tb6_lock);
91566f5d6ceSWei Wang 		}
916554cfb7eSYOSHIFUJI Hideaki 	}
917554cfb7eSYOSHIFUJI Hideaki 
918b7bc4b6aSDavid Ahern out:
919b7bc4b6aSDavid Ahern 	if (!res->f6i) {
920b7bc4b6aSDavid Ahern 		res->f6i = net->ipv6.fib6_null_entry;
9211cf844c7SDavid Ahern 		res->nh = res->f6i->fib6_nh;
9227d21fec9SDavid Ahern 		res->fib6_flags = res->f6i->fib6_flags;
9237d21fec9SDavid Ahern 		res->fib6_type = res->f6i->fib6_type;
924b7bc4b6aSDavid Ahern 	}
9251da177e4SLinus Torvalds }
9261da177e4SLinus Torvalds 
rt6_is_gw_or_nonexthop(const struct fib6_result * res)92785bd05deSDavid Ahern static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
9288b9df265SMartin KaFai Lau {
92985bd05deSDavid Ahern 	return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
93085bd05deSDavid Ahern 	       res->nh->fib_nh_gw_family;
9318b9df265SMartin KaFai Lau }
9328b9df265SMartin KaFai Lau 
93370ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
rt6_route_rcv(struct net_device * dev,u8 * opt,int len,const struct in6_addr * gwaddr)93470ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
935b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
93670ceb4f5SYOSHIFUJI Hideaki {
937c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
93870ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
93970ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
94070ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
9414bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
9428d1c802bSDavid Ahern 	struct fib6_info *rt;
94370ceb4f5SYOSHIFUJI Hideaki 
94470ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
94570ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
94670ceb4f5SYOSHIFUJI Hideaki 	}
94770ceb4f5SYOSHIFUJI Hideaki 
94870ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
94970ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
95070ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
95170ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
95270ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
95370ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
95470ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
95570ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
95670ceb4f5SYOSHIFUJI Hideaki 		}
95770ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
95870ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
95970ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
96070ceb4f5SYOSHIFUJI Hideaki 		}
96170ceb4f5SYOSHIFUJI Hideaki 	}
96270ceb4f5SYOSHIFUJI Hideaki 
96370ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
96470ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
9653933fc95SJens Rosenboom 		return -EINVAL;
96670ceb4f5SYOSHIFUJI Hideaki 
9674bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
96870ceb4f5SYOSHIFUJI Hideaki 
96970ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
97070ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
97170ceb4f5SYOSHIFUJI Hideaki 	else {
97270ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
97370ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
97470ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
97570ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
97670ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
97770ceb4f5SYOSHIFUJI Hideaki 	}
97870ceb4f5SYOSHIFUJI Hideaki 
979f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
980afb1d4b5SDavid Ahern 		rt = rt6_get_dflt_router(net, gwaddr, dev);
981f104a567SDuan Jiong 	else
982f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
983830218c1SDavid Ahern 					gwaddr, dev);
98470ceb4f5SYOSHIFUJI Hideaki 
98570ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
98611dd74b3SRoopa Prabhu 		ip6_del_rt(net, rt, false);
98770ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
98870ceb4f5SYOSHIFUJI Hideaki 	}
98970ceb4f5SYOSHIFUJI Hideaki 
99070ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
991830218c1SDavid Ahern 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
992830218c1SDavid Ahern 					dev, pref);
99370ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
99493c2fb25SDavid Ahern 		rt->fib6_flags = RTF_ROUTEINFO |
99593c2fb25SDavid Ahern 				 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
99670ceb4f5SYOSHIFUJI Hideaki 
99770ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
9981716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
99914895687SDavid Ahern 			fib6_clean_expires(rt);
10001716a961SGao feng 		else
100114895687SDavid Ahern 			fib6_set_expires(rt, jiffies + HZ * lifetime);
10021716a961SGao feng 
100393531c67SDavid Ahern 		fib6_info_release(rt);
100470ceb4f5SYOSHIFUJI Hideaki 	}
100570ceb4f5SYOSHIFUJI Hideaki 	return 0;
100670ceb4f5SYOSHIFUJI Hideaki }
100770ceb4f5SYOSHIFUJI Hideaki #endif
100870ceb4f5SYOSHIFUJI Hideaki 
1009ae90d867SDavid Ahern /*
1010ae90d867SDavid Ahern  *	Misc support functions
1011ae90d867SDavid Ahern  */
1012ae90d867SDavid Ahern 
1013ae90d867SDavid Ahern /* called with rcu_lock held */
ip6_rt_get_dev_rcu(const struct fib6_result * res)10140d161581SDavid Ahern static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
1015ae90d867SDavid Ahern {
10160d161581SDavid Ahern 	struct net_device *dev = res->nh->fib_nh_dev;
1017ae90d867SDavid Ahern 
10187d21fec9SDavid Ahern 	if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
1019ae90d867SDavid Ahern 		/* for copies of local routes, dst->dev needs to be the
1020ae90d867SDavid Ahern 		 * device if it is a master device, the master device if
1021ae90d867SDavid Ahern 		 * device is enslaved, and the loopback as the default
1022ae90d867SDavid Ahern 		 */
1023ae90d867SDavid Ahern 		if (netif_is_l3_slave(dev) &&
10247d21fec9SDavid Ahern 		    !rt6_need_strict(&res->f6i->fib6_dst.addr))
1025ae90d867SDavid Ahern 			dev = l3mdev_master_dev_rcu(dev);
1026ae90d867SDavid Ahern 		else if (!netif_is_l3_master(dev))
1027ae90d867SDavid Ahern 			dev = dev_net(dev)->loopback_dev;
1028ae90d867SDavid Ahern 		/* last case is netif_is_l3_master(dev) is true in which
1029ae90d867SDavid Ahern 		 * case we want dev returned to be dev
1030ae90d867SDavid Ahern 		 */
1031ae90d867SDavid Ahern 	}
1032ae90d867SDavid Ahern 
1033ae90d867SDavid Ahern 	return dev;
1034ae90d867SDavid Ahern }
1035ae90d867SDavid Ahern 
10366edb3c96SDavid Ahern static const int fib6_prop[RTN_MAX + 1] = {
10376edb3c96SDavid Ahern 	[RTN_UNSPEC]	= 0,
10386edb3c96SDavid Ahern 	[RTN_UNICAST]	= 0,
10396edb3c96SDavid Ahern 	[RTN_LOCAL]	= 0,
10406edb3c96SDavid Ahern 	[RTN_BROADCAST]	= 0,
10416edb3c96SDavid Ahern 	[RTN_ANYCAST]	= 0,
10426edb3c96SDavid Ahern 	[RTN_MULTICAST]	= 0,
10436edb3c96SDavid Ahern 	[RTN_BLACKHOLE]	= -EINVAL,
10446edb3c96SDavid Ahern 	[RTN_UNREACHABLE] = -EHOSTUNREACH,
10456edb3c96SDavid Ahern 	[RTN_PROHIBIT]	= -EACCES,
10466edb3c96SDavid Ahern 	[RTN_THROW]	= -EAGAIN,
10476edb3c96SDavid Ahern 	[RTN_NAT]	= -EINVAL,
10486edb3c96SDavid Ahern 	[RTN_XRESOLVE]	= -EINVAL,
10496edb3c96SDavid Ahern };
10506edb3c96SDavid Ahern 
ip6_rt_type_to_error(u8 fib6_type)10516edb3c96SDavid Ahern static int ip6_rt_type_to_error(u8 fib6_type)
10526edb3c96SDavid Ahern {
10536edb3c96SDavid Ahern 	return fib6_prop[fib6_type];
10546edb3c96SDavid Ahern }
10556edb3c96SDavid Ahern 
fib6_info_dst_flags(struct fib6_info * rt)10568d1c802bSDavid Ahern static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
10573b6761d1SDavid Ahern {
10583b6761d1SDavid Ahern 	unsigned short flags = 0;
10593b6761d1SDavid Ahern 
10603b6761d1SDavid Ahern 	if (rt->dst_nocount)
10613b6761d1SDavid Ahern 		flags |= DST_NOCOUNT;
10623b6761d1SDavid Ahern 	if (rt->dst_nopolicy)
10633b6761d1SDavid Ahern 		flags |= DST_NOPOLICY;
10643b6761d1SDavid Ahern 
10653b6761d1SDavid Ahern 	return flags;
10663b6761d1SDavid Ahern }
10673b6761d1SDavid Ahern 
ip6_rt_init_dst_reject(struct rt6_info * rt,u8 fib6_type)10687d21fec9SDavid Ahern static void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type)
10696edb3c96SDavid Ahern {
10707d21fec9SDavid Ahern 	rt->dst.error = ip6_rt_type_to_error(fib6_type);
10716edb3c96SDavid Ahern 
10727d21fec9SDavid Ahern 	switch (fib6_type) {
10736edb3c96SDavid Ahern 	case RTN_BLACKHOLE:
10746edb3c96SDavid Ahern 		rt->dst.output = dst_discard_out;
10756edb3c96SDavid Ahern 		rt->dst.input = dst_discard;
10766edb3c96SDavid Ahern 		break;
10776edb3c96SDavid Ahern 	case RTN_PROHIBIT:
10786edb3c96SDavid Ahern 		rt->dst.output = ip6_pkt_prohibit_out;
10796edb3c96SDavid Ahern 		rt->dst.input = ip6_pkt_prohibit;
10806edb3c96SDavid Ahern 		break;
10816edb3c96SDavid Ahern 	case RTN_THROW:
10826edb3c96SDavid Ahern 	case RTN_UNREACHABLE:
10836edb3c96SDavid Ahern 	default:
10846edb3c96SDavid Ahern 		rt->dst.output = ip6_pkt_discard_out;
10856edb3c96SDavid Ahern 		rt->dst.input = ip6_pkt_discard;
10866edb3c96SDavid Ahern 		break;
10876edb3c96SDavid Ahern 	}
10886edb3c96SDavid Ahern }
10896edb3c96SDavid Ahern 
ip6_rt_init_dst(struct rt6_info * rt,const struct fib6_result * res)10900d161581SDavid Ahern static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
10916edb3c96SDavid Ahern {
10927d21fec9SDavid Ahern 	struct fib6_info *f6i = res->f6i;
10930d161581SDavid Ahern 
10947d21fec9SDavid Ahern 	if (res->fib6_flags & RTF_REJECT) {
10957d21fec9SDavid Ahern 		ip6_rt_init_dst_reject(rt, res->fib6_type);
10966edb3c96SDavid Ahern 		return;
10976edb3c96SDavid Ahern 	}
10986edb3c96SDavid Ahern 
10996edb3c96SDavid Ahern 	rt->dst.error = 0;
11006edb3c96SDavid Ahern 	rt->dst.output = ip6_output;
11016edb3c96SDavid Ahern 
11027d21fec9SDavid Ahern 	if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) {
11036edb3c96SDavid Ahern 		rt->dst.input = ip6_input;
11047d21fec9SDavid Ahern 	} else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
11056edb3c96SDavid Ahern 		rt->dst.input = ip6_mc_input;
11066edb3c96SDavid Ahern 	} else {
11076edb3c96SDavid Ahern 		rt->dst.input = ip6_forward;
11086edb3c96SDavid Ahern 	}
11096edb3c96SDavid Ahern 
11100d161581SDavid Ahern 	if (res->nh->fib_nh_lws) {
11110d161581SDavid Ahern 		rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
11126edb3c96SDavid Ahern 		lwtunnel_set_redirect(&rt->dst);
11136edb3c96SDavid Ahern 	}
11146edb3c96SDavid Ahern 
11156edb3c96SDavid Ahern 	rt->dst.lastuse = jiffies;
11166edb3c96SDavid Ahern }
11176edb3c96SDavid Ahern 
1118e873e4b9SWei Wang /* Caller must already hold reference to @from */
rt6_set_from(struct rt6_info * rt,struct fib6_info * from)11198d1c802bSDavid Ahern static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
1120ae90d867SDavid Ahern {
1121ae90d867SDavid Ahern 	rt->rt6i_flags &= ~RTF_EXPIRES;
1122a68886a6SDavid Ahern 	rcu_assign_pointer(rt->from, from);
1123e1255ed4SDavid Ahern 	ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
1124ae90d867SDavid Ahern }
1125ae90d867SDavid Ahern 
11260d161581SDavid Ahern /* Caller must already hold reference to f6i in result */
ip6_rt_copy_init(struct rt6_info * rt,const struct fib6_result * res)11270d161581SDavid Ahern static void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
1128ae90d867SDavid Ahern {
11290d161581SDavid Ahern 	const struct fib6_nh *nh = res->nh;
11300d161581SDavid Ahern 	const struct net_device *dev = nh->fib_nh_dev;
11310d161581SDavid Ahern 	struct fib6_info *f6i = res->f6i;
1132dcd1f572SDavid Ahern 
11330d161581SDavid Ahern 	ip6_rt_init_dst(rt, res);
11346edb3c96SDavid Ahern 
11350d161581SDavid Ahern 	rt->rt6i_dst = f6i->fib6_dst;
1136dcd1f572SDavid Ahern 	rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
11377d21fec9SDavid Ahern 	rt->rt6i_flags = res->fib6_flags;
11380d161581SDavid Ahern 	if (nh->fib_nh_gw_family) {
11390d161581SDavid Ahern 		rt->rt6i_gateway = nh->fib_nh_gw6;
11402b2450caSDavid Ahern 		rt->rt6i_flags |= RTF_GATEWAY;
11412b2450caSDavid Ahern 	}
11420d161581SDavid Ahern 	rt6_set_from(rt, f6i);
1143ae90d867SDavid Ahern #ifdef CONFIG_IPV6_SUBTREES
11440d161581SDavid Ahern 	rt->rt6i_src = f6i->fib6_src;
1145ae90d867SDavid Ahern #endif
1146ae90d867SDavid Ahern }
1147ae90d867SDavid Ahern 
fib6_backtrack(struct fib6_node * fn,struct in6_addr * saddr)1148a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1149a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
1150a3c00e46SMartin KaFai Lau {
115166f5d6ceSWei Wang 	struct fib6_node *pn, *sn;
1152a3c00e46SMartin KaFai Lau 	while (1) {
1153a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
1154a3c00e46SMartin KaFai Lau 			return NULL;
115566f5d6ceSWei Wang 		pn = rcu_dereference(fn->parent);
115666f5d6ceSWei Wang 		sn = FIB6_SUBTREE(pn);
115766f5d6ceSWei Wang 		if (sn && sn != fn)
11586454743bSDavid Ahern 			fn = fib6_node_lookup(sn, NULL, saddr);
1159a3c00e46SMartin KaFai Lau 		else
1160a3c00e46SMartin KaFai Lau 			fn = pn;
1161a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
1162a3c00e46SMartin KaFai Lau 			return fn;
1163a3c00e46SMartin KaFai Lau 	}
1164a3c00e46SMartin KaFai Lau }
1165c71099acSThomas Graf 
ip6_hold_safe(struct net * net,struct rt6_info ** prt)116610585b43SDavid Ahern static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
1167d3843fe5SWei Wang {
1168d3843fe5SWei Wang 	struct rt6_info *rt = *prt;
1169d3843fe5SWei Wang 
1170d3843fe5SWei Wang 	if (dst_hold_safe(&rt->dst))
1171d3843fe5SWei Wang 		return true;
117210585b43SDavid Ahern 	if (net) {
1173d3843fe5SWei Wang 		rt = net->ipv6.ip6_null_entry;
1174d3843fe5SWei Wang 		dst_hold(&rt->dst);
1175d3843fe5SWei Wang 	} else {
1176d3843fe5SWei Wang 		rt = NULL;
1177d3843fe5SWei Wang 	}
1178d3843fe5SWei Wang 	*prt = rt;
1179d3843fe5SWei Wang 	return false;
1180d3843fe5SWei Wang }
1181d3843fe5SWei Wang 
1182dec9b0e2SDavid Ahern /* called with rcu_lock held */
ip6_create_rt_rcu(const struct fib6_result * res)11839b6b35abSDavid Ahern static struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
1184dec9b0e2SDavid Ahern {
11859b6b35abSDavid Ahern 	struct net_device *dev = res->nh->fib_nh_dev;
11869b6b35abSDavid Ahern 	struct fib6_info *f6i = res->f6i;
11879b6b35abSDavid Ahern 	unsigned short flags;
1188dec9b0e2SDavid Ahern 	struct rt6_info *nrt;
1189dec9b0e2SDavid Ahern 
11909b6b35abSDavid Ahern 	if (!fib6_info_hold_safe(f6i))
11911c87e79aSXin Long 		goto fallback;
1192e873e4b9SWei Wang 
11939b6b35abSDavid Ahern 	flags = fib6_info_dst_flags(f6i);
119493531c67SDavid Ahern 	nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
11951c87e79aSXin Long 	if (!nrt) {
11969b6b35abSDavid Ahern 		fib6_info_release(f6i);
11971c87e79aSXin Long 		goto fallback;
11981c87e79aSXin Long 	}
1199dec9b0e2SDavid Ahern 
12000d161581SDavid Ahern 	ip6_rt_copy_init(nrt, res);
12011c87e79aSXin Long 	return nrt;
12021c87e79aSXin Long 
12031c87e79aSXin Long fallback:
12041c87e79aSXin Long 	nrt = dev_net(dev)->ipv6.ip6_null_entry;
12051c87e79aSXin Long 	dst_hold(&nrt->dst);
1206dec9b0e2SDavid Ahern 	return nrt;
1207dec9b0e2SDavid Ahern }
1208dec9b0e2SDavid Ahern 
ip6_pol_route_lookup(struct net * net,struct fib6_table * table,struct flowi6 * fl6,const struct sk_buff * skb,int flags)120955cced4fSBrian Vazquez INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net,
12108ed67789SDaniel Lezcano 					     struct fib6_table *table,
1211b75cc8f9SDavid Ahern 					     struct flowi6 *fl6,
1212b75cc8f9SDavid Ahern 					     const struct sk_buff *skb,
1213b75cc8f9SDavid Ahern 					     int flags)
12141da177e4SLinus Torvalds {
1215b1d40991SDavid Ahern 	struct fib6_result res = {};
12161da177e4SLinus Torvalds 	struct fib6_node *fn;
121723fb93a4SDavid Ahern 	struct rt6_info *rt;
12181da177e4SLinus Torvalds 
121966f5d6ceSWei Wang 	rcu_read_lock();
12206454743bSDavid Ahern 	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1221c71099acSThomas Graf restart:
1222b1d40991SDavid Ahern 	res.f6i = rcu_dereference(fn->leaf);
1223b1d40991SDavid Ahern 	if (!res.f6i)
1224b1d40991SDavid Ahern 		res.f6i = net->ipv6.fib6_null_entry;
1225af52a52cSDavid Ahern 	else
122675ef7389SDavid Ahern 		rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
122775ef7389SDavid Ahern 				 flags);
1228af52a52cSDavid Ahern 
1229b1d40991SDavid Ahern 	if (res.f6i == net->ipv6.fib6_null_entry) {
1230a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1231a3c00e46SMartin KaFai Lau 		if (fn)
1232a3c00e46SMartin KaFai Lau 			goto restart;
1233af52a52cSDavid Ahern 
1234af52a52cSDavid Ahern 		rt = net->ipv6.ip6_null_entry;
1235af52a52cSDavid Ahern 		dst_hold(&rt->dst);
1236af52a52cSDavid Ahern 		goto out;
1237f88d8ea6SDavid Ahern 	} else if (res.fib6_flags & RTF_REJECT) {
1238f88d8ea6SDavid Ahern 		goto do_create;
1239a3c00e46SMartin KaFai Lau 	}
12402b760fcfSWei Wang 
1241b1d40991SDavid Ahern 	fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1242b1d40991SDavid Ahern 			 fl6->flowi6_oif != 0, skb, flags);
1243b1d40991SDavid Ahern 
12444c9483b2SDavid S. Miller 	/* Search through exception table */
12457e4b5128SDavid Ahern 	rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
124623fb93a4SDavid Ahern 	if (rt) {
124710585b43SDavid Ahern 		if (ip6_hold_safe(net, &rt))
1248d3843fe5SWei Wang 			dst_use_noref(&rt->dst, jiffies);
124923fb93a4SDavid Ahern 	} else {
1250f88d8ea6SDavid Ahern do_create:
12519b6b35abSDavid Ahern 		rt = ip6_create_rt_rcu(&res);
1252dec9b0e2SDavid Ahern 	}
1253d3843fe5SWei Wang 
1254af52a52cSDavid Ahern out:
12558ff2e5b2SDavid Ahern 	trace_fib6_table_lookup(net, &res, table, fl6);
1256af52a52cSDavid Ahern 
125766f5d6ceSWei Wang 	rcu_read_unlock();
1258b811580dSDavid Ahern 
12591da177e4SLinus Torvalds 	return rt;
1260c71099acSThomas Graf }
1261c71099acSThomas Graf 
ip6_route_lookup(struct net * net,struct flowi6 * fl6,const struct sk_buff * skb,int flags)1262ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
1263b75cc8f9SDavid Ahern 				   const struct sk_buff *skb, int flags)
1264ea6e574eSFlorian Westphal {
1265b75cc8f9SDavid Ahern 	return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
1266ea6e574eSFlorian Westphal }
1267ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
1268ea6e574eSFlorian Westphal 
rt6_lookup(struct net * net,const struct in6_addr * daddr,const struct in6_addr * saddr,int oif,const struct sk_buff * skb,int strict)12699acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
1270b75cc8f9SDavid Ahern 			    const struct in6_addr *saddr, int oif,
1271b75cc8f9SDavid Ahern 			    const struct sk_buff *skb, int strict)
1272c71099acSThomas Graf {
12734c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
12744c9483b2SDavid S. Miller 		.flowi6_oif = oif,
12754c9483b2SDavid S. Miller 		.daddr = *daddr,
1276c71099acSThomas Graf 	};
1277c71099acSThomas Graf 	struct dst_entry *dst;
127877d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
1279c71099acSThomas Graf 
1280adaa70bbSThomas Graf 	if (saddr) {
12814c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
1282adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
1283adaa70bbSThomas Graf 	}
1284adaa70bbSThomas Graf 
1285b75cc8f9SDavid Ahern 	dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
1286c71099acSThomas Graf 	if (dst->error == 0)
1287797a4c1fSEric Dumazet 		return dst_rt6_info(dst);
1288c71099acSThomas Graf 
1289c71099acSThomas Graf 	dst_release(dst);
1290c71099acSThomas Graf 
12911da177e4SLinus Torvalds 	return NULL;
12921da177e4SLinus Torvalds }
12937159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
12947159039aSYOSHIFUJI Hideaki 
1295c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
12961cfb71eeSWei Wang  * It takes new route entry, the addition fails by any reason the
12971cfb71eeSWei Wang  * route is released.
12981cfb71eeSWei Wang  * Caller must hold dst before calling it.
12991da177e4SLinus Torvalds  */
13001da177e4SLinus Torvalds 
__ip6_ins_rt(struct fib6_info * rt,struct nl_info * info,struct netlink_ext_ack * extack)13018d1c802bSDavid Ahern static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
1302333c4301SDavid Ahern 			struct netlink_ext_ack *extack)
13031da177e4SLinus Torvalds {
13041da177e4SLinus Torvalds 	int err;
1305c71099acSThomas Graf 	struct fib6_table *table;
13061da177e4SLinus Torvalds 
130793c2fb25SDavid Ahern 	table = rt->fib6_table;
130866f5d6ceSWei Wang 	spin_lock_bh(&table->tb6_lock);
1309d4ead6b3SDavid Ahern 	err = fib6_add(&table->tb6_root, rt, info, extack);
131066f5d6ceSWei Wang 	spin_unlock_bh(&table->tb6_lock);
13111da177e4SLinus Torvalds 
13121da177e4SLinus Torvalds 	return err;
13131da177e4SLinus Torvalds }
13141da177e4SLinus Torvalds 
ip6_ins_rt(struct net * net,struct fib6_info * rt)13158d1c802bSDavid Ahern int ip6_ins_rt(struct net *net, struct fib6_info *rt)
131640e22e8fSThomas Graf {
1317afb1d4b5SDavid Ahern 	struct nl_info info = {	.nl_net = net, };
1318e715b6d3SFlorian Westphal 
1319d4ead6b3SDavid Ahern 	return __ip6_ins_rt(rt, &info, NULL);
132040e22e8fSThomas Graf }
132140e22e8fSThomas Graf 
ip6_rt_cache_alloc(const struct fib6_result * res,const struct in6_addr * daddr,const struct in6_addr * saddr)132285bd05deSDavid Ahern static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
132321efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
1324b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
13251da177e4SLinus Torvalds {
132685bd05deSDavid Ahern 	struct fib6_info *f6i = res->f6i;
13274832c30dSDavid Ahern 	struct net_device *dev;
13281da177e4SLinus Torvalds 	struct rt6_info *rt;
13291da177e4SLinus Torvalds 
13301da177e4SLinus Torvalds 	/*
13311da177e4SLinus Torvalds 	 *	Clone the route.
13321da177e4SLinus Torvalds 	 */
13331da177e4SLinus Torvalds 
133485bd05deSDavid Ahern 	if (!fib6_info_hold_safe(f6i))
1335e873e4b9SWei Wang 		return NULL;
1336e873e4b9SWei Wang 
13370d161581SDavid Ahern 	dev = ip6_rt_get_dev_rcu(res);
133893531c67SDavid Ahern 	rt = ip6_dst_alloc(dev_net(dev), dev, 0);
1339e873e4b9SWei Wang 	if (!rt) {
134085bd05deSDavid Ahern 		fib6_info_release(f6i);
134183a09abdSMartin KaFai Lau 		return NULL;
1342e873e4b9SWei Wang 	}
134383a09abdSMartin KaFai Lau 
13440d161581SDavid Ahern 	ip6_rt_copy_init(rt, res);
13458b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
134683a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
134783a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
13488b9df265SMartin KaFai Lau 
134985bd05deSDavid Ahern 	if (!rt6_is_gw_or_nonexthop(res)) {
135085bd05deSDavid Ahern 		if (f6i->fib6_dst.plen != 128 &&
135185bd05deSDavid Ahern 		    ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
135258c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
13531da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
13541da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
13554e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
13561da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
13571da177e4SLinus Torvalds 		}
13581da177e4SLinus Torvalds #endif
135995a9a5baSYOSHIFUJI Hideaki 	}
136095a9a5baSYOSHIFUJI Hideaki 
1361299d9939SYOSHIFUJI Hideaki 	return rt;
1362299d9939SYOSHIFUJI Hideaki }
1363299d9939SYOSHIFUJI Hideaki 
ip6_rt_pcpu_alloc(const struct fib6_result * res)1364db3fedeeSDavid Ahern static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
1365d52d3997SMartin KaFai Lau {
1366db3fedeeSDavid Ahern 	struct fib6_info *f6i = res->f6i;
1367db3fedeeSDavid Ahern 	unsigned short flags = fib6_info_dst_flags(f6i);
13684832c30dSDavid Ahern 	struct net_device *dev;
1369d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
1370d52d3997SMartin KaFai Lau 
1371db3fedeeSDavid Ahern 	if (!fib6_info_hold_safe(f6i))
1372e873e4b9SWei Wang 		return NULL;
1373e873e4b9SWei Wang 
13744832c30dSDavid Ahern 	rcu_read_lock();
13750d161581SDavid Ahern 	dev = ip6_rt_get_dev_rcu(res);
1376d8882935SEric Dumazet 	pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags | DST_NOCOUNT);
13774832c30dSDavid Ahern 	rcu_read_unlock();
1378e873e4b9SWei Wang 	if (!pcpu_rt) {
1379db3fedeeSDavid Ahern 		fib6_info_release(f6i);
1380d52d3997SMartin KaFai Lau 		return NULL;
1381e873e4b9SWei Wang 	}
13820d161581SDavid Ahern 	ip6_rt_copy_init(pcpu_rt, res);
1383d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
13848f34e53bSDavid Ahern 
13858f34e53bSDavid Ahern 	if (f6i->nh)
13868f34e53bSDavid Ahern 		pcpu_rt->sernum = rt_genid_ipv6(dev_net(dev));
13878f34e53bSDavid Ahern 
1388d52d3997SMartin KaFai Lau 	return pcpu_rt;
1389d52d3997SMartin KaFai Lau }
1390d52d3997SMartin KaFai Lau 
rt6_is_valid(const struct rt6_info * rt6)13918f34e53bSDavid Ahern static bool rt6_is_valid(const struct rt6_info *rt6)
13928f34e53bSDavid Ahern {
13938f34e53bSDavid Ahern 	return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev));
13948f34e53bSDavid Ahern }
13958f34e53bSDavid Ahern 
139666f5d6ceSWei Wang /* It should be called with rcu_read_lock() acquired */
rt6_get_pcpu_route(const struct fib6_result * res)1397db3fedeeSDavid Ahern static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
1398d52d3997SMartin KaFai Lau {
1399c353071aSEric Dumazet 	struct rt6_info *pcpu_rt;
1400d52d3997SMartin KaFai Lau 
1401c353071aSEric Dumazet 	pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu);
1402d52d3997SMartin KaFai Lau 
14038f34e53bSDavid Ahern 	if (pcpu_rt && pcpu_rt->sernum && !rt6_is_valid(pcpu_rt)) {
14048f34e53bSDavid Ahern 		struct rt6_info *prev, **p;
14058f34e53bSDavid Ahern 
14068f34e53bSDavid Ahern 		p = this_cpu_ptr(res->nh->rt6i_pcpu);
14077e796c3fSEric Dumazet 		/* Paired with READ_ONCE() in __fib6_drop_pcpu_from() */
14088f34e53bSDavid Ahern 		prev = xchg(p, NULL);
14098f34e53bSDavid Ahern 		if (prev) {
14108f34e53bSDavid Ahern 			dst_dev_put(&prev->dst);
14118f34e53bSDavid Ahern 			dst_release(&prev->dst);
14128f34e53bSDavid Ahern 		}
14138f34e53bSDavid Ahern 
14148f34e53bSDavid Ahern 		pcpu_rt = NULL;
14158f34e53bSDavid Ahern 	}
14168f34e53bSDavid Ahern 
1417a73e4195SMartin KaFai Lau 	return pcpu_rt;
1418a73e4195SMartin KaFai Lau }
1419a73e4195SMartin KaFai Lau 
rt6_make_pcpu_route(struct net * net,const struct fib6_result * res)1420afb1d4b5SDavid Ahern static struct rt6_info *rt6_make_pcpu_route(struct net *net,
1421db3fedeeSDavid Ahern 					    const struct fib6_result *res)
1422a73e4195SMartin KaFai Lau {
1423a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
1424d52d3997SMartin KaFai Lau 
1425db3fedeeSDavid Ahern 	pcpu_rt = ip6_rt_pcpu_alloc(res);
14260e09edccSWei Wang 	if (!pcpu_rt)
14270e09edccSWei Wang 		return NULL;
1428d52d3997SMartin KaFai Lau 
1429f40b6ae2SDavid Ahern 	p = this_cpu_ptr(res->nh->rt6i_pcpu);
1430d52d3997SMartin KaFai Lau 	prev = cmpxchg(p, NULL, pcpu_rt);
1431951f788aSEric Dumazet 	BUG_ON(prev);
1432a94b9367SWei Wang 
143361fb0d01SEric Dumazet 	if (res->f6i->fib6_destroying) {
143461fb0d01SEric Dumazet 		struct fib6_info *from;
143561fb0d01SEric Dumazet 
143670530a2fSEric Dumazet 		from = unrcu_pointer(xchg(&pcpu_rt->from, NULL));
143761fb0d01SEric Dumazet 		fib6_info_release(from);
143861fb0d01SEric Dumazet 	}
143961fb0d01SEric Dumazet 
1440d52d3997SMartin KaFai Lau 	return pcpu_rt;
1441d52d3997SMartin KaFai Lau }
1442d52d3997SMartin KaFai Lau 
144335732d01SWei Wang /* exception hash table implementation
144435732d01SWei Wang  */
144535732d01SWei Wang static DEFINE_SPINLOCK(rt6_exception_lock);
144635732d01SWei Wang 
144735732d01SWei Wang /* Remove rt6_ex from hash table and free the memory
144835732d01SWei Wang  * Caller must hold rt6_exception_lock
144935732d01SWei Wang  */
rt6_remove_exception(struct rt6_exception_bucket * bucket,struct rt6_exception * rt6_ex)145035732d01SWei Wang static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
145135732d01SWei Wang 				 struct rt6_exception *rt6_ex)
145235732d01SWei Wang {
1453b2427e67SColin Ian King 	struct net *net;
145481eb8447SWei Wang 
145535732d01SWei Wang 	if (!bucket || !rt6_ex)
145635732d01SWei Wang 		return;
1457b2427e67SColin Ian King 
1458b2427e67SColin Ian King 	net = dev_net(rt6_ex->rt6i->dst.dev);
1459f5b51fe8SPaolo Abeni 	net->ipv6.rt6_stats->fib_rt_cache--;
1460f5b51fe8SPaolo Abeni 
1461f5b51fe8SPaolo Abeni 	/* purge completely the exception to allow releasing the held resources:
1462f5b51fe8SPaolo Abeni 	 * some [sk] cache may keep the dst around for unlimited time
1463f5b51fe8SPaolo Abeni 	 */
1464f5b51fe8SPaolo Abeni 	dst_dev_put(&rt6_ex->rt6i->dst);
1465f5b51fe8SPaolo Abeni 
146635732d01SWei Wang 	hlist_del_rcu(&rt6_ex->hlist);
146777634cc6SDavid Ahern 	dst_release(&rt6_ex->rt6i->dst);
146835732d01SWei Wang 	kfree_rcu(rt6_ex, rcu);
146935732d01SWei Wang 	WARN_ON_ONCE(!bucket->depth);
147035732d01SWei Wang 	bucket->depth--;
147135732d01SWei Wang }
147235732d01SWei Wang 
147335732d01SWei Wang /* Remove oldest rt6_ex in bucket and free the memory
147435732d01SWei Wang  * Caller must hold rt6_exception_lock
147535732d01SWei Wang  */
rt6_exception_remove_oldest(struct rt6_exception_bucket * bucket)147635732d01SWei Wang static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
147735732d01SWei Wang {
147835732d01SWei Wang 	struct rt6_exception *rt6_ex, *oldest = NULL;
147935732d01SWei Wang 
148035732d01SWei Wang 	if (!bucket)
148135732d01SWei Wang 		return;
148235732d01SWei Wang 
148335732d01SWei Wang 	hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
148435732d01SWei Wang 		if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
148535732d01SWei Wang 			oldest = rt6_ex;
148635732d01SWei Wang 	}
148735732d01SWei Wang 	rt6_remove_exception(bucket, oldest);
148835732d01SWei Wang }
148935732d01SWei Wang 
rt6_exception_hash(const struct in6_addr * dst,const struct in6_addr * src)149035732d01SWei Wang static u32 rt6_exception_hash(const struct in6_addr *dst,
149135732d01SWei Wang 			      const struct in6_addr *src)
149235732d01SWei Wang {
149349ecc2e9SEric Dumazet 	static siphash_aligned_key_t rt6_exception_key;
14944785305cSEric Dumazet 	struct {
14954785305cSEric Dumazet 		struct in6_addr dst;
14964785305cSEric Dumazet 		struct in6_addr src;
14974785305cSEric Dumazet 	} __aligned(SIPHASH_ALIGNMENT) combined = {
14984785305cSEric Dumazet 		.dst = *dst,
14994785305cSEric Dumazet 	};
15004785305cSEric Dumazet 	u64 val;
150135732d01SWei Wang 
15024785305cSEric Dumazet 	net_get_random_once(&rt6_exception_key, sizeof(rt6_exception_key));
150335732d01SWei Wang 
150435732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES
150535732d01SWei Wang 	if (src)
15064785305cSEric Dumazet 		combined.src = *src;
150735732d01SWei Wang #endif
15084785305cSEric Dumazet 	val = siphash(&combined, sizeof(combined), &rt6_exception_key);
15094785305cSEric Dumazet 
15104785305cSEric Dumazet 	return hash_64(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
151135732d01SWei Wang }
151235732d01SWei Wang 
151335732d01SWei Wang /* Helper function to find the cached rt in the hash table
151435732d01SWei Wang  * and update bucket pointer to point to the bucket for this
151535732d01SWei Wang  * (daddr, saddr) pair
151635732d01SWei Wang  * Caller must hold rt6_exception_lock
151735732d01SWei Wang  */
151835732d01SWei Wang static struct rt6_exception *
__rt6_find_exception_spinlock(struct rt6_exception_bucket ** bucket,const struct in6_addr * daddr,const struct in6_addr * saddr)151935732d01SWei Wang __rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
152035732d01SWei Wang 			      const struct in6_addr *daddr,
152135732d01SWei Wang 			      const struct in6_addr *saddr)
152235732d01SWei Wang {
152335732d01SWei Wang 	struct rt6_exception *rt6_ex;
152435732d01SWei Wang 	u32 hval;
152535732d01SWei Wang 
152635732d01SWei Wang 	if (!(*bucket) || !daddr)
152735732d01SWei Wang 		return NULL;
152835732d01SWei Wang 
152935732d01SWei Wang 	hval = rt6_exception_hash(daddr, saddr);
153035732d01SWei Wang 	*bucket += hval;
153135732d01SWei Wang 
153235732d01SWei Wang 	hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
153335732d01SWei Wang 		struct rt6_info *rt6 = rt6_ex->rt6i;
153435732d01SWei Wang 		bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
153535732d01SWei Wang 
153635732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES
153735732d01SWei Wang 		if (matched && saddr)
153835732d01SWei Wang 			matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
153935732d01SWei Wang #endif
154035732d01SWei Wang 		if (matched)
154135732d01SWei Wang 			return rt6_ex;
154235732d01SWei Wang 	}
154335732d01SWei Wang 	return NULL;
154435732d01SWei Wang }
154535732d01SWei Wang 
154635732d01SWei Wang /* Helper function to find the cached rt in the hash table
154735732d01SWei Wang  * and update bucket pointer to point to the bucket for this
154835732d01SWei Wang  * (daddr, saddr) pair
154935732d01SWei Wang  * Caller must hold rcu_read_lock()
155035732d01SWei Wang  */
155135732d01SWei Wang static struct rt6_exception *
__rt6_find_exception_rcu(struct rt6_exception_bucket ** bucket,const struct in6_addr * daddr,const struct in6_addr * saddr)155235732d01SWei Wang __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
155335732d01SWei Wang 			 const struct in6_addr *daddr,
155435732d01SWei Wang 			 const struct in6_addr *saddr)
155535732d01SWei Wang {
155635732d01SWei Wang 	struct rt6_exception *rt6_ex;
155735732d01SWei Wang 	u32 hval;
155835732d01SWei Wang 
155935732d01SWei Wang 	WARN_ON_ONCE(!rcu_read_lock_held());
156035732d01SWei Wang 
156135732d01SWei Wang 	if (!(*bucket) || !daddr)
156235732d01SWei Wang 		return NULL;
156335732d01SWei Wang 
156435732d01SWei Wang 	hval = rt6_exception_hash(daddr, saddr);
156535732d01SWei Wang 	*bucket += hval;
156635732d01SWei Wang 
156735732d01SWei Wang 	hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
156835732d01SWei Wang 		struct rt6_info *rt6 = rt6_ex->rt6i;
156935732d01SWei Wang 		bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
157035732d01SWei Wang 
157135732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES
157235732d01SWei Wang 		if (matched && saddr)
157335732d01SWei Wang 			matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
157435732d01SWei Wang #endif
157535732d01SWei Wang 		if (matched)
157635732d01SWei Wang 			return rt6_ex;
157735732d01SWei Wang 	}
157835732d01SWei Wang 	return NULL;
157935732d01SWei Wang }
158035732d01SWei Wang 
fib6_mtu(const struct fib6_result * res)1581b748f260SDavid Ahern static unsigned int fib6_mtu(const struct fib6_result *res)
158235732d01SWei Wang {
1583b748f260SDavid Ahern 	const struct fib6_nh *nh = res->nh;
1584d4ead6b3SDavid Ahern 	unsigned int mtu;
1585d4ead6b3SDavid Ahern 
1586b748f260SDavid Ahern 	if (res->f6i->fib6_pmtu) {
1587b748f260SDavid Ahern 		mtu = res->f6i->fib6_pmtu;
1588dcd1f572SDavid Ahern 	} else {
1589b748f260SDavid Ahern 		struct net_device *dev = nh->fib_nh_dev;
1590dcd1f572SDavid Ahern 		struct inet6_dev *idev;
1591dcd1f572SDavid Ahern 
1592dcd1f572SDavid Ahern 		rcu_read_lock();
1593dcd1f572SDavid Ahern 		idev = __in6_dev_get(dev);
1594dcd1f572SDavid Ahern 		mtu = idev->cnf.mtu6;
1595dcd1f572SDavid Ahern 		rcu_read_unlock();
1596dcd1f572SDavid Ahern 	}
1597dcd1f572SDavid Ahern 
1598d4ead6b3SDavid Ahern 	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1599d4ead6b3SDavid Ahern 
1600b748f260SDavid Ahern 	return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
1601d4ead6b3SDavid Ahern }
1602d4ead6b3SDavid Ahern 
1603cc5c073aSDavid Ahern #define FIB6_EXCEPTION_BUCKET_FLUSHED  0x1UL
1604cc5c073aSDavid Ahern 
1605cc5c073aSDavid Ahern /* used when the flushed bit is not relevant, only access to the bucket
1606cc5c073aSDavid Ahern  * (ie., all bucket users except rt6_insert_exception);
1607cc5c073aSDavid Ahern  *
1608cc5c073aSDavid Ahern  * called under rcu lock; sometimes called with rt6_exception_lock held
1609cc5c073aSDavid Ahern  */
1610cc5c073aSDavid Ahern static
fib6_nh_get_excptn_bucket(const struct fib6_nh * nh,spinlock_t * lock)1611cc5c073aSDavid Ahern struct rt6_exception_bucket *fib6_nh_get_excptn_bucket(const struct fib6_nh *nh,
1612cc5c073aSDavid Ahern 						       spinlock_t *lock)
1613cc5c073aSDavid Ahern {
1614cc5c073aSDavid Ahern 	struct rt6_exception_bucket *bucket;
1615cc5c073aSDavid Ahern 
1616cc5c073aSDavid Ahern 	if (lock)
1617cc5c073aSDavid Ahern 		bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
1618cc5c073aSDavid Ahern 						   lockdep_is_held(lock));
1619cc5c073aSDavid Ahern 	else
1620cc5c073aSDavid Ahern 		bucket = rcu_dereference(nh->rt6i_exception_bucket);
1621cc5c073aSDavid Ahern 
1622cc5c073aSDavid Ahern 	/* remove bucket flushed bit if set */
1623cc5c073aSDavid Ahern 	if (bucket) {
1624cc5c073aSDavid Ahern 		unsigned long p = (unsigned long)bucket;
1625cc5c073aSDavid Ahern 
1626cc5c073aSDavid Ahern 		p &= ~FIB6_EXCEPTION_BUCKET_FLUSHED;
1627cc5c073aSDavid Ahern 		bucket = (struct rt6_exception_bucket *)p;
1628cc5c073aSDavid Ahern 	}
1629cc5c073aSDavid Ahern 
1630cc5c073aSDavid Ahern 	return bucket;
1631cc5c073aSDavid Ahern }
1632cc5c073aSDavid Ahern 
fib6_nh_excptn_bucket_flushed(struct rt6_exception_bucket * bucket)1633cc5c073aSDavid Ahern static bool fib6_nh_excptn_bucket_flushed(struct rt6_exception_bucket *bucket)
1634cc5c073aSDavid Ahern {
1635cc5c073aSDavid Ahern 	unsigned long p = (unsigned long)bucket;
1636cc5c073aSDavid Ahern 
1637cc5c073aSDavid Ahern 	return !!(p & FIB6_EXCEPTION_BUCKET_FLUSHED);
1638cc5c073aSDavid Ahern }
1639cc5c073aSDavid Ahern 
1640cc5c073aSDavid Ahern /* called with rt6_exception_lock held */
fib6_nh_excptn_bucket_set_flushed(struct fib6_nh * nh,spinlock_t * lock)1641cc5c073aSDavid Ahern static void fib6_nh_excptn_bucket_set_flushed(struct fib6_nh *nh,
1642cc5c073aSDavid Ahern 					      spinlock_t *lock)
1643cc5c073aSDavid Ahern {
1644cc5c073aSDavid Ahern 	struct rt6_exception_bucket *bucket;
1645cc5c073aSDavid Ahern 	unsigned long p;
1646cc5c073aSDavid Ahern 
1647cc5c073aSDavid Ahern 	bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
1648cc5c073aSDavid Ahern 					   lockdep_is_held(lock));
1649cc5c073aSDavid Ahern 
1650cc5c073aSDavid Ahern 	p = (unsigned long)bucket;
1651cc5c073aSDavid Ahern 	p |= FIB6_EXCEPTION_BUCKET_FLUSHED;
1652cc5c073aSDavid Ahern 	bucket = (struct rt6_exception_bucket *)p;
1653cc5c073aSDavid Ahern 	rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
1654cc5c073aSDavid Ahern }
1655cc5c073aSDavid Ahern 
rt6_insert_exception(struct rt6_info * nrt,const struct fib6_result * res)165635732d01SWei Wang static int rt6_insert_exception(struct rt6_info *nrt,
16575012f0a5SDavid Ahern 				const struct fib6_result *res)
165835732d01SWei Wang {
16595e670d84SDavid Ahern 	struct net *net = dev_net(nrt->dst.dev);
166035732d01SWei Wang 	struct rt6_exception_bucket *bucket;
1661cc5c073aSDavid Ahern 	struct fib6_info *f6i = res->f6i;
166235732d01SWei Wang 	struct in6_addr *src_key = NULL;
166335732d01SWei Wang 	struct rt6_exception *rt6_ex;
1664cc5c073aSDavid Ahern 	struct fib6_nh *nh = res->nh;
1665a00df2caSEric Dumazet 	int max_depth;
166635732d01SWei Wang 	int err = 0;
166735732d01SWei Wang 
166835732d01SWei Wang 	spin_lock_bh(&rt6_exception_lock);
166935732d01SWei Wang 
1670cc5c073aSDavid Ahern 	bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
167135732d01SWei Wang 					  lockdep_is_held(&rt6_exception_lock));
167235732d01SWei Wang 	if (!bucket) {
167335732d01SWei Wang 		bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
167435732d01SWei Wang 				 GFP_ATOMIC);
167535732d01SWei Wang 		if (!bucket) {
167635732d01SWei Wang 			err = -ENOMEM;
167735732d01SWei Wang 			goto out;
167835732d01SWei Wang 		}
1679cc5c073aSDavid Ahern 		rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
1680cc5c073aSDavid Ahern 	} else if (fib6_nh_excptn_bucket_flushed(bucket)) {
1681cc5c073aSDavid Ahern 		err = -EINVAL;
1682cc5c073aSDavid Ahern 		goto out;
168335732d01SWei Wang 	}
168435732d01SWei Wang 
168535732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES
16865012f0a5SDavid Ahern 	/* fib6_src.plen != 0 indicates f6i is in subtree
168735732d01SWei Wang 	 * and exception table is indexed by a hash of
16885012f0a5SDavid Ahern 	 * both fib6_dst and fib6_src.
168935732d01SWei Wang 	 * Otherwise, the exception table is indexed by
16905012f0a5SDavid Ahern 	 * a hash of only fib6_dst.
169135732d01SWei Wang 	 */
16925012f0a5SDavid Ahern 	if (f6i->fib6_src.plen)
169335732d01SWei Wang 		src_key = &nrt->rt6i_src.addr;
169435732d01SWei Wang #endif
16955012f0a5SDavid Ahern 	/* rt6_mtu_change() might lower mtu on f6i.
1696f5bbe7eeSWei Wang 	 * Only insert this exception route if its mtu
16975012f0a5SDavid Ahern 	 * is less than f6i's mtu value.
1698f5bbe7eeSWei Wang 	 */
1699b748f260SDavid Ahern 	if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
1700f5bbe7eeSWei Wang 		err = -EINVAL;
1701f5bbe7eeSWei Wang 		goto out;
1702f5bbe7eeSWei Wang 	}
170360006a48SWei Wang 
170435732d01SWei Wang 	rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
170535732d01SWei Wang 					       src_key);
170635732d01SWei Wang 	if (rt6_ex)
170735732d01SWei Wang 		rt6_remove_exception(bucket, rt6_ex);
170835732d01SWei Wang 
170935732d01SWei Wang 	rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
171035732d01SWei Wang 	if (!rt6_ex) {
171135732d01SWei Wang 		err = -ENOMEM;
171235732d01SWei Wang 		goto out;
171335732d01SWei Wang 	}
171435732d01SWei Wang 	rt6_ex->rt6i = nrt;
171535732d01SWei Wang 	rt6_ex->stamp = jiffies;
171635732d01SWei Wang 	hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
171735732d01SWei Wang 	bucket->depth++;
171881eb8447SWei Wang 	net->ipv6.rt6_stats->fib_rt_cache++;
171935732d01SWei Wang 
1720a00df2caSEric Dumazet 	/* Randomize max depth to avoid some side channels attacks. */
17218032bf12SJason A. Donenfeld 	max_depth = FIB6_MAX_DEPTH + get_random_u32_below(FIB6_MAX_DEPTH);
1722a00df2caSEric Dumazet 	while (bucket->depth > max_depth)
172335732d01SWei Wang 		rt6_exception_remove_oldest(bucket);
172435732d01SWei Wang 
172535732d01SWei Wang out:
172635732d01SWei Wang 	spin_unlock_bh(&rt6_exception_lock);
172735732d01SWei Wang 
172835732d01SWei Wang 	/* Update fn->fn_sernum to invalidate all cached dst */
1729b886d5f2SPaolo Abeni 	if (!err) {
17305012f0a5SDavid Ahern 		spin_lock_bh(&f6i->fib6_table->tb6_lock);
17315012f0a5SDavid Ahern 		fib6_update_sernum(net, f6i);
17325012f0a5SDavid Ahern 		spin_unlock_bh(&f6i->fib6_table->tb6_lock);
1733b886d5f2SPaolo Abeni 		fib6_force_start_gc(net);
1734b886d5f2SPaolo Abeni 	}
173535732d01SWei Wang 
173635732d01SWei Wang 	return err;
173735732d01SWei Wang }
173835732d01SWei Wang 
fib6_nh_flush_exceptions(struct fib6_nh * nh,struct fib6_info * from)1739c0b220cfSDavid Ahern static void fib6_nh_flush_exceptions(struct fib6_nh *nh, struct fib6_info *from)
174035732d01SWei Wang {
174135732d01SWei Wang 	struct rt6_exception_bucket *bucket;
174235732d01SWei Wang 	struct rt6_exception *rt6_ex;
174335732d01SWei Wang 	struct hlist_node *tmp;
174435732d01SWei Wang 	int i;
174535732d01SWei Wang 
174635732d01SWei Wang 	spin_lock_bh(&rt6_exception_lock);
174735732d01SWei Wang 
1748cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
174935732d01SWei Wang 	if (!bucket)
175035732d01SWei Wang 		goto out;
175135732d01SWei Wang 
1752cc5c073aSDavid Ahern 	/* Prevent rt6_insert_exception() to recreate the bucket list */
1753cc5c073aSDavid Ahern 	if (!from)
1754cc5c073aSDavid Ahern 		fib6_nh_excptn_bucket_set_flushed(nh, &rt6_exception_lock);
1755cc5c073aSDavid Ahern 
175635732d01SWei Wang 	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1757cc5c073aSDavid Ahern 		hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) {
1758cc5c073aSDavid Ahern 			if (!from ||
1759cc5c073aSDavid Ahern 			    rcu_access_pointer(rt6_ex->rt6i->from) == from)
176035732d01SWei Wang 				rt6_remove_exception(bucket, rt6_ex);
1761cc5c073aSDavid Ahern 		}
1762cc5c073aSDavid Ahern 		WARN_ON_ONCE(!from && bucket->depth);
176335732d01SWei Wang 		bucket++;
176435732d01SWei Wang 	}
176535732d01SWei Wang out:
176635732d01SWei Wang 	spin_unlock_bh(&rt6_exception_lock);
176735732d01SWei Wang }
176835732d01SWei Wang 
rt6_nh_flush_exceptions(struct fib6_nh * nh,void * arg)1769e659ba31SDavid Ahern static int rt6_nh_flush_exceptions(struct fib6_nh *nh, void *arg)
1770e659ba31SDavid Ahern {
1771e659ba31SDavid Ahern 	struct fib6_info *f6i = arg;
1772e659ba31SDavid Ahern 
1773e659ba31SDavid Ahern 	fib6_nh_flush_exceptions(nh, f6i);
1774e659ba31SDavid Ahern 
1775e659ba31SDavid Ahern 	return 0;
1776e659ba31SDavid Ahern }
1777e659ba31SDavid Ahern 
rt6_flush_exceptions(struct fib6_info * f6i)1778c0b220cfSDavid Ahern void rt6_flush_exceptions(struct fib6_info *f6i)
1779c0b220cfSDavid Ahern {
1780e659ba31SDavid Ahern 	if (f6i->nh)
1781e659ba31SDavid Ahern 		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_flush_exceptions,
1782e659ba31SDavid Ahern 					 f6i);
1783e659ba31SDavid Ahern 	else
17841cf844c7SDavid Ahern 		fib6_nh_flush_exceptions(f6i->fib6_nh, f6i);
1785c0b220cfSDavid Ahern }
1786c0b220cfSDavid Ahern 
178735732d01SWei Wang /* Find cached rt in the hash table inside passed in rt
178835732d01SWei Wang  * Caller has to hold rcu_read_lock()
178935732d01SWei Wang  */
rt6_find_cached_rt(const struct fib6_result * res,const struct in6_addr * daddr,const struct in6_addr * saddr)17907e4b5128SDavid Ahern static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
1791510e2cedSWei Wang 					   const struct in6_addr *daddr,
1792510e2cedSWei Wang 					   const struct in6_addr *saddr)
179335732d01SWei Wang {
1794510e2cedSWei Wang 	const struct in6_addr *src_key = NULL;
179535732d01SWei Wang 	struct rt6_exception_bucket *bucket;
179635732d01SWei Wang 	struct rt6_exception *rt6_ex;
17977e4b5128SDavid Ahern 	struct rt6_info *ret = NULL;
179835732d01SWei Wang 
179935732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES
18007e4b5128SDavid Ahern 	/* fib6i_src.plen != 0 indicates f6i is in subtree
180135732d01SWei Wang 	 * and exception table is indexed by a hash of
18027e4b5128SDavid Ahern 	 * both fib6_dst and fib6_src.
1803510e2cedSWei Wang 	 * However, the src addr used to create the hash
1804510e2cedSWei Wang 	 * might not be exactly the passed in saddr which
1805510e2cedSWei Wang 	 * is a /128 addr from the flow.
1806510e2cedSWei Wang 	 * So we need to use f6i->fib6_src to redo lookup
1807510e2cedSWei Wang 	 * if the passed in saddr does not find anything.
1808510e2cedSWei Wang 	 * (See the logic in ip6_rt_cache_alloc() on how
1809510e2cedSWei Wang 	 * rt->rt6i_src is updated.)
181035732d01SWei Wang 	 */
18117e4b5128SDavid Ahern 	if (res->f6i->fib6_src.plen)
181235732d01SWei Wang 		src_key = saddr;
1813510e2cedSWei Wang find_ex:
181435732d01SWei Wang #endif
1815cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(res->nh, NULL);
181635732d01SWei Wang 	rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
181735732d01SWei Wang 
181835732d01SWei Wang 	if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
18197e4b5128SDavid Ahern 		ret = rt6_ex->rt6i;
182035732d01SWei Wang 
1821510e2cedSWei Wang #ifdef CONFIG_IPV6_SUBTREES
1822510e2cedSWei Wang 	/* Use fib6_src as src_key and redo lookup */
1823510e2cedSWei Wang 	if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) {
1824510e2cedSWei Wang 		src_key = &res->f6i->fib6_src.addr;
1825510e2cedSWei Wang 		goto find_ex;
1826510e2cedSWei Wang 	}
1827510e2cedSWei Wang #endif
1828510e2cedSWei Wang 
18297e4b5128SDavid Ahern 	return ret;
183035732d01SWei Wang }
183135732d01SWei Wang 
183235732d01SWei Wang /* Remove the passed in cached rt from the hash table that contains it */
fib6_nh_remove_exception(const struct fib6_nh * nh,int plen,const struct rt6_info * rt)1833cc5c073aSDavid Ahern static int fib6_nh_remove_exception(const struct fib6_nh *nh, int plen,
1834c0b220cfSDavid Ahern 				    const struct rt6_info *rt)
183535732d01SWei Wang {
1836c0b220cfSDavid Ahern 	const struct in6_addr *src_key = NULL;
183735732d01SWei Wang 	struct rt6_exception_bucket *bucket;
183835732d01SWei Wang 	struct rt6_exception *rt6_ex;
183935732d01SWei Wang 	int err;
184035732d01SWei Wang 
1841cc5c073aSDavid Ahern 	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
184235732d01SWei Wang 		return -ENOENT;
184335732d01SWei Wang 
184435732d01SWei Wang 	spin_lock_bh(&rt6_exception_lock);
1845cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
1846cc5c073aSDavid Ahern 
184735732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES
1848cc5c073aSDavid Ahern 	/* rt6i_src.plen != 0 indicates 'from' is in subtree
1849cc5c073aSDavid Ahern 	 * and exception table is indexed by a hash of
1850cc5c073aSDavid Ahern 	 * both rt6i_dst and rt6i_src.
185135732d01SWei Wang 	 * Otherwise, the exception table is indexed by
185235732d01SWei Wang 	 * a hash of only rt6i_dst.
185335732d01SWei Wang 	 */
1854c0b220cfSDavid Ahern 	if (plen)
185535732d01SWei Wang 		src_key = &rt->rt6i_src.addr;
185635732d01SWei Wang #endif
185735732d01SWei Wang 	rt6_ex = __rt6_find_exception_spinlock(&bucket,
185835732d01SWei Wang 					       &rt->rt6i_dst.addr,
185935732d01SWei Wang 					       src_key);
186035732d01SWei Wang 	if (rt6_ex) {
186135732d01SWei Wang 		rt6_remove_exception(bucket, rt6_ex);
186235732d01SWei Wang 		err = 0;
186335732d01SWei Wang 	} else {
186435732d01SWei Wang 		err = -ENOENT;
186535732d01SWei Wang 	}
186635732d01SWei Wang 
186735732d01SWei Wang 	spin_unlock_bh(&rt6_exception_lock);
186835732d01SWei Wang 	return err;
186935732d01SWei Wang }
187035732d01SWei Wang 
1871e659ba31SDavid Ahern struct fib6_nh_excptn_arg {
1872e659ba31SDavid Ahern 	struct rt6_info	*rt;
1873e659ba31SDavid Ahern 	int		plen;
1874e659ba31SDavid Ahern };
1875e659ba31SDavid Ahern 
rt6_nh_remove_exception_rt(struct fib6_nh * nh,void * _arg)1876e659ba31SDavid Ahern static int rt6_nh_remove_exception_rt(struct fib6_nh *nh, void *_arg)
1877e659ba31SDavid Ahern {
1878e659ba31SDavid Ahern 	struct fib6_nh_excptn_arg *arg = _arg;
1879e659ba31SDavid Ahern 	int err;
1880e659ba31SDavid Ahern 
1881e659ba31SDavid Ahern 	err = fib6_nh_remove_exception(nh, arg->plen, arg->rt);
1882e659ba31SDavid Ahern 	if (err == 0)
1883e659ba31SDavid Ahern 		return 1;
1884e659ba31SDavid Ahern 
1885e659ba31SDavid Ahern 	return 0;
1886e659ba31SDavid Ahern }
1887e659ba31SDavid Ahern 
rt6_remove_exception_rt(struct rt6_info * rt)1888c0b220cfSDavid Ahern static int rt6_remove_exception_rt(struct rt6_info *rt)
1889c0b220cfSDavid Ahern {
1890c0b220cfSDavid Ahern 	struct fib6_info *from;
1891c0b220cfSDavid Ahern 
1892c0b220cfSDavid Ahern 	from = rcu_dereference(rt->from);
1893cc5c073aSDavid Ahern 	if (!from || !(rt->rt6i_flags & RTF_CACHE))
1894c0b220cfSDavid Ahern 		return -EINVAL;
1895c0b220cfSDavid Ahern 
1896e659ba31SDavid Ahern 	if (from->nh) {
1897e659ba31SDavid Ahern 		struct fib6_nh_excptn_arg arg = {
1898e659ba31SDavid Ahern 			.rt = rt,
1899e659ba31SDavid Ahern 			.plen = from->fib6_src.plen
1900e659ba31SDavid Ahern 		};
1901e659ba31SDavid Ahern 		int rc;
1902e659ba31SDavid Ahern 
1903e659ba31SDavid Ahern 		/* rc = 1 means an entry was found */
1904e659ba31SDavid Ahern 		rc = nexthop_for_each_fib6_nh(from->nh,
1905e659ba31SDavid Ahern 					      rt6_nh_remove_exception_rt,
1906e659ba31SDavid Ahern 					      &arg);
1907e659ba31SDavid Ahern 		return rc ? 0 : -ENOENT;
1908e659ba31SDavid Ahern 	}
1909e659ba31SDavid Ahern 
19101cf844c7SDavid Ahern 	return fib6_nh_remove_exception(from->fib6_nh,
1911cc5c073aSDavid Ahern 					from->fib6_src.plen, rt);
1912c0b220cfSDavid Ahern }
1913c0b220cfSDavid Ahern 
191435732d01SWei Wang /* Find rt6_ex which contains the passed in rt cache and
191535732d01SWei Wang  * refresh its stamp
191635732d01SWei Wang  */
fib6_nh_update_exception(const struct fib6_nh * nh,int plen,const struct rt6_info * rt)1917cc5c073aSDavid Ahern static void fib6_nh_update_exception(const struct fib6_nh *nh, int plen,
1918c0b220cfSDavid Ahern 				     const struct rt6_info *rt)
191935732d01SWei Wang {
1920c0b220cfSDavid Ahern 	const struct in6_addr *src_key = NULL;
192135732d01SWei Wang 	struct rt6_exception_bucket *bucket;
192235732d01SWei Wang 	struct rt6_exception *rt6_ex;
1923193f3685SPaolo Abeni 
1924cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(nh, NULL);
192535732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES
1926cc5c073aSDavid Ahern 	/* rt6i_src.plen != 0 indicates 'from' is in subtree
1927cc5c073aSDavid Ahern 	 * and exception table is indexed by a hash of
1928cc5c073aSDavid Ahern 	 * both rt6i_dst and rt6i_src.
192935732d01SWei Wang 	 * Otherwise, the exception table is indexed by
193035732d01SWei Wang 	 * a hash of only rt6i_dst.
193135732d01SWei Wang 	 */
1932c0b220cfSDavid Ahern 	if (plen)
193335732d01SWei Wang 		src_key = &rt->rt6i_src.addr;
193435732d01SWei Wang #endif
1935cc5c073aSDavid Ahern 	rt6_ex = __rt6_find_exception_rcu(&bucket, &rt->rt6i_dst.addr, src_key);
193635732d01SWei Wang 	if (rt6_ex)
193735732d01SWei Wang 		rt6_ex->stamp = jiffies;
1938c0b220cfSDavid Ahern }
193935732d01SWei Wang 
1940e659ba31SDavid Ahern struct fib6_nh_match_arg {
1941e659ba31SDavid Ahern 	const struct net_device *dev;
1942e659ba31SDavid Ahern 	const struct in6_addr	*gw;
1943e659ba31SDavid Ahern 	struct fib6_nh		*match;
1944e659ba31SDavid Ahern };
1945e659ba31SDavid Ahern 
1946e659ba31SDavid Ahern /* determine if fib6_nh has given device and gateway */
fib6_nh_find_match(struct fib6_nh * nh,void * _arg)1947e659ba31SDavid Ahern static int fib6_nh_find_match(struct fib6_nh *nh, void *_arg)
1948e659ba31SDavid Ahern {
1949e659ba31SDavid Ahern 	struct fib6_nh_match_arg *arg = _arg;
1950e659ba31SDavid Ahern 
1951e659ba31SDavid Ahern 	if (arg->dev != nh->fib_nh_dev ||
1952e659ba31SDavid Ahern 	    (arg->gw && !nh->fib_nh_gw_family) ||
1953e659ba31SDavid Ahern 	    (!arg->gw && nh->fib_nh_gw_family) ||
1954e659ba31SDavid Ahern 	    (arg->gw && !ipv6_addr_equal(arg->gw, &nh->fib_nh_gw6)))
1955e659ba31SDavid Ahern 		return 0;
1956e659ba31SDavid Ahern 
1957e659ba31SDavid Ahern 	arg->match = nh;
1958e659ba31SDavid Ahern 
1959e659ba31SDavid Ahern 	/* found a match, break the loop */
1960e659ba31SDavid Ahern 	return 1;
1961e659ba31SDavid Ahern }
1962e659ba31SDavid Ahern 
rt6_update_exception_stamp_rt(struct rt6_info * rt)1963c0b220cfSDavid Ahern static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1964c0b220cfSDavid Ahern {
1965c0b220cfSDavid Ahern 	struct fib6_info *from;
1966e659ba31SDavid Ahern 	struct fib6_nh *fib6_nh;
1967c0b220cfSDavid Ahern 
1968c0b220cfSDavid Ahern 	rcu_read_lock();
1969c0b220cfSDavid Ahern 
1970c0b220cfSDavid Ahern 	from = rcu_dereference(rt->from);
1971c0b220cfSDavid Ahern 	if (!from || !(rt->rt6i_flags & RTF_CACHE))
1972c0b220cfSDavid Ahern 		goto unlock;
1973c0b220cfSDavid Ahern 
1974e659ba31SDavid Ahern 	if (from->nh) {
1975e659ba31SDavid Ahern 		struct fib6_nh_match_arg arg = {
1976e659ba31SDavid Ahern 			.dev = rt->dst.dev,
1977e659ba31SDavid Ahern 			.gw = &rt->rt6i_gateway,
1978e659ba31SDavid Ahern 		};
1979e659ba31SDavid Ahern 
1980e659ba31SDavid Ahern 		nexthop_for_each_fib6_nh(from->nh, fib6_nh_find_match, &arg);
1981e659ba31SDavid Ahern 
1982e659ba31SDavid Ahern 		if (!arg.match)
1983cff6a327SDavid Ahern 			goto unlock;
1984e659ba31SDavid Ahern 		fib6_nh = arg.match;
1985e659ba31SDavid Ahern 	} else {
1986e659ba31SDavid Ahern 		fib6_nh = from->fib6_nh;
1987e659ba31SDavid Ahern 	}
1988e659ba31SDavid Ahern 	fib6_nh_update_exception(fib6_nh, from->fib6_src.plen, rt);
1989193f3685SPaolo Abeni unlock:
199035732d01SWei Wang 	rcu_read_unlock();
199135732d01SWei Wang }
199235732d01SWei Wang 
rt6_mtu_change_route_allowed(struct inet6_dev * idev,struct rt6_info * rt,int mtu)1993e9fa1495SStefano Brivio static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1994e9fa1495SStefano Brivio 					 struct rt6_info *rt, int mtu)
1995e9fa1495SStefano Brivio {
1996e9fa1495SStefano Brivio 	/* If the new MTU is lower than the route PMTU, this new MTU will be the
1997e9fa1495SStefano Brivio 	 * lowest MTU in the path: always allow updating the route PMTU to
1998e9fa1495SStefano Brivio 	 * reflect PMTU decreases.
1999e9fa1495SStefano Brivio 	 *
2000e9fa1495SStefano Brivio 	 * If the new MTU is higher, and the route PMTU is equal to the local
2001e9fa1495SStefano Brivio 	 * MTU, this means the old MTU is the lowest in the path, so allow
2002e9fa1495SStefano Brivio 	 * updating it: if other nodes now have lower MTUs, PMTU discovery will
2003e9fa1495SStefano Brivio 	 * handle this.
2004e9fa1495SStefano Brivio 	 */
2005e9fa1495SStefano Brivio 
2006e9fa1495SStefano Brivio 	if (dst_mtu(&rt->dst) >= mtu)
2007e9fa1495SStefano Brivio 		return true;
2008e9fa1495SStefano Brivio 
2009e9fa1495SStefano Brivio 	if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
2010e9fa1495SStefano Brivio 		return true;
2011e9fa1495SStefano Brivio 
2012e9fa1495SStefano Brivio 	return false;
2013e9fa1495SStefano Brivio }
2014e9fa1495SStefano Brivio 
rt6_exceptions_update_pmtu(struct inet6_dev * idev,const struct fib6_nh * nh,int mtu)2015e9fa1495SStefano Brivio static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
2016cc5c073aSDavid Ahern 				       const struct fib6_nh *nh, int mtu)
2017f5bbe7eeSWei Wang {
2018f5bbe7eeSWei Wang 	struct rt6_exception_bucket *bucket;
2019f5bbe7eeSWei Wang 	struct rt6_exception *rt6_ex;
2020f5bbe7eeSWei Wang 	int i;
2021f5bbe7eeSWei Wang 
2022cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
2023e9fa1495SStefano Brivio 	if (!bucket)
2024e9fa1495SStefano Brivio 		return;
2025e9fa1495SStefano Brivio 
2026f5bbe7eeSWei Wang 	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
2027f5bbe7eeSWei Wang 		hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
2028f5bbe7eeSWei Wang 			struct rt6_info *entry = rt6_ex->rt6i;
2029e9fa1495SStefano Brivio 
2030e9fa1495SStefano Brivio 			/* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
2031d4ead6b3SDavid Ahern 			 * route), the metrics of its rt->from have already
2032f5bbe7eeSWei Wang 			 * been updated.
2033f5bbe7eeSWei Wang 			 */
2034d4ead6b3SDavid Ahern 			if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
2035e9fa1495SStefano Brivio 			    rt6_mtu_change_route_allowed(idev, entry, mtu))
2036d4ead6b3SDavid Ahern 				dst_metric_set(&entry->dst, RTAX_MTU, mtu);
2037f5bbe7eeSWei Wang 		}
2038f5bbe7eeSWei Wang 		bucket++;
2039f5bbe7eeSWei Wang 	}
2040f5bbe7eeSWei Wang }
2041f5bbe7eeSWei Wang 
2042b16cb459SWei Wang #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2043b16cb459SWei Wang 
fib6_nh_exceptions_clean_tohost(const struct fib6_nh * nh,const struct in6_addr * gateway)2044cc5c073aSDavid Ahern static void fib6_nh_exceptions_clean_tohost(const struct fib6_nh *nh,
2045cc5c073aSDavid Ahern 					    const struct in6_addr *gateway)
2046b16cb459SWei Wang {
2047b16cb459SWei Wang 	struct rt6_exception_bucket *bucket;
2048b16cb459SWei Wang 	struct rt6_exception *rt6_ex;
2049b16cb459SWei Wang 	struct hlist_node *tmp;
2050b16cb459SWei Wang 	int i;
2051b16cb459SWei Wang 
2052cc5c073aSDavid Ahern 	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
2053b16cb459SWei Wang 		return;
2054b16cb459SWei Wang 
2055b16cb459SWei Wang 	spin_lock_bh(&rt6_exception_lock);
2056cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
2057b16cb459SWei Wang 	if (bucket) {
2058b16cb459SWei Wang 		for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
2059b16cb459SWei Wang 			hlist_for_each_entry_safe(rt6_ex, tmp,
2060b16cb459SWei Wang 						  &bucket->chain, hlist) {
2061b16cb459SWei Wang 				struct rt6_info *entry = rt6_ex->rt6i;
2062b16cb459SWei Wang 
2063b16cb459SWei Wang 				if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
2064b16cb459SWei Wang 				    RTF_CACHE_GATEWAY &&
2065b16cb459SWei Wang 				    ipv6_addr_equal(gateway,
2066b16cb459SWei Wang 						    &entry->rt6i_gateway)) {
2067b16cb459SWei Wang 					rt6_remove_exception(bucket, rt6_ex);
2068b16cb459SWei Wang 				}
2069b16cb459SWei Wang 			}
2070b16cb459SWei Wang 			bucket++;
2071b16cb459SWei Wang 		}
2072b16cb459SWei Wang 	}
2073b16cb459SWei Wang 
2074b16cb459SWei Wang 	spin_unlock_bh(&rt6_exception_lock);
2075b16cb459SWei Wang }
2076b16cb459SWei Wang 
rt6_age_examine_exception(struct rt6_exception_bucket * bucket,struct rt6_exception * rt6_ex,struct fib6_gc_args * gc_args,unsigned long now)2077c757faa8SWei Wang static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
2078c757faa8SWei Wang 				      struct rt6_exception *rt6_ex,
2079c757faa8SWei Wang 				      struct fib6_gc_args *gc_args,
2080c757faa8SWei Wang 				      unsigned long now)
2081c757faa8SWei Wang {
2082c757faa8SWei Wang 	struct rt6_info *rt = rt6_ex->rt6i;
2083c757faa8SWei Wang 
20841859bac0SPaolo Abeni 	/* we are pruning and obsoleting aged-out and non gateway exceptions
20851859bac0SPaolo Abeni 	 * even if others have still references to them, so that on next
20861859bac0SPaolo Abeni 	 * dst_check() such references can be dropped.
20871859bac0SPaolo Abeni 	 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
20881859bac0SPaolo Abeni 	 * expired, independently from their aging, as per RFC 8201 section 4
20891859bac0SPaolo Abeni 	 */
209031afeb42SWei Wang 	if (!(rt->rt6i_flags & RTF_EXPIRES)) {
209131afeb42SWei Wang 		if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
2092c757faa8SWei Wang 			RT6_TRACE("aging clone %p\n", rt);
2093c757faa8SWei Wang 			rt6_remove_exception(bucket, rt6_ex);
2094c757faa8SWei Wang 			return;
209531afeb42SWei Wang 		}
209631afeb42SWei Wang 	} else if (time_after(jiffies, rt->dst.expires)) {
209731afeb42SWei Wang 		RT6_TRACE("purging expired route %p\n", rt);
209831afeb42SWei Wang 		rt6_remove_exception(bucket, rt6_ex);
209931afeb42SWei Wang 		return;
210031afeb42SWei Wang 	}
210131afeb42SWei Wang 
210231afeb42SWei Wang 	if (rt->rt6i_flags & RTF_GATEWAY) {
2103c757faa8SWei Wang 		struct neighbour *neigh;
2104c757faa8SWei Wang 
21051bfa26ffSEric Dumazet 		neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
21061bfa26ffSEric Dumazet 
2107b7a320c3SXu Jia 		if (!(neigh && (neigh->flags & NTF_ROUTER))) {
2108c757faa8SWei Wang 			RT6_TRACE("purging route %p via non-router but gateway\n",
2109c757faa8SWei Wang 				  rt);
2110c757faa8SWei Wang 			rt6_remove_exception(bucket, rt6_ex);
2111c757faa8SWei Wang 			return;
2112c757faa8SWei Wang 		}
2113c757faa8SWei Wang 	}
211431afeb42SWei Wang 
2115c757faa8SWei Wang 	gc_args->more++;
2116c757faa8SWei Wang }
2117c757faa8SWei Wang 
fib6_nh_age_exceptions(const struct fib6_nh * nh,struct fib6_gc_args * gc_args,unsigned long now)2118cc5c073aSDavid Ahern static void fib6_nh_age_exceptions(const struct fib6_nh *nh,
2119c757faa8SWei Wang 				   struct fib6_gc_args *gc_args,
2120c757faa8SWei Wang 				   unsigned long now)
2121c757faa8SWei Wang {
2122c757faa8SWei Wang 	struct rt6_exception_bucket *bucket;
2123c757faa8SWei Wang 	struct rt6_exception *rt6_ex;
2124c757faa8SWei Wang 	struct hlist_node *tmp;
2125c757faa8SWei Wang 	int i;
2126c757faa8SWei Wang 
2127cc5c073aSDavid Ahern 	if (!rcu_access_pointer(nh->rt6i_exception_bucket))
2128c757faa8SWei Wang 		return;
2129c757faa8SWei Wang 
21301bfa26ffSEric Dumazet 	rcu_read_lock_bh();
21311bfa26ffSEric Dumazet 	spin_lock(&rt6_exception_lock);
2132cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
2133c757faa8SWei Wang 	if (bucket) {
2134c757faa8SWei Wang 		for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
2135c757faa8SWei Wang 			hlist_for_each_entry_safe(rt6_ex, tmp,
2136c757faa8SWei Wang 						  &bucket->chain, hlist) {
2137c757faa8SWei Wang 				rt6_age_examine_exception(bucket, rt6_ex,
2138c757faa8SWei Wang 							  gc_args, now);
2139c757faa8SWei Wang 			}
2140c757faa8SWei Wang 			bucket++;
2141c757faa8SWei Wang 		}
2142c757faa8SWei Wang 	}
21431bfa26ffSEric Dumazet 	spin_unlock(&rt6_exception_lock);
21441bfa26ffSEric Dumazet 	rcu_read_unlock_bh();
2145c757faa8SWei Wang }
2146c757faa8SWei Wang 
2147e659ba31SDavid Ahern struct fib6_nh_age_excptn_arg {
2148e659ba31SDavid Ahern 	struct fib6_gc_args	*gc_args;
2149e659ba31SDavid Ahern 	unsigned long		now;
2150e659ba31SDavid Ahern };
2151e659ba31SDavid Ahern 
rt6_nh_age_exceptions(struct fib6_nh * nh,void * _arg)2152e659ba31SDavid Ahern static int rt6_nh_age_exceptions(struct fib6_nh *nh, void *_arg)
2153e659ba31SDavid Ahern {
2154e659ba31SDavid Ahern 	struct fib6_nh_age_excptn_arg *arg = _arg;
2155e659ba31SDavid Ahern 
2156e659ba31SDavid Ahern 	fib6_nh_age_exceptions(nh, arg->gc_args, arg->now);
2157e659ba31SDavid Ahern 	return 0;
2158e659ba31SDavid Ahern }
2159e659ba31SDavid Ahern 
rt6_age_exceptions(struct fib6_info * f6i,struct fib6_gc_args * gc_args,unsigned long now)2160cc5c073aSDavid Ahern void rt6_age_exceptions(struct fib6_info *f6i,
2161c0b220cfSDavid Ahern 			struct fib6_gc_args *gc_args,
2162c0b220cfSDavid Ahern 			unsigned long now)
2163c0b220cfSDavid Ahern {
2164e659ba31SDavid Ahern 	if (f6i->nh) {
2165e659ba31SDavid Ahern 		struct fib6_nh_age_excptn_arg arg = {
2166e659ba31SDavid Ahern 			.gc_args = gc_args,
2167e659ba31SDavid Ahern 			.now = now
2168e659ba31SDavid Ahern 		};
2169e659ba31SDavid Ahern 
2170e659ba31SDavid Ahern 		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_age_exceptions,
2171e659ba31SDavid Ahern 					 &arg);
2172e659ba31SDavid Ahern 	} else {
21731cf844c7SDavid Ahern 		fib6_nh_age_exceptions(f6i->fib6_nh, gc_args, now);
2174c0b220cfSDavid Ahern 	}
2175e659ba31SDavid Ahern }
2176c0b220cfSDavid Ahern 
21771d053da9SDavid Ahern /* must be called with rcu lock held */
fib6_table_lookup(struct net * net,struct fib6_table * table,int oif,struct flowi6 * fl6,struct fib6_result * res,int strict)2178effda4ddSDavid Ahern int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
2179effda4ddSDavid Ahern 		      struct flowi6 *fl6, struct fib6_result *res, int strict)
21801da177e4SLinus Torvalds {
2181367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
21821da177e4SLinus Torvalds 
21836454743bSDavid Ahern 	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
2184367efcb9SMartin KaFai Lau 	saved_fn = fn;
21851da177e4SLinus Torvalds 
2186a3c00e46SMartin KaFai Lau redo_rt6_select:
2187effda4ddSDavid Ahern 	rt6_select(net, fn, oif, res, strict);
2188effda4ddSDavid Ahern 	if (res->f6i == net->ipv6.fib6_null_entry) {
2189a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
2190a3c00e46SMartin KaFai Lau 		if (fn)
2191a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
2192367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
2193367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
2194367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
2195367efcb9SMartin KaFai Lau 			fn = saved_fn;
2196367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
2197367efcb9SMartin KaFai Lau 		}
2198a3c00e46SMartin KaFai Lau 	}
2199a3c00e46SMartin KaFai Lau 
2200effda4ddSDavid Ahern 	trace_fib6_table_lookup(net, res, table, fl6);
2201d52d3997SMartin KaFai Lau 
2202effda4ddSDavid Ahern 	return 0;
22031d053da9SDavid Ahern }
22041d053da9SDavid Ahern 
ip6_pol_route(struct net * net,struct fib6_table * table,int oif,struct flowi6 * fl6,const struct sk_buff * skb,int flags)22051d053da9SDavid Ahern struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
22061d053da9SDavid Ahern 			       int oif, struct flowi6 *fl6,
22071d053da9SDavid Ahern 			       const struct sk_buff *skb, int flags)
22081d053da9SDavid Ahern {
2209b1d40991SDavid Ahern 	struct fib6_result res = {};
22100e09edccSWei Wang 	struct rt6_info *rt = NULL;
22111d053da9SDavid Ahern 	int strict = 0;
22121d053da9SDavid Ahern 
22130e09edccSWei Wang 	WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) &&
22140e09edccSWei Wang 		     !rcu_read_lock_held());
22150e09edccSWei Wang 
22161d053da9SDavid Ahern 	strict |= flags & RT6_LOOKUP_F_IFACE;
22171d053da9SDavid Ahern 	strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
22181d053da9SDavid Ahern 	if (net->ipv6.devconf_all->forwarding == 0)
22191d053da9SDavid Ahern 		strict |= RT6_LOOKUP_F_REACHABLE;
22201d053da9SDavid Ahern 
22211d053da9SDavid Ahern 	rcu_read_lock();
22221d053da9SDavid Ahern 
2223effda4ddSDavid Ahern 	fib6_table_lookup(net, table, oif, fl6, &res, strict);
22240e09edccSWei Wang 	if (res.f6i == net->ipv6.fib6_null_entry)
22250e09edccSWei Wang 		goto out;
222623fb93a4SDavid Ahern 
2227b1d40991SDavid Ahern 	fib6_select_path(net, &res, fl6, oif, false, skb, strict);
2228d83009d4SDavid Ahern 
222923fb93a4SDavid Ahern 	/*Search through exception table */
22307e4b5128SDavid Ahern 	rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
223123fb93a4SDavid Ahern 	if (rt) {
22320e09edccSWei Wang 		goto out;
22333da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
2234b1d40991SDavid Ahern 			    !res.nh->fib_nh_gw_family)) {
22353da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
22363da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
22373da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
22383da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
22393da59bd9SMartin KaFai Lau 		 */
22400e09edccSWei Wang 		rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
22413da59bd9SMartin KaFai Lau 
22420e09edccSWei Wang 		if (rt) {
22430e09edccSWei Wang 			/* 1 refcnt is taken during ip6_rt_cache_alloc().
22440e09edccSWei Wang 			 * As rt6_uncached_list_add() does not consume refcnt,
22450e09edccSWei Wang 			 * this refcnt is always returned to the caller even
22460e09edccSWei Wang 			 * if caller sets RT6_LOOKUP_F_DST_NOREF flag.
22470e09edccSWei Wang 			 */
22480e09edccSWei Wang 			rt6_uncached_list_add(rt);
22494d85cd0cSDavid Ahern 			rcu_read_unlock();
22503da59bd9SMartin KaFai Lau 
22510e09edccSWei Wang 			return rt;
22521cfb71eeSWei Wang 		}
2253d52d3997SMartin KaFai Lau 	} else {
2254d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
2255951f788aSEric Dumazet 		local_bh_disable();
22560e09edccSWei Wang 		rt = rt6_get_pcpu_route(&res);
2257d52d3997SMartin KaFai Lau 
22580e09edccSWei Wang 		if (!rt)
22590e09edccSWei Wang 			rt = rt6_make_pcpu_route(net, &res);
226093531c67SDavid Ahern 
2261951f788aSEric Dumazet 		local_bh_enable();
22620e09edccSWei Wang 	}
22630e09edccSWei Wang out:
22640e09edccSWei Wang 	if (!rt)
22650e09edccSWei Wang 		rt = net->ipv6.ip6_null_entry;
22660e09edccSWei Wang 	if (!(flags & RT6_LOOKUP_F_DST_NOREF))
22670e09edccSWei Wang 		ip6_hold_safe(net, &rt);
2268951f788aSEric Dumazet 	rcu_read_unlock();
2269d4bea421SDavid Ahern 
22700e09edccSWei Wang 	return rt;
2271c71099acSThomas Graf }
22729ff74384SDavid Ahern EXPORT_SYMBOL_GPL(ip6_pol_route);
2273c71099acSThomas Graf 
ip6_pol_route_input(struct net * net,struct fib6_table * table,struct flowi6 * fl6,const struct sk_buff * skb,int flags)227455cced4fSBrian Vazquez INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_input(struct net *net,
2275b75cc8f9SDavid Ahern 					    struct fib6_table *table,
2276b75cc8f9SDavid Ahern 					    struct flowi6 *fl6,
2277b75cc8f9SDavid Ahern 					    const struct sk_buff *skb,
2278b75cc8f9SDavid Ahern 					    int flags)
22794acad72dSPavel Emelyanov {
2280b75cc8f9SDavid Ahern 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
22814acad72dSPavel Emelyanov }
22824acad72dSPavel Emelyanov 
ip6_route_input_lookup(struct net * net,struct net_device * dev,struct flowi6 * fl6,const struct sk_buff * skb,int flags)2283d409b847SMahesh Bandewar struct dst_entry *ip6_route_input_lookup(struct net *net,
228472331bc0SShmulik Ladkani 					 struct net_device *dev,
2285b75cc8f9SDavid Ahern 					 struct flowi6 *fl6,
2286b75cc8f9SDavid Ahern 					 const struct sk_buff *skb,
2287b75cc8f9SDavid Ahern 					 int flags)
228872331bc0SShmulik Ladkani {
228972331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
229072331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
229172331bc0SShmulik Ladkani 
2292b75cc8f9SDavid Ahern 	return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
229372331bc0SShmulik Ladkani }
2294d409b847SMahesh Bandewar EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
229572331bc0SShmulik Ladkani 
ip6_multipath_l3_keys(const struct sk_buff * skb,struct flow_keys * keys,struct flow_keys * flkeys)229623aebdacSJakub Sitnicki static void ip6_multipath_l3_keys(const struct sk_buff *skb,
22975e5d6fedSRoopa Prabhu 				  struct flow_keys *keys,
22985e5d6fedSRoopa Prabhu 				  struct flow_keys *flkeys)
229923aebdacSJakub Sitnicki {
230023aebdacSJakub Sitnicki 	const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
230123aebdacSJakub Sitnicki 	const struct ipv6hdr *key_iph = outer_iph;
23025e5d6fedSRoopa Prabhu 	struct flow_keys *_flkeys = flkeys;
230323aebdacSJakub Sitnicki 	const struct ipv6hdr *inner_iph;
230423aebdacSJakub Sitnicki 	const struct icmp6hdr *icmph;
230523aebdacSJakub Sitnicki 	struct ipv6hdr _inner_iph;
2306cea67a2dSEric Dumazet 	struct icmp6hdr _icmph;
230723aebdacSJakub Sitnicki 
230823aebdacSJakub Sitnicki 	if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
230923aebdacSJakub Sitnicki 		goto out;
231023aebdacSJakub Sitnicki 
2311cea67a2dSEric Dumazet 	icmph = skb_header_pointer(skb, skb_transport_offset(skb),
2312cea67a2dSEric Dumazet 				   sizeof(_icmph), &_icmph);
2313cea67a2dSEric Dumazet 	if (!icmph)
2314cea67a2dSEric Dumazet 		goto out;
2315cea67a2dSEric Dumazet 
231654074f1dSMatteo Croce 	if (!icmpv6_is_err(icmph->icmp6_type))
231723aebdacSJakub Sitnicki 		goto out;
231823aebdacSJakub Sitnicki 
231923aebdacSJakub Sitnicki 	inner_iph = skb_header_pointer(skb,
232023aebdacSJakub Sitnicki 				       skb_transport_offset(skb) + sizeof(*icmph),
232123aebdacSJakub Sitnicki 				       sizeof(_inner_iph), &_inner_iph);
232223aebdacSJakub Sitnicki 	if (!inner_iph)
232323aebdacSJakub Sitnicki 		goto out;
232423aebdacSJakub Sitnicki 
232523aebdacSJakub Sitnicki 	key_iph = inner_iph;
23265e5d6fedSRoopa Prabhu 	_flkeys = NULL;
232723aebdacSJakub Sitnicki out:
23285e5d6fedSRoopa Prabhu 	if (_flkeys) {
23295e5d6fedSRoopa Prabhu 		keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
23305e5d6fedSRoopa Prabhu 		keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
23315e5d6fedSRoopa Prabhu 		keys->tags.flow_label = _flkeys->tags.flow_label;
23325e5d6fedSRoopa Prabhu 		keys->basic.ip_proto = _flkeys->basic.ip_proto;
23335e5d6fedSRoopa Prabhu 	} else {
233423aebdacSJakub Sitnicki 		keys->addrs.v6addrs.src = key_iph->saddr;
233523aebdacSJakub Sitnicki 		keys->addrs.v6addrs.dst = key_iph->daddr;
2336fa1be7e0SMichal Kubecek 		keys->tags.flow_label = ip6_flowlabel(key_iph);
233723aebdacSJakub Sitnicki 		keys->basic.ip_proto = key_iph->nexthdr;
233823aebdacSJakub Sitnicki 	}
23395e5d6fedSRoopa Prabhu }
234023aebdacSJakub Sitnicki 
rt6_multipath_custom_hash_outer(const struct net * net,const struct sk_buff * skb,bool * p_has_inner)234173c2c5cbSIdo Schimmel static u32 rt6_multipath_custom_hash_outer(const struct net *net,
234273c2c5cbSIdo Schimmel 					   const struct sk_buff *skb,
234373c2c5cbSIdo Schimmel 					   bool *p_has_inner)
234473c2c5cbSIdo Schimmel {
234573c2c5cbSIdo Schimmel 	u32 hash_fields = ip6_multipath_hash_fields(net);
234673c2c5cbSIdo Schimmel 	struct flow_keys keys, hash_keys;
234773c2c5cbSIdo Schimmel 
234873c2c5cbSIdo Schimmel 	if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK))
234973c2c5cbSIdo Schimmel 		return 0;
235073c2c5cbSIdo Schimmel 
235173c2c5cbSIdo Schimmel 	memset(&hash_keys, 0, sizeof(hash_keys));
235273c2c5cbSIdo Schimmel 	skb_flow_dissect_flow_keys(skb, &keys, FLOW_DISSECTOR_F_STOP_AT_ENCAP);
235373c2c5cbSIdo Schimmel 
235473c2c5cbSIdo Schimmel 	hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
235573c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP)
235673c2c5cbSIdo Schimmel 		hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src;
235773c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP)
235873c2c5cbSIdo Schimmel 		hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst;
235973c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)
236073c2c5cbSIdo Schimmel 		hash_keys.basic.ip_proto = keys.basic.ip_proto;
236173c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL)
236273c2c5cbSIdo Schimmel 		hash_keys.tags.flow_label = keys.tags.flow_label;
236373c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)
236473c2c5cbSIdo Schimmel 		hash_keys.ports.src = keys.ports.src;
236573c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
236673c2c5cbSIdo Schimmel 		hash_keys.ports.dst = keys.ports.dst;
236773c2c5cbSIdo Schimmel 
236873c2c5cbSIdo Schimmel 	*p_has_inner = !!(keys.control.flags & FLOW_DIS_ENCAPSULATION);
236973c2c5cbSIdo Schimmel 	return flow_hash_from_keys(&hash_keys);
237073c2c5cbSIdo Schimmel }
237173c2c5cbSIdo Schimmel 
rt6_multipath_custom_hash_inner(const struct net * net,const struct sk_buff * skb,bool has_inner)237273c2c5cbSIdo Schimmel static u32 rt6_multipath_custom_hash_inner(const struct net *net,
237373c2c5cbSIdo Schimmel 					   const struct sk_buff *skb,
237473c2c5cbSIdo Schimmel 					   bool has_inner)
237573c2c5cbSIdo Schimmel {
237673c2c5cbSIdo Schimmel 	u32 hash_fields = ip6_multipath_hash_fields(net);
237773c2c5cbSIdo Schimmel 	struct flow_keys keys, hash_keys;
237873c2c5cbSIdo Schimmel 
237973c2c5cbSIdo Schimmel 	/* We assume the packet carries an encapsulation, but if none was
238073c2c5cbSIdo Schimmel 	 * encountered during dissection of the outer flow, then there is no
238173c2c5cbSIdo Schimmel 	 * point in calling the flow dissector again.
238273c2c5cbSIdo Schimmel 	 */
238373c2c5cbSIdo Schimmel 	if (!has_inner)
238473c2c5cbSIdo Schimmel 		return 0;
238573c2c5cbSIdo Schimmel 
238673c2c5cbSIdo Schimmel 	if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_MASK))
238773c2c5cbSIdo Schimmel 		return 0;
238873c2c5cbSIdo Schimmel 
238973c2c5cbSIdo Schimmel 	memset(&hash_keys, 0, sizeof(hash_keys));
239073c2c5cbSIdo Schimmel 	skb_flow_dissect_flow_keys(skb, &keys, 0);
239173c2c5cbSIdo Schimmel 
239273c2c5cbSIdo Schimmel 	if (!(keys.control.flags & FLOW_DIS_ENCAPSULATION))
239373c2c5cbSIdo Schimmel 		return 0;
239473c2c5cbSIdo Schimmel 
239573c2c5cbSIdo Schimmel 	if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
239673c2c5cbSIdo Schimmel 		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
239773c2c5cbSIdo Schimmel 		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP)
239873c2c5cbSIdo Schimmel 			hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
239973c2c5cbSIdo Schimmel 		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP)
240073c2c5cbSIdo Schimmel 			hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
240173c2c5cbSIdo Schimmel 	} else if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
240273c2c5cbSIdo Schimmel 		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
240373c2c5cbSIdo Schimmel 		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP)
240473c2c5cbSIdo Schimmel 			hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src;
240573c2c5cbSIdo Schimmel 		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP)
240673c2c5cbSIdo Schimmel 			hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst;
240773c2c5cbSIdo Schimmel 		if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_FLOWLABEL)
240873c2c5cbSIdo Schimmel 			hash_keys.tags.flow_label = keys.tags.flow_label;
240973c2c5cbSIdo Schimmel 	}
241073c2c5cbSIdo Schimmel 
241173c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_IP_PROTO)
241273c2c5cbSIdo Schimmel 		hash_keys.basic.ip_proto = keys.basic.ip_proto;
241373c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_PORT)
241473c2c5cbSIdo Schimmel 		hash_keys.ports.src = keys.ports.src;
241573c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_PORT)
241673c2c5cbSIdo Schimmel 		hash_keys.ports.dst = keys.ports.dst;
241773c2c5cbSIdo Schimmel 
241873c2c5cbSIdo Schimmel 	return flow_hash_from_keys(&hash_keys);
241973c2c5cbSIdo Schimmel }
242073c2c5cbSIdo Schimmel 
rt6_multipath_custom_hash_skb(const struct net * net,const struct sk_buff * skb)242173c2c5cbSIdo Schimmel static u32 rt6_multipath_custom_hash_skb(const struct net *net,
242273c2c5cbSIdo Schimmel 					 const struct sk_buff *skb)
242373c2c5cbSIdo Schimmel {
242473c2c5cbSIdo Schimmel 	u32 mhash, mhash_inner;
242573c2c5cbSIdo Schimmel 	bool has_inner = true;
242673c2c5cbSIdo Schimmel 
242773c2c5cbSIdo Schimmel 	mhash = rt6_multipath_custom_hash_outer(net, skb, &has_inner);
242873c2c5cbSIdo Schimmel 	mhash_inner = rt6_multipath_custom_hash_inner(net, skb, has_inner);
242973c2c5cbSIdo Schimmel 
243073c2c5cbSIdo Schimmel 	return jhash_2words(mhash, mhash_inner, 0);
243173c2c5cbSIdo Schimmel }
243273c2c5cbSIdo Schimmel 
rt6_multipath_custom_hash_fl6(const struct net * net,const struct flowi6 * fl6)243373c2c5cbSIdo Schimmel static u32 rt6_multipath_custom_hash_fl6(const struct net *net,
243473c2c5cbSIdo Schimmel 					 const struct flowi6 *fl6)
243573c2c5cbSIdo Schimmel {
243673c2c5cbSIdo Schimmel 	u32 hash_fields = ip6_multipath_hash_fields(net);
243773c2c5cbSIdo Schimmel 	struct flow_keys hash_keys;
243873c2c5cbSIdo Schimmel 
243973c2c5cbSIdo Schimmel 	if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK))
244073c2c5cbSIdo Schimmel 		return 0;
244173c2c5cbSIdo Schimmel 
244273c2c5cbSIdo Schimmel 	memset(&hash_keys, 0, sizeof(hash_keys));
244373c2c5cbSIdo Schimmel 	hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
244473c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP)
244573c2c5cbSIdo Schimmel 		hash_keys.addrs.v6addrs.src = fl6->saddr;
244673c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP)
244773c2c5cbSIdo Schimmel 		hash_keys.addrs.v6addrs.dst = fl6->daddr;
244873c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)
244973c2c5cbSIdo Schimmel 		hash_keys.basic.ip_proto = fl6->flowi6_proto;
245073c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL)
245173c2c5cbSIdo Schimmel 		hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
245273c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)
245373c2c5cbSIdo Schimmel 		hash_keys.ports.src = fl6->fl6_sport;
245473c2c5cbSIdo Schimmel 	if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
245573c2c5cbSIdo Schimmel 		hash_keys.ports.dst = fl6->fl6_dport;
245673c2c5cbSIdo Schimmel 
245773c2c5cbSIdo Schimmel 	return flow_hash_from_keys(&hash_keys);
245873c2c5cbSIdo Schimmel }
245973c2c5cbSIdo Schimmel 
246023aebdacSJakub Sitnicki /* if skb is set it will be used and fl6 can be NULL */
rt6_multipath_hash(const struct net * net,const struct flowi6 * fl6,const struct sk_buff * skb,struct flow_keys * flkeys)2461b4bac172SDavid Ahern u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
2462b4bac172SDavid Ahern 		       const struct sk_buff *skb, struct flow_keys *flkeys)
246323aebdacSJakub Sitnicki {
246423aebdacSJakub Sitnicki 	struct flow_keys hash_keys;
2465b95b6e07SIdo Schimmel 	u32 mhash = 0;
246623aebdacSJakub Sitnicki 
2467bbfa047aSDavid S. Miller 	switch (ip6_multipath_hash_policy(net)) {
2468b4bac172SDavid Ahern 	case 0:
24696f74b6c2SDavid Ahern 		memset(&hash_keys, 0, sizeof(hash_keys));
24706f74b6c2SDavid Ahern 		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
24719a2a537aSDavid Ahern 		if (skb) {
24725e5d6fedSRoopa Prabhu 			ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
24739a2a537aSDavid Ahern 		} else {
24749a2a537aSDavid Ahern 			hash_keys.addrs.v6addrs.src = fl6->saddr;
24759a2a537aSDavid Ahern 			hash_keys.addrs.v6addrs.dst = fl6->daddr;
2476fa1be7e0SMichal Kubecek 			hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
24779a2a537aSDavid Ahern 			hash_keys.basic.ip_proto = fl6->flowi6_proto;
247823aebdacSJakub Sitnicki 		}
2479b95b6e07SIdo Schimmel 		mhash = flow_hash_from_keys(&hash_keys);
2480b4bac172SDavid Ahern 		break;
2481b4bac172SDavid Ahern 	case 1:
2482b4bac172SDavid Ahern 		if (skb) {
2483b4bac172SDavid Ahern 			unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2484b4bac172SDavid Ahern 			struct flow_keys keys;
2485b4bac172SDavid Ahern 
2486b4bac172SDavid Ahern 			/* short-circuit if we already have L4 hash present */
2487b4bac172SDavid Ahern 			if (skb->l4_hash)
2488b4bac172SDavid Ahern 				return skb_get_hash_raw(skb) >> 1;
2489b4bac172SDavid Ahern 
2490b4bac172SDavid Ahern 			memset(&hash_keys, 0, sizeof(hash_keys));
2491b4bac172SDavid Ahern 
2492b4bac172SDavid Ahern 			if (!flkeys) {
2493b4bac172SDavid Ahern 				skb_flow_dissect_flow_keys(skb, &keys, flag);
2494b4bac172SDavid Ahern 				flkeys = &keys;
2495b4bac172SDavid Ahern 			}
2496b4bac172SDavid Ahern 			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2497b4bac172SDavid Ahern 			hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2498b4bac172SDavid Ahern 			hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2499b4bac172SDavid Ahern 			hash_keys.ports.src = flkeys->ports.src;
2500b4bac172SDavid Ahern 			hash_keys.ports.dst = flkeys->ports.dst;
2501b4bac172SDavid Ahern 			hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2502b4bac172SDavid Ahern 		} else {
2503b4bac172SDavid Ahern 			memset(&hash_keys, 0, sizeof(hash_keys));
2504b4bac172SDavid Ahern 			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2505b4bac172SDavid Ahern 			hash_keys.addrs.v6addrs.src = fl6->saddr;
2506b4bac172SDavid Ahern 			hash_keys.addrs.v6addrs.dst = fl6->daddr;
2507b4bac172SDavid Ahern 			hash_keys.ports.src = fl6->fl6_sport;
2508b4bac172SDavid Ahern 			hash_keys.ports.dst = fl6->fl6_dport;
2509b4bac172SDavid Ahern 			hash_keys.basic.ip_proto = fl6->flowi6_proto;
2510b4bac172SDavid Ahern 		}
2511b95b6e07SIdo Schimmel 		mhash = flow_hash_from_keys(&hash_keys);
2512b4bac172SDavid Ahern 		break;
2513d8f74f09SStephen Suryaputra 	case 2:
2514d8f74f09SStephen Suryaputra 		memset(&hash_keys, 0, sizeof(hash_keys));
2515d8f74f09SStephen Suryaputra 		hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2516d8f74f09SStephen Suryaputra 		if (skb) {
2517d8f74f09SStephen Suryaputra 			struct flow_keys keys;
2518d8f74f09SStephen Suryaputra 
2519d8f74f09SStephen Suryaputra 			if (!flkeys) {
2520d8f74f09SStephen Suryaputra 				skb_flow_dissect_flow_keys(skb, &keys, 0);
2521d8f74f09SStephen Suryaputra 				flkeys = &keys;
2522d8f74f09SStephen Suryaputra 			}
2523d8f74f09SStephen Suryaputra 
2524d8f74f09SStephen Suryaputra 			/* Inner can be v4 or v6 */
2525d8f74f09SStephen Suryaputra 			if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
2526d8f74f09SStephen Suryaputra 				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
2527d8f74f09SStephen Suryaputra 				hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;
2528d8f74f09SStephen Suryaputra 				hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;
2529d8f74f09SStephen Suryaputra 			} else if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
2530d8f74f09SStephen Suryaputra 				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2531d8f74f09SStephen Suryaputra 				hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2532d8f74f09SStephen Suryaputra 				hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2533d8f74f09SStephen Suryaputra 				hash_keys.tags.flow_label = flkeys->tags.flow_label;
2534d8f74f09SStephen Suryaputra 				hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2535d8f74f09SStephen Suryaputra 			} else {
2536d8f74f09SStephen Suryaputra 				/* Same as case 0 */
2537d8f74f09SStephen Suryaputra 				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2538d8f74f09SStephen Suryaputra 				ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2539d8f74f09SStephen Suryaputra 			}
2540d8f74f09SStephen Suryaputra 		} else {
2541d8f74f09SStephen Suryaputra 			/* Same as case 0 */
2542d8f74f09SStephen Suryaputra 			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2543d8f74f09SStephen Suryaputra 			hash_keys.addrs.v6addrs.src = fl6->saddr;
2544d8f74f09SStephen Suryaputra 			hash_keys.addrs.v6addrs.dst = fl6->daddr;
2545d8f74f09SStephen Suryaputra 			hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
2546d8f74f09SStephen Suryaputra 			hash_keys.basic.ip_proto = fl6->flowi6_proto;
2547d8f74f09SStephen Suryaputra 		}
2548b95b6e07SIdo Schimmel 		mhash = flow_hash_from_keys(&hash_keys);
2549d8f74f09SStephen Suryaputra 		break;
255073c2c5cbSIdo Schimmel 	case 3:
255173c2c5cbSIdo Schimmel 		if (skb)
255273c2c5cbSIdo Schimmel 			mhash = rt6_multipath_custom_hash_skb(net, skb);
255373c2c5cbSIdo Schimmel 		else
255473c2c5cbSIdo Schimmel 			mhash = rt6_multipath_custom_hash_fl6(net, fl6);
255573c2c5cbSIdo Schimmel 		break;
2556b4bac172SDavid Ahern 	}
255723aebdacSJakub Sitnicki 
25589a2a537aSDavid Ahern 	return mhash >> 1;
255923aebdacSJakub Sitnicki }
256023aebdacSJakub Sitnicki 
256167f415ddSWei Wang /* Called with rcu held */
ip6_route_input(struct sk_buff * skb)2562c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
2563c71099acSThomas Graf {
2564b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
2565c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
256667f415ddSWei Wang 	int flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_DST_NOREF;
2567904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
25684c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
2569e0d56fddSDavid Ahern 		.flowi6_iif = skb->dev->ifindex,
25704c9483b2SDavid S. Miller 		.daddr = iph->daddr,
25714c9483b2SDavid S. Miller 		.saddr = iph->saddr,
25726502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
25734c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
25744c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
2575c71099acSThomas Graf 	};
25765e5d6fedSRoopa Prabhu 	struct flow_keys *flkeys = NULL, _flkeys;
2577adaa70bbSThomas Graf 
2578904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
257946fa062aSJiri Benc 	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
2580904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
25815e5d6fedSRoopa Prabhu 
25825e5d6fedSRoopa Prabhu 	if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
25835e5d6fedSRoopa Prabhu 		flkeys = &_flkeys;
25845e5d6fedSRoopa Prabhu 
258523aebdacSJakub Sitnicki 	if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
2586b4bac172SDavid Ahern 		fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
258706e9d040SJiri Benc 	skb_dst_drop(skb);
258867f415ddSWei Wang 	skb_dst_set_noref(skb, ip6_route_input_lookup(net, skb->dev,
258967f415ddSWei Wang 						      &fl6, skb, flags));
2590c71099acSThomas Graf }
2591c71099acSThomas Graf 
ip6_pol_route_output(struct net * net,struct fib6_table * table,struct flowi6 * fl6,const struct sk_buff * skb,int flags)259255cced4fSBrian Vazquez INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_output(struct net *net,
2593b75cc8f9SDavid Ahern 					     struct fib6_table *table,
2594b75cc8f9SDavid Ahern 					     struct flowi6 *fl6,
2595b75cc8f9SDavid Ahern 					     const struct sk_buff *skb,
2596b75cc8f9SDavid Ahern 					     int flags)
2597c71099acSThomas Graf {
2598b75cc8f9SDavid Ahern 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
2599c71099acSThomas Graf }
2600c71099acSThomas Graf 
ip6_route_output_flags_noref(struct net * net,const struct sock * sk,struct flowi6 * fl6,int flags)260190317bcdSGuillaume Nault static struct dst_entry *ip6_route_output_flags_noref(struct net *net,
26027d9e5f42SWei Wang 						      const struct sock *sk,
260390317bcdSGuillaume Nault 						      struct flowi6 *fl6,
260490317bcdSGuillaume Nault 						      int flags)
2605c71099acSThomas Graf {
2606d46a9d67SDavid Ahern 	bool any_src;
2607c71099acSThomas Graf 
26083ede0bbcSRobert Shearman 	if (ipv6_addr_type(&fl6->daddr) &
26093ede0bbcSRobert Shearman 	    (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
26104c1feac5SDavid Ahern 		struct dst_entry *dst;
26114c1feac5SDavid Ahern 
26127d9e5f42SWei Wang 		/* This function does not take refcnt on the dst */
26134c1feac5SDavid Ahern 		dst = l3mdev_link_scope_lookup(net, fl6);
2614ca254490SDavid Ahern 		if (dst)
2615ca254490SDavid Ahern 			return dst;
26164c1feac5SDavid Ahern 	}
2617ca254490SDavid Ahern 
26181fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
26194dc27d1cSDavid McCullough 
26207d9e5f42SWei Wang 	flags |= RT6_LOOKUP_F_DST_NOREF;
2621d46a9d67SDavid Ahern 	any_src = ipv6_addr_any(&fl6->saddr);
2622741a11d9SDavid Ahern 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
2623d46a9d67SDavid Ahern 	    (fl6->flowi6_oif && any_src))
262477d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
2625c71099acSThomas Graf 
2626d46a9d67SDavid Ahern 	if (!any_src)
2627adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
26280c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
26290c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
2630adaa70bbSThomas Graf 
2631b75cc8f9SDavid Ahern 	return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
26321da177e4SLinus Torvalds }
26337d9e5f42SWei Wang 
ip6_route_output_flags(struct net * net,const struct sock * sk,struct flowi6 * fl6,int flags)26347d9e5f42SWei Wang struct dst_entry *ip6_route_output_flags(struct net *net,
26357d9e5f42SWei Wang 					 const struct sock *sk,
26367d9e5f42SWei Wang 					 struct flowi6 *fl6,
26377d9e5f42SWei Wang 					 int flags)
26387d9e5f42SWei Wang {
26397d9e5f42SWei Wang 	struct dst_entry *dst;
26407d9e5f42SWei Wang 	struct rt6_info *rt6;
26417d9e5f42SWei Wang 
26427d9e5f42SWei Wang 	rcu_read_lock();
26437d9e5f42SWei Wang 	dst = ip6_route_output_flags_noref(net, sk, fl6, flags);
2644797a4c1fSEric Dumazet 	rt6 = dst_rt6_info(dst);
26457d9e5f42SWei Wang 	/* For dst cached in uncached_list, refcnt is already taken. */
2646d288a162SWangyang Guo 	if (list_empty(&rt6->dst.rt_uncached) && !dst_hold_safe(dst)) {
26477d9e5f42SWei Wang 		dst = &net->ipv6.ip6_null_entry->dst;
26487d9e5f42SWei Wang 		dst_hold(dst);
26497d9e5f42SWei Wang 	}
26507d9e5f42SWei Wang 	rcu_read_unlock();
26517d9e5f42SWei Wang 
26527d9e5f42SWei Wang 	return dst;
26537d9e5f42SWei Wang }
26546f21c96aSPaolo Abeni EXPORT_SYMBOL_GPL(ip6_route_output_flags);
26551da177e4SLinus Torvalds 
ip6_blackhole_route(struct net * net,struct dst_entry * dst_orig)26562774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
265714e50e57SDavid S. Miller {
2658797a4c1fSEric Dumazet 	struct rt6_info *rt, *ort = dst_rt6_info(dst_orig);
26591dbe3252SWei Wang 	struct net_device *loopback_dev = net->loopback_dev;
266014e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
266114e50e57SDavid S. Miller 
26621dbe3252SWei Wang 	rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
266362cf27e5SSteffen Klassert 		       DST_OBSOLETE_DEAD, 0);
266414e50e57SDavid S. Miller 	if (rt) {
26650a1f5962SMartin KaFai Lau 		rt6_info_init(rt);
266681eb8447SWei Wang 		atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
26670a1f5962SMartin KaFai Lau 
2668d8d1f30bSChangli Gao 		new = &rt->dst;
266914e50e57SDavid S. Miller 		new->__use = 1;
2670352e512cSHerbert Xu 		new->input = dst_discard;
2671ede2059dSEric W. Biederman 		new->output = dst_discard_out;
267214e50e57SDavid S. Miller 
2673defb3519SDavid S. Miller 		dst_copy_metrics(new, &ort->dst);
267414e50e57SDavid S. Miller 
26751dbe3252SWei Wang 		rt->rt6i_idev = in6_dev_get(loopback_dev);
26764e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
26770a1f5962SMartin KaFai Lau 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
267814e50e57SDavid S. Miller 
267914e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
268014e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
268114e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
268214e50e57SDavid S. Miller #endif
268314e50e57SDavid S. Miller 	}
268414e50e57SDavid S. Miller 
268569ead7afSDavid S. Miller 	dst_release(dst_orig);
268669ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
268714e50e57SDavid S. Miller }
268814e50e57SDavid S. Miller 
26891da177e4SLinus Torvalds /*
26901da177e4SLinus Torvalds  *	Destination cache support functions
26911da177e4SLinus Torvalds  */
26921da177e4SLinus Torvalds 
fib6_check(struct fib6_info * f6i,u32 cookie)26938d1c802bSDavid Ahern static bool fib6_check(struct fib6_info *f6i, u32 cookie)
26943da59bd9SMartin KaFai Lau {
269536143645SSteffen Klassert 	u32 rt_cookie = 0;
2696c5cff856SWei Wang 
26978ae86971SDavid Ahern 	if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
269893531c67SDavid Ahern 		return false;
269993531c67SDavid Ahern 
270093531c67SDavid Ahern 	if (fib6_check_expired(f6i))
270193531c67SDavid Ahern 		return false;
270293531c67SDavid Ahern 
270393531c67SDavid Ahern 	return true;
270493531c67SDavid Ahern }
270593531c67SDavid Ahern 
rt6_check(struct rt6_info * rt,struct fib6_info * from,u32 cookie)2706a68886a6SDavid Ahern static struct dst_entry *rt6_check(struct rt6_info *rt,
2707a68886a6SDavid Ahern 				   struct fib6_info *from,
2708a68886a6SDavid Ahern 				   u32 cookie)
27093da59bd9SMartin KaFai Lau {
2710c5cff856SWei Wang 	u32 rt_cookie = 0;
2711c5cff856SWei Wang 
271249d05fe2SDavid Ahern 	if (!from || !fib6_get_cookie_safe(from, &rt_cookie) ||
271393531c67SDavid Ahern 	    rt_cookie != cookie)
27143da59bd9SMartin KaFai Lau 		return NULL;
27153da59bd9SMartin KaFai Lau 
27163da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
27173da59bd9SMartin KaFai Lau 		return NULL;
27183da59bd9SMartin KaFai Lau 
27193da59bd9SMartin KaFai Lau 	return &rt->dst;
27203da59bd9SMartin KaFai Lau }
27213da59bd9SMartin KaFai Lau 
rt6_dst_from_check(struct rt6_info * rt,struct fib6_info * from,u32 cookie)2722a68886a6SDavid Ahern static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2723a68886a6SDavid Ahern 					    struct fib6_info *from,
2724a68886a6SDavid Ahern 					    u32 cookie)
27253da59bd9SMartin KaFai Lau {
27265973fb1eSMartin KaFai Lau 	if (!__rt6_check_expired(rt) &&
27275973fb1eSMartin KaFai Lau 	    rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
2728a68886a6SDavid Ahern 	    fib6_check(from, cookie))
27293da59bd9SMartin KaFai Lau 		return &rt->dst;
27303da59bd9SMartin KaFai Lau 	else
27313da59bd9SMartin KaFai Lau 		return NULL;
27323da59bd9SMartin KaFai Lau }
27333da59bd9SMartin KaFai Lau 
ip6_dst_check(struct dst_entry * dst,u32 cookie)2734bbd807dfSBrian Vazquez INDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst,
2735bbd807dfSBrian Vazquez 							u32 cookie)
27361da177e4SLinus Torvalds {
2737a87b7dc9SDavid Ahern 	struct dst_entry *dst_ret;
2738a68886a6SDavid Ahern 	struct fib6_info *from;
27391da177e4SLinus Torvalds 	struct rt6_info *rt;
27401da177e4SLinus Torvalds 
2741797a4c1fSEric Dumazet 	rt = dst_rt6_info(dst);
2742a87b7dc9SDavid Ahern 
27438f34e53bSDavid Ahern 	if (rt->sernum)
27448f34e53bSDavid Ahern 		return rt6_is_valid(rt) ? dst : NULL;
27458f34e53bSDavid Ahern 
2746a87b7dc9SDavid Ahern 	rcu_read_lock();
27471da177e4SLinus Torvalds 
27486f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
27496f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
27506f3118b5SNicolas Dichtel 	 * into this function always.
27516f3118b5SNicolas Dichtel 	 */
2752e3bc10bdSHannes Frederic Sowa 
2753a68886a6SDavid Ahern 	from = rcu_dereference(rt->from);
27544b32b5adSMartin KaFai Lau 
2755a68886a6SDavid Ahern 	if (from && (rt->rt6i_flags & RTF_PCPU ||
2756d288a162SWangyang Guo 	    unlikely(!list_empty(&rt->dst.rt_uncached))))
2757a68886a6SDavid Ahern 		dst_ret = rt6_dst_from_check(rt, from, cookie);
27583da59bd9SMartin KaFai Lau 	else
2759a68886a6SDavid Ahern 		dst_ret = rt6_check(rt, from, cookie);
2760a87b7dc9SDavid Ahern 
2761a87b7dc9SDavid Ahern 	rcu_read_unlock();
2762a87b7dc9SDavid Ahern 
2763a87b7dc9SDavid Ahern 	return dst_ret;
27641da177e4SLinus Torvalds }
27659c97921aSBrian Vazquez EXPORT_INDIRECT_CALLABLE(ip6_dst_check);
27661da177e4SLinus Torvalds 
ip6_negative_advice(struct sock * sk,struct dst_entry * dst)27675af198c3SEric Dumazet static void ip6_negative_advice(struct sock *sk,
27685af198c3SEric Dumazet 				struct dst_entry *dst)
27691da177e4SLinus Torvalds {
2770797a4c1fSEric Dumazet 	struct rt6_info *rt = dst_rt6_info(dst);
27711da177e4SLinus Torvalds 
277254c1a859SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_CACHE) {
2773c3c14da0SDavid Ahern 		rcu_read_lock();
277454c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt6_check_expired(rt)) {
2775*f43d12fdSJiri Wiesner 			/* rt/dst can not be destroyed yet,
2776*f43d12fdSJiri Wiesner 			 * because of rcu_read_lock()
2777*f43d12fdSJiri Wiesner 			 */
27785af198c3SEric Dumazet 			sk_dst_reset(sk);
277993531c67SDavid Ahern 			rt6_remove_exception_rt(rt);
27801da177e4SLinus Torvalds 		}
2781c3c14da0SDavid Ahern 		rcu_read_unlock();
27825af198c3SEric Dumazet 		return;
278354c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
27845af198c3SEric Dumazet 	sk_dst_reset(sk);
27851da177e4SLinus Torvalds }
27861da177e4SLinus Torvalds 
ip6_link_failure(struct sk_buff * skb)27871da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
27881da177e4SLinus Torvalds {
27891da177e4SLinus Torvalds 	struct rt6_info *rt;
27901da177e4SLinus Torvalds 
27913ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
27921da177e4SLinus Torvalds 
2793797a4c1fSEric Dumazet 	rt = dst_rt6_info(skb_dst(skb));
27941da177e4SLinus Torvalds 	if (rt) {
27958a14e46fSDavid Ahern 		rcu_read_lock();
27961eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
279793531c67SDavid Ahern 			rt6_remove_exception_rt(rt);
2798c5cff856SWei Wang 		} else {
2799a68886a6SDavid Ahern 			struct fib6_info *from;
2800c5cff856SWei Wang 			struct fib6_node *fn;
2801c5cff856SWei Wang 
2802a68886a6SDavid Ahern 			from = rcu_dereference(rt->from);
2803a68886a6SDavid Ahern 			if (from) {
2804a68886a6SDavid Ahern 				fn = rcu_dereference(from->fib6_node);
2805c5cff856SWei Wang 				if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2806aafc2e32SEric Dumazet 					WRITE_ONCE(fn->fn_sernum, -1);
2807a68886a6SDavid Ahern 			}
28081da177e4SLinus Torvalds 		}
28091da177e4SLinus Torvalds 		rcu_read_unlock();
28101da177e4SLinus Torvalds 	}
28111da177e4SLinus Torvalds }
28121da177e4SLinus Torvalds 
rt6_update_expires(struct rt6_info * rt0,int timeout)28136a3e030fSDavid Ahern static void rt6_update_expires(struct rt6_info *rt0, int timeout)
28146a3e030fSDavid Ahern {
2815a68886a6SDavid Ahern 	if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2816a68886a6SDavid Ahern 		struct fib6_info *from;
2817a68886a6SDavid Ahern 
2818a68886a6SDavid Ahern 		rcu_read_lock();
2819a68886a6SDavid Ahern 		from = rcu_dereference(rt0->from);
2820a68886a6SDavid Ahern 		if (from)
2821a68886a6SDavid Ahern 			rt0->dst.expires = from->expires;
2822a68886a6SDavid Ahern 		rcu_read_unlock();
2823a68886a6SDavid Ahern 	}
28246a3e030fSDavid Ahern 
28256a3e030fSDavid Ahern 	dst_set_expires(&rt0->dst, timeout);
28266a3e030fSDavid Ahern 	rt0->rt6i_flags |= RTF_EXPIRES;
28276700c270SDavid S. Miller }
28281da177e4SLinus Torvalds 
rt6_do_update_pmtu(struct rt6_info * rt,u32 mtu)282945e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
283045e4fd26SMartin KaFai Lau {
283145e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
283245e4fd26SMartin KaFai Lau 
2833d4ead6b3SDavid Ahern 	dst_metric_set(&rt->dst, RTAX_MTU, mtu);
283445e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
283545e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
283645e4fd26SMartin KaFai Lau }
283745e4fd26SMartin KaFai Lau 
rt6_cache_allowed_for_pmtu(const struct rt6_info * rt)28380d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
28390d3f6d29SMartin KaFai Lau {
28400d3f6d29SMartin KaFai Lau 	return !(rt->rt6i_flags & RTF_CACHE) &&
28411490ed2aSPaolo Abeni 		(rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
28420d3f6d29SMartin KaFai Lau }
28430d3f6d29SMartin KaFai Lau 
__ip6_rt_update_pmtu(struct dst_entry * dst,const struct sock * sk,const struct ipv6hdr * iph,u32 mtu,bool confirm_neigh)284445e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2845bd085ef6SHangbin Liu 				 const struct ipv6hdr *iph, u32 mtu,
2846bd085ef6SHangbin Liu 				 bool confirm_neigh)
28471da177e4SLinus Torvalds {
28480dec879fSJulian Anastasov 	const struct in6_addr *daddr, *saddr;
2849797a4c1fSEric Dumazet 	struct rt6_info *rt6 = dst_rt6_info(dst);
28501da177e4SLinus Torvalds 
285109454fd0SMaciej Żenczykowski 	/* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU)
285209454fd0SMaciej Żenczykowski 	 * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it.
285309454fd0SMaciej Żenczykowski 	 * [see also comment in rt6_mtu_change_route()]
285409454fd0SMaciej Żenczykowski 	 */
285519bda36cSXin Long 
285645e4fd26SMartin KaFai Lau 	if (iph) {
285745e4fd26SMartin KaFai Lau 		daddr = &iph->daddr;
285845e4fd26SMartin KaFai Lau 		saddr = &iph->saddr;
285945e4fd26SMartin KaFai Lau 	} else if (sk) {
286045e4fd26SMartin KaFai Lau 		daddr = &sk->sk_v6_daddr;
286145e4fd26SMartin KaFai Lau 		saddr = &inet6_sk(sk)->saddr;
286245e4fd26SMartin KaFai Lau 	} else {
28630dec879fSJulian Anastasov 		daddr = NULL;
28640dec879fSJulian Anastasov 		saddr = NULL;
28651da177e4SLinus Torvalds 	}
2866bd085ef6SHangbin Liu 
2867bd085ef6SHangbin Liu 	if (confirm_neigh)
28680dec879fSJulian Anastasov 		dst_confirm_neigh(dst, daddr);
2869bd085ef6SHangbin Liu 
28704a65dff8SGeorg Kohmann 	if (mtu < IPV6_MIN_MTU)
28714a65dff8SGeorg Kohmann 		return;
28720dec879fSJulian Anastasov 	if (mtu >= dst_mtu(dst))
28730dec879fSJulian Anastasov 		return;
28740dec879fSJulian Anastasov 
28750dec879fSJulian Anastasov 	if (!rt6_cache_allowed_for_pmtu(rt6)) {
28760dec879fSJulian Anastasov 		rt6_do_update_pmtu(rt6, mtu);
28772b760fcfSWei Wang 		/* update rt6_ex->stamp for cache */
28782b760fcfSWei Wang 		if (rt6->rt6i_flags & RTF_CACHE)
28792b760fcfSWei Wang 			rt6_update_exception_stamp_rt(rt6);
28800dec879fSJulian Anastasov 	} else if (daddr) {
288185bd05deSDavid Ahern 		struct fib6_result res = {};
28820dec879fSJulian Anastasov 		struct rt6_info *nrt6;
28830dec879fSJulian Anastasov 
28844d85cd0cSDavid Ahern 		rcu_read_lock();
288585bd05deSDavid Ahern 		res.f6i = rcu_dereference(rt6->from);
288643a4b60dSDavid Ahern 		if (!res.f6i)
288743a4b60dSDavid Ahern 			goto out_unlock;
288843a4b60dSDavid Ahern 
28897d21fec9SDavid Ahern 		res.fib6_flags = res.f6i->fib6_flags;
28907d21fec9SDavid Ahern 		res.fib6_type = res.f6i->fib6_type;
28917d21fec9SDavid Ahern 
28922d44234bSDavid Ahern 		if (res.f6i->nh) {
28932d44234bSDavid Ahern 			struct fib6_nh_match_arg arg = {
28942d44234bSDavid Ahern 				.dev = dst->dev,
28952d44234bSDavid Ahern 				.gw = &rt6->rt6i_gateway,
28962d44234bSDavid Ahern 			};
28972d44234bSDavid Ahern 
28982d44234bSDavid Ahern 			nexthop_for_each_fib6_nh(res.f6i->nh,
28992d44234bSDavid Ahern 						 fib6_nh_find_match, &arg);
29002d44234bSDavid Ahern 
29012d44234bSDavid Ahern 			/* fib6_info uses a nexthop that does not have fib6_nh
29022d44234bSDavid Ahern 			 * using the dst->dev + gw. Should be impossible.
29032d44234bSDavid Ahern 			 */
290443a4b60dSDavid Ahern 			if (!arg.match)
290543a4b60dSDavid Ahern 				goto out_unlock;
29062d44234bSDavid Ahern 
29072d44234bSDavid Ahern 			res.nh = arg.match;
29082d44234bSDavid Ahern 		} else {
29092d44234bSDavid Ahern 			res.nh = res.f6i->fib6_nh;
29102d44234bSDavid Ahern 		}
29112d44234bSDavid Ahern 
291285bd05deSDavid Ahern 		nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
291345e4fd26SMartin KaFai Lau 		if (nrt6) {
291445e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
29155012f0a5SDavid Ahern 			if (rt6_insert_exception(nrt6, &res))
29162b760fcfSWei Wang 				dst_release_immediate(&nrt6->dst);
291745e4fd26SMartin KaFai Lau 		}
291843a4b60dSDavid Ahern out_unlock:
2919a68886a6SDavid Ahern 		rcu_read_unlock();
292045e4fd26SMartin KaFai Lau 	}
292145e4fd26SMartin KaFai Lau }
292245e4fd26SMartin KaFai Lau 
ip6_rt_update_pmtu(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb,u32 mtu,bool confirm_neigh)292345e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2924bd085ef6SHangbin Liu 			       struct sk_buff *skb, u32 mtu,
2925bd085ef6SHangbin Liu 			       bool confirm_neigh)
292645e4fd26SMartin KaFai Lau {
2927bd085ef6SHangbin Liu 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu,
2928bd085ef6SHangbin Liu 			     confirm_neigh);
29291da177e4SLinus Torvalds }
29301da177e4SLinus Torvalds 
ip6_update_pmtu(struct sk_buff * skb,struct net * net,__be32 mtu,int oif,u32 mark,kuid_t uid)293142ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
2932e2d118a1SLorenzo Colitti 		     int oif, u32 mark, kuid_t uid)
293381aded24SDavid S. Miller {
293481aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
293581aded24SDavid S. Miller 	struct dst_entry *dst;
2936dc92095dSMaciej Żenczykowski 	struct flowi6 fl6 = {
2937dc92095dSMaciej Żenczykowski 		.flowi6_oif = oif,
2938dc92095dSMaciej Żenczykowski 		.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2939dc92095dSMaciej Żenczykowski 		.daddr = iph->daddr,
2940dc92095dSMaciej Żenczykowski 		.saddr = iph->saddr,
2941dc92095dSMaciej Żenczykowski 		.flowlabel = ip6_flowinfo(iph),
2942dc92095dSMaciej Żenczykowski 		.flowi6_uid = uid,
2943dc92095dSMaciej Żenczykowski 	};
294481aded24SDavid S. Miller 
294581aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
294681aded24SDavid S. Miller 	if (!dst->error)
2947bd085ef6SHangbin Liu 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true);
294881aded24SDavid S. Miller 	dst_release(dst);
294981aded24SDavid S. Miller }
295081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
295181aded24SDavid S. Miller 
ip6_sk_update_pmtu(struct sk_buff * skb,struct sock * sk,__be32 mtu)295281aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
295381aded24SDavid S. Miller {
29547ddacfa5SDavid Ahern 	int oif = sk->sk_bound_dev_if;
295533c162a9SMartin KaFai Lau 	struct dst_entry *dst;
295633c162a9SMartin KaFai Lau 
29577ddacfa5SDavid Ahern 	if (!oif && skb->dev)
29587ddacfa5SDavid Ahern 		oif = l3mdev_master_ifindex(skb->dev);
29597ddacfa5SDavid Ahern 
29603c5b4d69SEric Dumazet 	ip6_update_pmtu(skb, sock_net(sk), mtu, oif, READ_ONCE(sk->sk_mark),
29613c5b4d69SEric Dumazet 			sk->sk_uid);
296233c162a9SMartin KaFai Lau 
296333c162a9SMartin KaFai Lau 	dst = __sk_dst_get(sk);
296433c162a9SMartin KaFai Lau 	if (!dst || !dst->obsolete ||
296533c162a9SMartin KaFai Lau 	    dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
296633c162a9SMartin KaFai Lau 		return;
296733c162a9SMartin KaFai Lau 
296833c162a9SMartin KaFai Lau 	bh_lock_sock(sk);
296933c162a9SMartin KaFai Lau 	if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
297033c162a9SMartin KaFai Lau 		ip6_datagram_dst_update(sk, false);
297133c162a9SMartin KaFai Lau 	bh_unlock_sock(sk);
297281aded24SDavid S. Miller }
297381aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
297481aded24SDavid S. Miller 
ip6_sk_dst_store_flow(struct sock * sk,struct dst_entry * dst,const struct flowi6 * fl6)29757d6850f7SAlexey Kodanev void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
29767d6850f7SAlexey Kodanev 			   const struct flowi6 *fl6)
29777d6850f7SAlexey Kodanev {
29787d6850f7SAlexey Kodanev #ifdef CONFIG_IPV6_SUBTREES
29797d6850f7SAlexey Kodanev 	struct ipv6_pinfo *np = inet6_sk(sk);
29807d6850f7SAlexey Kodanev #endif
29817d6850f7SAlexey Kodanev 
29827d6850f7SAlexey Kodanev 	ip6_dst_store(sk, dst,
29837d6850f7SAlexey Kodanev 		      ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
29847d6850f7SAlexey Kodanev 		      &sk->sk_v6_daddr : NULL,
29857d6850f7SAlexey Kodanev #ifdef CONFIG_IPV6_SUBTREES
29867d6850f7SAlexey Kodanev 		      ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
29877d6850f7SAlexey Kodanev 		      &np->saddr :
29887d6850f7SAlexey Kodanev #endif
29897d6850f7SAlexey Kodanev 		      NULL);
29907d6850f7SAlexey Kodanev }
29917d6850f7SAlexey Kodanev 
ip6_redirect_nh_match(const struct fib6_result * res,struct flowi6 * fl6,const struct in6_addr * gw,struct rt6_info ** ret)29929b6b35abSDavid Ahern static bool ip6_redirect_nh_match(const struct fib6_result *res,
29930b34eb00SDavid Ahern 				  struct flowi6 *fl6,
29940b34eb00SDavid Ahern 				  const struct in6_addr *gw,
29950b34eb00SDavid Ahern 				  struct rt6_info **ret)
29960b34eb00SDavid Ahern {
29979b6b35abSDavid Ahern 	const struct fib6_nh *nh = res->nh;
29989b6b35abSDavid Ahern 
29990b34eb00SDavid Ahern 	if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
30000b34eb00SDavid Ahern 	    fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
30010b34eb00SDavid Ahern 		return false;
30020b34eb00SDavid Ahern 
30030b34eb00SDavid Ahern 	/* rt_cache's gateway might be different from its 'parent'
30040b34eb00SDavid Ahern 	 * in the case of an ip redirect.
30050b34eb00SDavid Ahern 	 * So we keep searching in the exception table if the gateway
30060b34eb00SDavid Ahern 	 * is different.
30070b34eb00SDavid Ahern 	 */
30080b34eb00SDavid Ahern 	if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
30090b34eb00SDavid Ahern 		struct rt6_info *rt_cache;
30100b34eb00SDavid Ahern 
30119b6b35abSDavid Ahern 		rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
30120b34eb00SDavid Ahern 		if (rt_cache &&
30130b34eb00SDavid Ahern 		    ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
30140b34eb00SDavid Ahern 			*ret = rt_cache;
30150b34eb00SDavid Ahern 			return true;
30160b34eb00SDavid Ahern 		}
30170b34eb00SDavid Ahern 		return false;
30180b34eb00SDavid Ahern 	}
30190b34eb00SDavid Ahern 	return true;
30200b34eb00SDavid Ahern }
30210b34eb00SDavid Ahern 
3022c55c8988SDavid Ahern struct fib6_nh_rd_arg {
3023c55c8988SDavid Ahern 	struct fib6_result	*res;
3024c55c8988SDavid Ahern 	struct flowi6		*fl6;
3025c55c8988SDavid Ahern 	const struct in6_addr	*gw;
3026c55c8988SDavid Ahern 	struct rt6_info		**ret;
3027c55c8988SDavid Ahern };
3028c55c8988SDavid Ahern 
fib6_nh_redirect_match(struct fib6_nh * nh,void * _arg)3029c55c8988SDavid Ahern static int fib6_nh_redirect_match(struct fib6_nh *nh, void *_arg)
3030c55c8988SDavid Ahern {
3031c55c8988SDavid Ahern 	struct fib6_nh_rd_arg *arg = _arg;
3032c55c8988SDavid Ahern 
3033c55c8988SDavid Ahern 	arg->res->nh = nh;
3034c55c8988SDavid Ahern 	return ip6_redirect_nh_match(arg->res, arg->fl6, arg->gw, arg->ret);
3035c55c8988SDavid Ahern }
3036c55c8988SDavid Ahern 
3037b55b76b2SDuan Jiong /* Handle redirects */
3038b55b76b2SDuan Jiong struct ip6rd_flowi {
3039b55b76b2SDuan Jiong 	struct flowi6 fl6;
3040b55b76b2SDuan Jiong 	struct in6_addr gateway;
3041b55b76b2SDuan Jiong };
3042b55b76b2SDuan Jiong 
__ip6_route_redirect(struct net * net,struct fib6_table * table,struct flowi6 * fl6,const struct sk_buff * skb,int flags)304355cced4fSBrian Vazquez INDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net,
3044b55b76b2SDuan Jiong 					     struct fib6_table *table,
3045b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
3046b75cc8f9SDavid Ahern 					     const struct sk_buff *skb,
3047b55b76b2SDuan Jiong 					     int flags)
3048b55b76b2SDuan Jiong {
3049b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
30500b34eb00SDavid Ahern 	struct rt6_info *ret = NULL;
30519b6b35abSDavid Ahern 	struct fib6_result res = {};
3052c55c8988SDavid Ahern 	struct fib6_nh_rd_arg arg = {
3053c55c8988SDavid Ahern 		.res = &res,
3054c55c8988SDavid Ahern 		.fl6 = fl6,
3055c55c8988SDavid Ahern 		.gw  = &rdfl->gateway,
3056c55c8988SDavid Ahern 		.ret = &ret
3057c55c8988SDavid Ahern 	};
30588d1c802bSDavid Ahern 	struct fib6_info *rt;
3059b55b76b2SDuan Jiong 	struct fib6_node *fn;
3060b55b76b2SDuan Jiong 
3061b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
306267c408cfSAlexander Alemayhu 	 * check if the redirect has come from appropriate router.
3063b55b76b2SDuan Jiong 	 *
3064b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
3065b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
3066b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
3067b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
3068b55b76b2SDuan Jiong 	 * routes.
3069b55b76b2SDuan Jiong 	 */
3070b55b76b2SDuan Jiong 
307166f5d6ceSWei Wang 	rcu_read_lock();
30726454743bSDavid Ahern 	fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
3073b55b76b2SDuan Jiong restart:
307466f5d6ceSWei Wang 	for_each_fib6_node_rt_rcu(fn) {
30759b6b35abSDavid Ahern 		res.f6i = rt;
307614895687SDavid Ahern 		if (fib6_check_expired(rt))
3077b55b76b2SDuan Jiong 			continue;
307893c2fb25SDavid Ahern 		if (rt->fib6_flags & RTF_REJECT)
3079b55b76b2SDuan Jiong 			break;
3080c55c8988SDavid Ahern 		if (unlikely(rt->nh)) {
3081c55c8988SDavid Ahern 			if (nexthop_is_blackhole(rt->nh))
3082c55c8988SDavid Ahern 				continue;
3083c55c8988SDavid Ahern 			/* on match, res->nh is filled in and potentially ret */
3084c55c8988SDavid Ahern 			if (nexthop_for_each_fib6_nh(rt->nh,
3085c55c8988SDavid Ahern 						     fib6_nh_redirect_match,
3086c55c8988SDavid Ahern 						     &arg))
30870b34eb00SDavid Ahern 				goto out;
3088c55c8988SDavid Ahern 		} else {
3089c55c8988SDavid Ahern 			res.nh = rt->fib6_nh;
3090c55c8988SDavid Ahern 			if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway,
3091c55c8988SDavid Ahern 						  &ret))
3092c55c8988SDavid Ahern 				goto out;
3093c55c8988SDavid Ahern 		}
3094b55b76b2SDuan Jiong 	}
3095b55b76b2SDuan Jiong 
3096b55b76b2SDuan Jiong 	if (!rt)
3097421842edSDavid Ahern 		rt = net->ipv6.fib6_null_entry;
309893c2fb25SDavid Ahern 	else if (rt->fib6_flags & RTF_REJECT) {
309923fb93a4SDavid Ahern 		ret = net->ipv6.ip6_null_entry;
3100b0a1ba59SMartin KaFai Lau 		goto out;
3101b0a1ba59SMartin KaFai Lau 	}
3102b0a1ba59SMartin KaFai Lau 
3103421842edSDavid Ahern 	if (rt == net->ipv6.fib6_null_entry) {
3104a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
3105a3c00e46SMartin KaFai Lau 		if (fn)
3106a3c00e46SMartin KaFai Lau 			goto restart;
3107b55b76b2SDuan Jiong 	}
3108a3c00e46SMartin KaFai Lau 
31099b6b35abSDavid Ahern 	res.f6i = rt;
31101cf844c7SDavid Ahern 	res.nh = rt->fib6_nh;
3111b0a1ba59SMartin KaFai Lau out:
31127d21fec9SDavid Ahern 	if (ret) {
311310585b43SDavid Ahern 		ip6_hold_safe(net, &ret);
31147d21fec9SDavid Ahern 	} else {
31157d21fec9SDavid Ahern 		res.fib6_flags = res.f6i->fib6_flags;
31167d21fec9SDavid Ahern 		res.fib6_type = res.f6i->fib6_type;
31179b6b35abSDavid Ahern 		ret = ip6_create_rt_rcu(&res);
31187d21fec9SDavid Ahern 	}
3119b55b76b2SDuan Jiong 
312066f5d6ceSWei Wang 	rcu_read_unlock();
3121b55b76b2SDuan Jiong 
31228ff2e5b2SDavid Ahern 	trace_fib6_table_lookup(net, &res, table, fl6);
312323fb93a4SDavid Ahern 	return ret;
3124b55b76b2SDuan Jiong };
3125b55b76b2SDuan Jiong 
ip6_route_redirect(struct net * net,const struct flowi6 * fl6,const struct sk_buff * skb,const struct in6_addr * gateway)3126b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
3127b55b76b2SDuan Jiong 					    const struct flowi6 *fl6,
3128b75cc8f9SDavid Ahern 					    const struct sk_buff *skb,
3129b55b76b2SDuan Jiong 					    const struct in6_addr *gateway)
3130b55b76b2SDuan Jiong {
3131b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
3132b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
3133b55b76b2SDuan Jiong 
3134b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
3135b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
3136b55b76b2SDuan Jiong 
3137b75cc8f9SDavid Ahern 	return fib6_rule_lookup(net, &rdfl.fl6, skb,
3138b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
3139b55b76b2SDuan Jiong }
3140b55b76b2SDuan Jiong 
ip6_redirect(struct sk_buff * skb,struct net * net,int oif,u32 mark,kuid_t uid)3141e2d118a1SLorenzo Colitti void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
3142e2d118a1SLorenzo Colitti 		  kuid_t uid)
31433a5ad2eeSDavid S. Miller {
31443a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
31453a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
31461f7f10acSMaciej Żenczykowski 	struct flowi6 fl6 = {
31471f7f10acSMaciej Żenczykowski 		.flowi6_iif = LOOPBACK_IFINDEX,
31481f7f10acSMaciej Żenczykowski 		.flowi6_oif = oif,
31491f7f10acSMaciej Żenczykowski 		.flowi6_mark = mark,
31501f7f10acSMaciej Żenczykowski 		.daddr = iph->daddr,
31511f7f10acSMaciej Żenczykowski 		.saddr = iph->saddr,
31521f7f10acSMaciej Żenczykowski 		.flowlabel = ip6_flowinfo(iph),
31531f7f10acSMaciej Żenczykowski 		.flowi6_uid = uid,
31541f7f10acSMaciej Żenczykowski 	};
31553a5ad2eeSDavid S. Miller 
3156b75cc8f9SDavid Ahern 	dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
31576700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
31583a5ad2eeSDavid S. Miller 	dst_release(dst);
31593a5ad2eeSDavid S. Miller }
31603a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
31613a5ad2eeSDavid S. Miller 
ip6_redirect_no_header(struct sk_buff * skb,struct net * net,int oif)3162d456336dSMaciej Żenczykowski void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
3163c92a59ecSDuan Jiong {
3164c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
3165c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
3166c92a59ecSDuan Jiong 	struct dst_entry *dst;
31670b26fb17SMaciej Żenczykowski 	struct flowi6 fl6 = {
31680b26fb17SMaciej Żenczykowski 		.flowi6_iif = LOOPBACK_IFINDEX,
31690b26fb17SMaciej Żenczykowski 		.flowi6_oif = oif,
31700b26fb17SMaciej Żenczykowski 		.daddr = msg->dest,
31710b26fb17SMaciej Żenczykowski 		.saddr = iph->daddr,
31720b26fb17SMaciej Żenczykowski 		.flowi6_uid = sock_net_uid(net, NULL),
31730b26fb17SMaciej Żenczykowski 	};
3174c92a59ecSDuan Jiong 
3175b75cc8f9SDavid Ahern 	dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
3176c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
3177c92a59ecSDuan Jiong 	dst_release(dst);
3178c92a59ecSDuan Jiong }
3179c92a59ecSDuan Jiong 
ip6_sk_redirect(struct sk_buff * skb,struct sock * sk)31803a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
31813a5ad2eeSDavid S. Miller {
31823c5b4d69SEric Dumazet 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if,
31833c5b4d69SEric Dumazet 		     READ_ONCE(sk->sk_mark), sk->sk_uid);
31843a5ad2eeSDavid S. Miller }
31853a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
31863a5ad2eeSDavid S. Miller 
ip6_default_advmss(const struct dst_entry * dst)31870dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
31881da177e4SLinus Torvalds {
31890dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
31900dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
31910dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
31920dbaee3bSDavid S. Miller 
31931da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
31941da177e4SLinus Torvalds 
31955578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
31965578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
31971da177e4SLinus Torvalds 
31981da177e4SLinus Torvalds 	/*
31991da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
32001da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
32011da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
32021da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
32031da177e4SLinus Torvalds 	 */
32041da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
32051da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
32061da177e4SLinus Torvalds 	return mtu;
32071da177e4SLinus Torvalds }
32081da177e4SLinus Torvalds 
ip6_mtu(const struct dst_entry * dst)3209f67fbeaeSBrian Vazquez INDIRECT_CALLABLE_SCOPE unsigned int ip6_mtu(const struct dst_entry *dst)
3210d33e4553SDavid S. Miller {
3211427faee1SVadim Fedorenko 	return ip6_dst_mtu_maybe_forward(dst, false);
3212d33e4553SDavid S. Miller }
32139c97921aSBrian Vazquez EXPORT_INDIRECT_CALLABLE(ip6_mtu);
3214d33e4553SDavid S. Miller 
3215901731b8SDavid Ahern /* MTU selection:
3216901731b8SDavid Ahern  * 1. mtu on route is locked - use it
3217901731b8SDavid Ahern  * 2. mtu from nexthop exception
3218901731b8SDavid Ahern  * 3. mtu from egress device
3219901731b8SDavid Ahern  *
3220901731b8SDavid Ahern  * based on ip6_dst_mtu_forward and exception logic of
3221901731b8SDavid Ahern  * rt6_find_cached_rt; called with rcu_read_lock
3222901731b8SDavid Ahern  */
ip6_mtu_from_fib6(const struct fib6_result * res,const struct in6_addr * daddr,const struct in6_addr * saddr)3223b748f260SDavid Ahern u32 ip6_mtu_from_fib6(const struct fib6_result *res,
3224b748f260SDavid Ahern 		      const struct in6_addr *daddr,
3225b748f260SDavid Ahern 		      const struct in6_addr *saddr)
3226901731b8SDavid Ahern {
3227b748f260SDavid Ahern 	const struct fib6_nh *nh = res->nh;
3228b748f260SDavid Ahern 	struct fib6_info *f6i = res->f6i;
3229901731b8SDavid Ahern 	struct inet6_dev *idev;
3230510e2cedSWei Wang 	struct rt6_info *rt;
3231901731b8SDavid Ahern 	u32 mtu = 0;
3232901731b8SDavid Ahern 
3233901731b8SDavid Ahern 	if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
3234901731b8SDavid Ahern 		mtu = f6i->fib6_pmtu;
3235901731b8SDavid Ahern 		if (mtu)
3236901731b8SDavid Ahern 			goto out;
3237901731b8SDavid Ahern 	}
3238901731b8SDavid Ahern 
3239510e2cedSWei Wang 	rt = rt6_find_cached_rt(res, daddr, saddr);
3240510e2cedSWei Wang 	if (unlikely(rt)) {
3241510e2cedSWei Wang 		mtu = dst_metric_raw(&rt->dst, RTAX_MTU);
3242510e2cedSWei Wang 	} else {
3243b748f260SDavid Ahern 		struct net_device *dev = nh->fib_nh_dev;
3244901731b8SDavid Ahern 
3245901731b8SDavid Ahern 		mtu = IPV6_MIN_MTU;
3246901731b8SDavid Ahern 		idev = __in6_dev_get(dev);
3247901731b8SDavid Ahern 		if (idev && idev->cnf.mtu6 > mtu)
3248901731b8SDavid Ahern 			mtu = idev->cnf.mtu6;
3249901731b8SDavid Ahern 	}
3250901731b8SDavid Ahern 
3251901731b8SDavid Ahern 	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
3252901731b8SDavid Ahern out:
3253b748f260SDavid Ahern 	return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
3254901731b8SDavid Ahern }
3255901731b8SDavid Ahern 
icmp6_dst_alloc(struct net_device * dev,struct flowi6 * fl6)32563b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
325787a11578SDavid S. Miller 				  struct flowi6 *fl6)
32581da177e4SLinus Torvalds {
325987a11578SDavid S. Miller 	struct dst_entry *dst;
32601da177e4SLinus Torvalds 	struct rt6_info *rt;
32611da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
3262c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
32631da177e4SLinus Torvalds 
326438308473SDavid S. Miller 	if (unlikely(!idev))
3265122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
32661da177e4SLinus Torvalds 
3267ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, dev, 0);
326838308473SDavid S. Miller 	if (unlikely(!rt)) {
32691da177e4SLinus Torvalds 		in6_dev_put(idev);
327087a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
32711da177e4SLinus Torvalds 		goto out;
32721da177e4SLinus Torvalds 	}
32731da177e4SLinus Torvalds 
3274588753f1SBrendan McGrath 	rt->dst.input = ip6_input;
32758e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
3276550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
327787a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
32788e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
32798e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
328014edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
32811da177e4SLinus Torvalds 
32824c981e28SIdo Schimmel 	/* Add this dst into uncached_list so that rt6_disable_ip() can
3283587fea74SWei Wang 	 * do proper release of the net_device
3284587fea74SWei Wang 	 */
3285587fea74SWei Wang 	rt6_uncached_list_add(rt);
32861da177e4SLinus Torvalds 
328787a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
328887a11578SDavid S. Miller 
32891da177e4SLinus Torvalds out:
329087a11578SDavid S. Miller 	return dst;
32911da177e4SLinus Torvalds }
32921da177e4SLinus Torvalds 
ip6_dst_gc(struct dst_ops * ops)3293af6d1034SJon Maxwell static void ip6_dst_gc(struct dst_ops *ops)
32941da177e4SLinus Torvalds {
329586393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
32967019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
32977019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
32987019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
32997019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
33009cb7c013SEric Dumazet 	unsigned int val;
3301fc66f95cSEric Dumazet 	int entries;
33021da177e4SLinus Torvalds 
3303af6d1034SJon Maxwell 	if (time_after(rt_last_gc + rt_min_interval, jiffies))
33041da177e4SLinus Torvalds 		goto out;
33051da177e4SLinus Torvalds 
33069cb7c013SEric Dumazet 	fib6_run_gc(atomic_inc_return(&net->ipv6.ip6_rt_gc_expire), net, true);
3307fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
3308fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
33099cb7c013SEric Dumazet 		atomic_set(&net->ipv6.ip6_rt_gc_expire, rt_gc_timeout >> 1);
33101da177e4SLinus Torvalds out:
33119cb7c013SEric Dumazet 	val = atomic_read(&net->ipv6.ip6_rt_gc_expire);
33129cb7c013SEric Dumazet 	atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity));
33131da177e4SLinus Torvalds }
33141da177e4SLinus Torvalds 
ip6_nh_lookup_table(struct net * net,struct fib6_config * cfg,const struct in6_addr * gw_addr,u32 tbid,int flags,struct fib6_result * res)3315b2c709ccSDavid Ahern static int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg,
3316b2c709ccSDavid Ahern 			       const struct in6_addr *gw_addr, u32 tbid,
3317b2c709ccSDavid Ahern 			       int flags, struct fib6_result *res)
33188c14586fSDavid Ahern {
33198c14586fSDavid Ahern 	struct flowi6 fl6 = {
33208c14586fSDavid Ahern 		.flowi6_oif = cfg->fc_ifindex,
33218c14586fSDavid Ahern 		.daddr = *gw_addr,
33228c14586fSDavid Ahern 		.saddr = cfg->fc_prefsrc,
33238c14586fSDavid Ahern 	};
33248c14586fSDavid Ahern 	struct fib6_table *table;
3325b2c709ccSDavid Ahern 	int err;
33268c14586fSDavid Ahern 
3327f4797b33SDavid Ahern 	table = fib6_get_table(net, tbid);
33288c14586fSDavid Ahern 	if (!table)
3329b2c709ccSDavid Ahern 		return -EINVAL;
33308c14586fSDavid Ahern 
33318c14586fSDavid Ahern 	if (!ipv6_addr_any(&cfg->fc_prefsrc))
33328c14586fSDavid Ahern 		flags |= RT6_LOOKUP_F_HAS_SADDR;
33338c14586fSDavid Ahern 
3334f4797b33SDavid Ahern 	flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
33358c14586fSDavid Ahern 
3336b2c709ccSDavid Ahern 	err = fib6_table_lookup(net, table, cfg->fc_ifindex, &fl6, res, flags);
3337b2c709ccSDavid Ahern 	if (!err && res->f6i != net->ipv6.fib6_null_entry)
3338b2c709ccSDavid Ahern 		fib6_select_path(net, res, &fl6, cfg->fc_ifindex,
3339b2c709ccSDavid Ahern 				 cfg->fc_ifindex != 0, NULL, flags);
33408c14586fSDavid Ahern 
3341b2c709ccSDavid Ahern 	return err;
33428c14586fSDavid Ahern }
33438c14586fSDavid Ahern 
ip6_route_check_nh_onlink(struct net * net,struct fib6_config * cfg,const struct net_device * dev,struct netlink_ext_ack * extack)3344fc1e64e1SDavid Ahern static int ip6_route_check_nh_onlink(struct net *net,
3345fc1e64e1SDavid Ahern 				     struct fib6_config *cfg,
33469fbb704cSDavid Ahern 				     const struct net_device *dev,
3347fc1e64e1SDavid Ahern 				     struct netlink_ext_ack *extack)
3348fc1e64e1SDavid Ahern {
3349b2c709ccSDavid Ahern 	u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
3350fc1e64e1SDavid Ahern 	const struct in6_addr *gw_addr = &cfg->fc_gateway;
3351b2c709ccSDavid Ahern 	struct fib6_result res = {};
3352fc1e64e1SDavid Ahern 	int err;
3353fc1e64e1SDavid Ahern 
3354b2c709ccSDavid Ahern 	err = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0, &res);
3355b2c709ccSDavid Ahern 	if (!err && !(res.fib6_flags & RTF_REJECT) &&
33564ed591c8SDavid Ahern 	    /* ignore match if it is the default route */
3357b2c709ccSDavid Ahern 	    !ipv6_addr_any(&res.f6i->fib6_dst.addr) &&
3358b2c709ccSDavid Ahern 	    (res.fib6_type != RTN_UNICAST || dev != res.nh->fib_nh_dev)) {
335944750f84SDavid Ahern 		NL_SET_ERR_MSG(extack,
336044750f84SDavid Ahern 			       "Nexthop has invalid gateway or device mismatch");
3361fc1e64e1SDavid Ahern 		err = -EINVAL;
3362fc1e64e1SDavid Ahern 	}
3363fc1e64e1SDavid Ahern 
3364fc1e64e1SDavid Ahern 	return err;
3365fc1e64e1SDavid Ahern }
3366fc1e64e1SDavid Ahern 
ip6_route_check_nh(struct net * net,struct fib6_config * cfg,struct net_device ** _dev,netdevice_tracker * dev_tracker,struct inet6_dev ** idev)33671edce99fSDavid Ahern static int ip6_route_check_nh(struct net *net,
33681edce99fSDavid Ahern 			      struct fib6_config *cfg,
33691edce99fSDavid Ahern 			      struct net_device **_dev,
33703515440dSEric Dumazet 			      netdevice_tracker *dev_tracker,
33711edce99fSDavid Ahern 			      struct inet6_dev **idev)
33721edce99fSDavid Ahern {
33731edce99fSDavid Ahern 	const struct in6_addr *gw_addr = &cfg->fc_gateway;
33741edce99fSDavid Ahern 	struct net_device *dev = _dev ? *_dev : NULL;
3375b2c709ccSDavid Ahern 	int flags = RT6_LOOKUP_F_IFACE;
3376b2c709ccSDavid Ahern 	struct fib6_result res = {};
33771edce99fSDavid Ahern 	int err = -EHOSTUNREACH;
33781edce99fSDavid Ahern 
33791edce99fSDavid Ahern 	if (cfg->fc_table) {
3380b2c709ccSDavid Ahern 		err = ip6_nh_lookup_table(net, cfg, gw_addr,
3381b2c709ccSDavid Ahern 					  cfg->fc_table, flags, &res);
3382b2c709ccSDavid Ahern 		/* gw_addr can not require a gateway or resolve to a reject
3383b2c709ccSDavid Ahern 		 * route. If a device is given, it must match the result.
3384b2c709ccSDavid Ahern 		 */
3385b2c709ccSDavid Ahern 		if (err || res.fib6_flags & RTF_REJECT ||
3386b2c709ccSDavid Ahern 		    res.nh->fib_nh_gw_family ||
3387b2c709ccSDavid Ahern 		    (dev && dev != res.nh->fib_nh_dev))
3388b2c709ccSDavid Ahern 			err = -EHOSTUNREACH;
33891edce99fSDavid Ahern 	}
33901edce99fSDavid Ahern 
3391b2c709ccSDavid Ahern 	if (err < 0) {
3392b2c709ccSDavid Ahern 		struct flowi6 fl6 = {
3393b2c709ccSDavid Ahern 			.flowi6_oif = cfg->fc_ifindex,
3394b2c709ccSDavid Ahern 			.daddr = *gw_addr,
3395b2c709ccSDavid Ahern 		};
33961edce99fSDavid Ahern 
3397b2c709ccSDavid Ahern 		err = fib6_lookup(net, cfg->fc_ifindex, &fl6, &res, flags);
3398b2c709ccSDavid Ahern 		if (err || res.fib6_flags & RTF_REJECT ||
3399b2c709ccSDavid Ahern 		    res.nh->fib_nh_gw_family)
3400b2c709ccSDavid Ahern 			err = -EHOSTUNREACH;
34011edce99fSDavid Ahern 
3402b2c709ccSDavid Ahern 		if (err)
3403b2c709ccSDavid Ahern 			return err;
3404b2c709ccSDavid Ahern 
3405b2c709ccSDavid Ahern 		fib6_select_path(net, &res, &fl6, cfg->fc_ifindex,
3406b2c709ccSDavid Ahern 				 cfg->fc_ifindex != 0, NULL, flags);
34071edce99fSDavid Ahern 	}
34081edce99fSDavid Ahern 
34091edce99fSDavid Ahern 	err = 0;
3410b2c709ccSDavid Ahern 	if (dev) {
3411b2c709ccSDavid Ahern 		if (dev != res.nh->fib_nh_dev)
3412b2c709ccSDavid Ahern 			err = -EHOSTUNREACH;
3413b2c709ccSDavid Ahern 	} else {
3414b2c709ccSDavid Ahern 		*_dev = dev = res.nh->fib_nh_dev;
34153515440dSEric Dumazet 		netdev_hold(dev, dev_tracker, GFP_ATOMIC);
3416b2c709ccSDavid Ahern 		*idev = in6_dev_get(dev);
3417b2c709ccSDavid Ahern 	}
34181edce99fSDavid Ahern 
34191edce99fSDavid Ahern 	return err;
34201edce99fSDavid Ahern }
34211edce99fSDavid Ahern 
ip6_validate_gw(struct net * net,struct fib6_config * cfg,struct net_device ** _dev,netdevice_tracker * dev_tracker,struct inet6_dev ** idev,struct netlink_ext_ack * extack)34229fbb704cSDavid Ahern static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
34233515440dSEric Dumazet 			   struct net_device **_dev,
34243515440dSEric Dumazet 			   netdevice_tracker *dev_tracker,
34253515440dSEric Dumazet 			   struct inet6_dev **idev,
34269fbb704cSDavid Ahern 			   struct netlink_ext_ack *extack)
34279fbb704cSDavid Ahern {
34289fbb704cSDavid Ahern 	const struct in6_addr *gw_addr = &cfg->fc_gateway;
34299fbb704cSDavid Ahern 	int gwa_type = ipv6_addr_type(gw_addr);
3430232378e8SDavid Ahern 	bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
34319fbb704cSDavid Ahern 	const struct net_device *dev = *_dev;
3432232378e8SDavid Ahern 	bool need_addr_check = !dev;
34339fbb704cSDavid Ahern 	int err = -EINVAL;
34349fbb704cSDavid Ahern 
34359fbb704cSDavid Ahern 	/* if gw_addr is local we will fail to detect this in case
34369fbb704cSDavid Ahern 	 * address is still TENTATIVE (DAD in progress). rt6_lookup()
34379fbb704cSDavid Ahern 	 * will return already-added prefix route via interface that
34389fbb704cSDavid Ahern 	 * prefix route was assigned to, which might be non-loopback.
34399fbb704cSDavid Ahern 	 */
3440232378e8SDavid Ahern 	if (dev &&
3441232378e8SDavid Ahern 	    ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
3442232378e8SDavid Ahern 		NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
34439fbb704cSDavid Ahern 		goto out;
34449fbb704cSDavid Ahern 	}
34459fbb704cSDavid Ahern 
34469fbb704cSDavid Ahern 	if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
34479fbb704cSDavid Ahern 		/* IPv6 strictly inhibits using not link-local
34489fbb704cSDavid Ahern 		 * addresses as nexthop address.
34499fbb704cSDavid Ahern 		 * Otherwise, router will not able to send redirects.
34509fbb704cSDavid Ahern 		 * It is very good, but in some (rare!) circumstances
34519fbb704cSDavid Ahern 		 * (SIT, PtP, NBMA NOARP links) it is handy to allow
34529fbb704cSDavid Ahern 		 * some exceptions. --ANK
34539fbb704cSDavid Ahern 		 * We allow IPv4-mapped nexthops to support RFC4798-type
34549fbb704cSDavid Ahern 		 * addressing
34559fbb704cSDavid Ahern 		 */
34569fbb704cSDavid Ahern 		if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
34579fbb704cSDavid Ahern 			NL_SET_ERR_MSG(extack, "Invalid gateway address");
34589fbb704cSDavid Ahern 			goto out;
34599fbb704cSDavid Ahern 		}
34609fbb704cSDavid Ahern 
3461b2c709ccSDavid Ahern 		rcu_read_lock();
3462b2c709ccSDavid Ahern 
34639fbb704cSDavid Ahern 		if (cfg->fc_flags & RTNH_F_ONLINK)
34649fbb704cSDavid Ahern 			err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
34659fbb704cSDavid Ahern 		else
34663515440dSEric Dumazet 			err = ip6_route_check_nh(net, cfg, _dev, dev_tracker,
34673515440dSEric Dumazet 						 idev);
34689fbb704cSDavid Ahern 
3469b2c709ccSDavid Ahern 		rcu_read_unlock();
3470b2c709ccSDavid Ahern 
34719fbb704cSDavid Ahern 		if (err)
34729fbb704cSDavid Ahern 			goto out;
34739fbb704cSDavid Ahern 	}
34749fbb704cSDavid Ahern 
34759fbb704cSDavid Ahern 	/* reload in case device was changed */
34769fbb704cSDavid Ahern 	dev = *_dev;
34779fbb704cSDavid Ahern 
34789fbb704cSDavid Ahern 	err = -EINVAL;
34799fbb704cSDavid Ahern 	if (!dev) {
34809fbb704cSDavid Ahern 		NL_SET_ERR_MSG(extack, "Egress device not specified");
34819fbb704cSDavid Ahern 		goto out;
34829fbb704cSDavid Ahern 	} else if (dev->flags & IFF_LOOPBACK) {
34839fbb704cSDavid Ahern 		NL_SET_ERR_MSG(extack,
34849fbb704cSDavid Ahern 			       "Egress device can not be loopback device for this route");
34859fbb704cSDavid Ahern 		goto out;
34869fbb704cSDavid Ahern 	}
3487232378e8SDavid Ahern 
3488232378e8SDavid Ahern 	/* if we did not check gw_addr above, do so now that the
3489232378e8SDavid Ahern 	 * egress device has been resolved.
3490232378e8SDavid Ahern 	 */
3491232378e8SDavid Ahern 	if (need_addr_check &&
3492232378e8SDavid Ahern 	    ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
3493232378e8SDavid Ahern 		NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
3494232378e8SDavid Ahern 		goto out;
3495232378e8SDavid Ahern 	}
3496232378e8SDavid Ahern 
34979fbb704cSDavid Ahern 	err = 0;
34989fbb704cSDavid Ahern out:
34999fbb704cSDavid Ahern 	return err;
35009fbb704cSDavid Ahern }
35019fbb704cSDavid Ahern 
fib6_is_reject(u32 flags,struct net_device * dev,int addr_type)350283c44251SDavid Ahern static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
350383c44251SDavid Ahern {
350483c44251SDavid Ahern 	if ((flags & RTF_REJECT) ||
350583c44251SDavid Ahern 	    (dev && (dev->flags & IFF_LOOPBACK) &&
350683c44251SDavid Ahern 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
3507aea23c32SDavid Ahern 	     !(flags & (RTF_ANYCAST | RTF_LOCAL))))
350883c44251SDavid Ahern 		return true;
350983c44251SDavid Ahern 
351083c44251SDavid Ahern 	return false;
351183c44251SDavid Ahern }
351283c44251SDavid Ahern 
fib6_nh_init(struct net * net,struct fib6_nh * fib6_nh,struct fib6_config * cfg,gfp_t gfp_flags,struct netlink_ext_ack * extack)351383c44251SDavid Ahern int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
351483c44251SDavid Ahern 		 struct fib6_config *cfg, gfp_t gfp_flags,
351583c44251SDavid Ahern 		 struct netlink_ext_ack *extack)
351683c44251SDavid Ahern {
351770f7457aSJakub Kicinski 	netdevice_tracker *dev_tracker = &fib6_nh->fib_nh_dev_tracker;
351883c44251SDavid Ahern 	struct net_device *dev = NULL;
351983c44251SDavid Ahern 	struct inet6_dev *idev = NULL;
352083c44251SDavid Ahern 	int addr_type;
352183c44251SDavid Ahern 	int err;
352283c44251SDavid Ahern 
3523f1741730SDavid Ahern 	fib6_nh->fib_nh_family = AF_INET6;
35241bef4c22SEric Dumazet #ifdef CONFIG_IPV6_ROUTER_PREF
35251bef4c22SEric Dumazet 	fib6_nh->last_probe = jiffies;
35261bef4c22SEric Dumazet #endif
352738428d68SRoopa Prabhu 	if (cfg->fc_is_fdb) {
352838428d68SRoopa Prabhu 		fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
352938428d68SRoopa Prabhu 		fib6_nh->fib_nh_gw_family = AF_INET6;
353038428d68SRoopa Prabhu 		return 0;
353138428d68SRoopa Prabhu 	}
3532f1741730SDavid Ahern 
353383c44251SDavid Ahern 	err = -ENODEV;
353483c44251SDavid Ahern 	if (cfg->fc_ifindex) {
353570f7457aSJakub Kicinski 		dev = netdev_get_by_index(net, cfg->fc_ifindex,
353670f7457aSJakub Kicinski 					  dev_tracker, gfp_flags);
353783c44251SDavid Ahern 		if (!dev)
353883c44251SDavid Ahern 			goto out;
353983c44251SDavid Ahern 		idev = in6_dev_get(dev);
354083c44251SDavid Ahern 		if (!idev)
354183c44251SDavid Ahern 			goto out;
354283c44251SDavid Ahern 	}
354383c44251SDavid Ahern 
354483c44251SDavid Ahern 	if (cfg->fc_flags & RTNH_F_ONLINK) {
354583c44251SDavid Ahern 		if (!dev) {
354683c44251SDavid Ahern 			NL_SET_ERR_MSG(extack,
354783c44251SDavid Ahern 				       "Nexthop device required for onlink");
354883c44251SDavid Ahern 			goto out;
354983c44251SDavid Ahern 		}
355083c44251SDavid Ahern 
355183c44251SDavid Ahern 		if (!(dev->flags & IFF_UP)) {
355283c44251SDavid Ahern 			NL_SET_ERR_MSG(extack, "Nexthop device is not up");
355383c44251SDavid Ahern 			err = -ENETDOWN;
355483c44251SDavid Ahern 			goto out;
355583c44251SDavid Ahern 		}
355683c44251SDavid Ahern 
3557ad1601aeSDavid Ahern 		fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
355883c44251SDavid Ahern 	}
355983c44251SDavid Ahern 
3560ad1601aeSDavid Ahern 	fib6_nh->fib_nh_weight = 1;
356183c44251SDavid Ahern 
356283c44251SDavid Ahern 	/* We cannot add true routes via loopback here,
356383c44251SDavid Ahern 	 * they would result in kernel looping; promote them to reject routes
356483c44251SDavid Ahern 	 */
356583c44251SDavid Ahern 	addr_type = ipv6_addr_type(&cfg->fc_dst);
356683c44251SDavid Ahern 	if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
356783c44251SDavid Ahern 		/* hold loopback dev/idev if we haven't done so. */
356883c44251SDavid Ahern 		if (dev != net->loopback_dev) {
356983c44251SDavid Ahern 			if (dev) {
357070f7457aSJakub Kicinski 				netdev_put(dev, dev_tracker);
357183c44251SDavid Ahern 				in6_dev_put(idev);
357283c44251SDavid Ahern 			}
357383c44251SDavid Ahern 			dev = net->loopback_dev;
357470f7457aSJakub Kicinski 			netdev_hold(dev, dev_tracker, gfp_flags);
357583c44251SDavid Ahern 			idev = in6_dev_get(dev);
357683c44251SDavid Ahern 			if (!idev) {
357783c44251SDavid Ahern 				err = -ENODEV;
357883c44251SDavid Ahern 				goto out;
357983c44251SDavid Ahern 			}
358083c44251SDavid Ahern 		}
35817dd73168SDavid Ahern 		goto pcpu_alloc;
358283c44251SDavid Ahern 	}
358383c44251SDavid Ahern 
358483c44251SDavid Ahern 	if (cfg->fc_flags & RTF_GATEWAY) {
35853515440dSEric Dumazet 		err = ip6_validate_gw(net, cfg, &dev, dev_tracker,
35863515440dSEric Dumazet 				      &idev, extack);
358783c44251SDavid Ahern 		if (err)
358883c44251SDavid Ahern 			goto out;
358983c44251SDavid Ahern 
3590ad1601aeSDavid Ahern 		fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
3591bdf00467SDavid Ahern 		fib6_nh->fib_nh_gw_family = AF_INET6;
359283c44251SDavid Ahern 	}
359383c44251SDavid Ahern 
359483c44251SDavid Ahern 	err = -ENODEV;
359583c44251SDavid Ahern 	if (!dev)
359683c44251SDavid Ahern 		goto out;
359783c44251SDavid Ahern 
3598b6947723SEric Dumazet 	if (!idev || idev->cnf.disable_ipv6) {
359983c44251SDavid Ahern 		NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
360083c44251SDavid Ahern 		err = -EACCES;
360183c44251SDavid Ahern 		goto out;
360283c44251SDavid Ahern 	}
360383c44251SDavid Ahern 
360483c44251SDavid Ahern 	if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
360583c44251SDavid Ahern 		NL_SET_ERR_MSG(extack, "Nexthop device is not up");
360683c44251SDavid Ahern 		err = -ENETDOWN;
360783c44251SDavid Ahern 		goto out;
360883c44251SDavid Ahern 	}
360983c44251SDavid Ahern 
361083c44251SDavid Ahern 	if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
361183c44251SDavid Ahern 	    !netif_carrier_ok(dev))
3612ad1601aeSDavid Ahern 		fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
361383c44251SDavid Ahern 
3614faee6769SAlexander Aring 	err = fib_nh_common_init(net, &fib6_nh->nh_common, cfg->fc_encap,
36157dd73168SDavid Ahern 				 cfg->fc_encap_type, cfg, gfp_flags, extack);
36167dd73168SDavid Ahern 	if (err)
36177dd73168SDavid Ahern 		goto out;
36187dd73168SDavid Ahern 
36197dd73168SDavid Ahern pcpu_alloc:
3620f40b6ae2SDavid Ahern 	fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
3621f40b6ae2SDavid Ahern 	if (!fib6_nh->rt6i_pcpu) {
3622f40b6ae2SDavid Ahern 		err = -ENOMEM;
3623f40b6ae2SDavid Ahern 		goto out;
3624f40b6ae2SDavid Ahern 	}
3625f40b6ae2SDavid Ahern 
3626ad1601aeSDavid Ahern 	fib6_nh->fib_nh_dev = dev;
3627f1741730SDavid Ahern 	fib6_nh->fib_nh_oif = dev->ifindex;
362883c44251SDavid Ahern 	err = 0;
362983c44251SDavid Ahern out:
363083c44251SDavid Ahern 	if (idev)
363183c44251SDavid Ahern 		in6_dev_put(idev);
363283c44251SDavid Ahern 
363383c44251SDavid Ahern 	if (err) {
3634ad1601aeSDavid Ahern 		lwtstate_put(fib6_nh->fib_nh_lws);
3635ad1601aeSDavid Ahern 		fib6_nh->fib_nh_lws = NULL;
363670f7457aSJakub Kicinski 		netdev_put(dev, dev_tracker);
363783c44251SDavid Ahern 	}
363883c44251SDavid Ahern 
363983c44251SDavid Ahern 	return err;
364083c44251SDavid Ahern }
364183c44251SDavid Ahern 
fib6_nh_release(struct fib6_nh * fib6_nh)3642dac7d0f2SDavid Ahern void fib6_nh_release(struct fib6_nh *fib6_nh)
3643dac7d0f2SDavid Ahern {
3644cc5c073aSDavid Ahern 	struct rt6_exception_bucket *bucket;
3645cc5c073aSDavid Ahern 
3646cc5c073aSDavid Ahern 	rcu_read_lock();
3647cc5c073aSDavid Ahern 
3648cc5c073aSDavid Ahern 	fib6_nh_flush_exceptions(fib6_nh, NULL);
3649cc5c073aSDavid Ahern 	bucket = fib6_nh_get_excptn_bucket(fib6_nh, NULL);
3650cc5c073aSDavid Ahern 	if (bucket) {
3651cc5c073aSDavid Ahern 		rcu_assign_pointer(fib6_nh->rt6i_exception_bucket, NULL);
3652cc5c073aSDavid Ahern 		kfree(bucket);
3653cc5c073aSDavid Ahern 	}
3654cc5c073aSDavid Ahern 
3655cc5c073aSDavid Ahern 	rcu_read_unlock();
3656cc5c073aSDavid Ahern 
365761308050SNikolay Aleksandrov 	fib6_nh_release_dsts(fib6_nh);
3658f40b6ae2SDavid Ahern 	free_percpu(fib6_nh->rt6i_pcpu);
3659f40b6ae2SDavid Ahern 
3660979e276eSDavid Ahern 	fib_nh_common_release(&fib6_nh->nh_common);
3661dac7d0f2SDavid Ahern }
3662dac7d0f2SDavid Ahern 
fib6_nh_release_dsts(struct fib6_nh * fib6_nh)36638837cbbfSNikolay Aleksandrov void fib6_nh_release_dsts(struct fib6_nh *fib6_nh)
36648837cbbfSNikolay Aleksandrov {
36658837cbbfSNikolay Aleksandrov 	int cpu;
36668837cbbfSNikolay Aleksandrov 
36678837cbbfSNikolay Aleksandrov 	if (!fib6_nh->rt6i_pcpu)
36688837cbbfSNikolay Aleksandrov 		return;
36698837cbbfSNikolay Aleksandrov 
36708837cbbfSNikolay Aleksandrov 	for_each_possible_cpu(cpu) {
36718837cbbfSNikolay Aleksandrov 		struct rt6_info *pcpu_rt, **ppcpu_rt;
36728837cbbfSNikolay Aleksandrov 
36738837cbbfSNikolay Aleksandrov 		ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
36748837cbbfSNikolay Aleksandrov 		pcpu_rt = xchg(ppcpu_rt, NULL);
36758837cbbfSNikolay Aleksandrov 		if (pcpu_rt) {
36768837cbbfSNikolay Aleksandrov 			dst_dev_put(&pcpu_rt->dst);
36778837cbbfSNikolay Aleksandrov 			dst_release(&pcpu_rt->dst);
36788837cbbfSNikolay Aleksandrov 		}
36798837cbbfSNikolay Aleksandrov 	}
36808837cbbfSNikolay Aleksandrov }
36818837cbbfSNikolay Aleksandrov 
ip6_route_info_create(struct fib6_config * cfg,gfp_t gfp_flags,struct netlink_ext_ack * extack)36828d1c802bSDavid Ahern static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
3683acb54e3cSDavid Ahern 					      gfp_t gfp_flags,
3684333c4301SDavid Ahern 					      struct netlink_ext_ack *extack)
36851da177e4SLinus Torvalds {
36865578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
36878d1c802bSDavid Ahern 	struct fib6_info *rt = NULL;
3688f88d8ea6SDavid Ahern 	struct nexthop *nh = NULL;
3689c71099acSThomas Graf 	struct fib6_table *table;
3690f88d8ea6SDavid Ahern 	struct fib6_nh *fib6_nh;
36918c5b83f0SRoopa Prabhu 	int err = -EINVAL;
369283c44251SDavid Ahern 	int addr_type;
36931da177e4SLinus Torvalds 
3694557c44beSDavid Ahern 	/* RTF_PCPU is an internal flag; can not be set by userspace */
3695d5d531cbSDavid Ahern 	if (cfg->fc_flags & RTF_PCPU) {
3696d5d531cbSDavid Ahern 		NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
3697557c44beSDavid Ahern 		goto out;
3698d5d531cbSDavid Ahern 	}
3699557c44beSDavid Ahern 
37002ea2352eSWei Wang 	/* RTF_CACHE is an internal flag; can not be set by userspace */
37012ea2352eSWei Wang 	if (cfg->fc_flags & RTF_CACHE) {
37022ea2352eSWei Wang 		NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
37032ea2352eSWei Wang 		goto out;
37042ea2352eSWei Wang 	}
37052ea2352eSWei Wang 
3706e8478e80SDavid Ahern 	if (cfg->fc_type > RTN_MAX) {
3707e8478e80SDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid route type");
3708e8478e80SDavid Ahern 		goto out;
3709e8478e80SDavid Ahern 	}
3710e8478e80SDavid Ahern 
3711d5d531cbSDavid Ahern 	if (cfg->fc_dst_len > 128) {
3712d5d531cbSDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid prefix length");
37138c5b83f0SRoopa Prabhu 		goto out;
3714d5d531cbSDavid Ahern 	}
3715d5d531cbSDavid Ahern 	if (cfg->fc_src_len > 128) {
3716d5d531cbSDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid source address length");
3717d5d531cbSDavid Ahern 		goto out;
3718d5d531cbSDavid Ahern 	}
37191da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
3720d5d531cbSDavid Ahern 	if (cfg->fc_src_len) {
3721d5d531cbSDavid Ahern 		NL_SET_ERR_MSG(extack,
3722d5d531cbSDavid Ahern 			       "Specifying source address requires IPV6_SUBTREES to be enabled");
37238c5b83f0SRoopa Prabhu 		goto out;
3724d5d531cbSDavid Ahern 	}
37251da177e4SLinus Torvalds #endif
37265b98324eSDavid Ahern 	if (cfg->fc_nh_id) {
37275b98324eSDavid Ahern 		nh = nexthop_find_by_id(net, cfg->fc_nh_id);
37285b98324eSDavid Ahern 		if (!nh) {
37295b98324eSDavid Ahern 			NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
37305b98324eSDavid Ahern 			goto out;
37315b98324eSDavid Ahern 		}
37325b98324eSDavid Ahern 		err = fib6_check_nexthop(nh, cfg, extack);
37335b98324eSDavid Ahern 		if (err)
37345b98324eSDavid Ahern 			goto out;
37355b98324eSDavid Ahern 	}
3736fc1e64e1SDavid Ahern 
3737c71099acSThomas Graf 	err = -ENOBUFS;
373838308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
3739d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
3740d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
374138308473SDavid S. Miller 		if (!table) {
3742f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
3743d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
3744d71314b4SMatti Vaittinen 		}
3745d71314b4SMatti Vaittinen 	} else {
3746d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
3747d71314b4SMatti Vaittinen 	}
374838308473SDavid S. Miller 
374938308473SDavid S. Miller 	if (!table)
3750c71099acSThomas Graf 		goto out;
3751c71099acSThomas Graf 
37521da177e4SLinus Torvalds 	err = -ENOMEM;
3753f88d8ea6SDavid Ahern 	rt = fib6_info_alloc(gfp_flags, !nh);
375493531c67SDavid Ahern 	if (!rt)
37551da177e4SLinus Torvalds 		goto out;
375693531c67SDavid Ahern 
375769f397e6SJason Xing 	rt->fib6_metrics = ip_fib_metrics_init(cfg->fc_mx, cfg->fc_mx_len,
3758d7e774f3SDavid Ahern 					       extack);
3759767a2217SDavid Ahern 	if (IS_ERR(rt->fib6_metrics)) {
3760767a2217SDavid Ahern 		err = PTR_ERR(rt->fib6_metrics);
3761fda21d46SEric Dumazet 		/* Do not leave garbage there. */
3762fda21d46SEric Dumazet 		rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
37638fb4792fSPaolo Abeni 		goto out_free;
3764767a2217SDavid Ahern 	}
3765767a2217SDavid Ahern 
376693531c67SDavid Ahern 	if (cfg->fc_flags & RTF_ADDRCONF)
376793531c67SDavid Ahern 		rt->dst_nocount = true;
37681da177e4SLinus Torvalds 
37691716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
3770b577b9aaSDavid Ahern 		fib6_set_expires(rt, jiffies +
37711716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
37721716a961SGao feng 	else
3773b577b9aaSDavid Ahern 		fib6_clean_expires(rt);
37741da177e4SLinus Torvalds 
377586872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
377686872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
377793c2fb25SDavid Ahern 	rt->fib6_protocol = cfg->fc_protocol;
377886872cb5SThomas Graf 
377983c44251SDavid Ahern 	rt->fib6_table = table;
378083c44251SDavid Ahern 	rt->fib6_metric = cfg->fc_metric;
3781c7036d97SDavid Ahern 	rt->fib6_type = cfg->fc_type ? : RTN_UNICAST;
37822b2450caSDavid Ahern 	rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
378319e42e45SRoopa Prabhu 
378493c2fb25SDavid Ahern 	ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
378593c2fb25SDavid Ahern 	rt->fib6_dst.plen = cfg->fc_dst_len;
37861da177e4SLinus Torvalds 
37871da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
378893c2fb25SDavid Ahern 	ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
378993c2fb25SDavid Ahern 	rt->fib6_src.plen = cfg->fc_src_len;
37901da177e4SLinus Torvalds #endif
3791f88d8ea6SDavid Ahern 	if (nh) {
3792f88d8ea6SDavid Ahern 		if (rt->fib6_src.plen) {
37934daa95afSColin Ian King 			NL_SET_ERR_MSG(extack, "Nexthops can not be used with source routing");
3794821bbf79SCoco Li 			goto out_free;
3795f88d8ea6SDavid Ahern 		}
3796706ec919SXiyu Yang 		if (!nexthop_get(nh)) {
3797706ec919SXiyu Yang 			NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
3798821bbf79SCoco Li 			goto out_free;
3799706ec919SXiyu Yang 		}
3800f88d8ea6SDavid Ahern 		rt->nh = nh;
3801f88d8ea6SDavid Ahern 		fib6_nh = nexthop_fib6_nh(rt->nh);
3802f88d8ea6SDavid Ahern 	} else {
38031cf844c7SDavid Ahern 		err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack);
38041da177e4SLinus Torvalds 		if (err)
38051da177e4SLinus Torvalds 			goto out;
38069fbb704cSDavid Ahern 
3807f88d8ea6SDavid Ahern 		fib6_nh = rt->fib6_nh;
3808f88d8ea6SDavid Ahern 
3809f88d8ea6SDavid Ahern 		/* We cannot add true routes via loopback here, they would
3810f88d8ea6SDavid Ahern 		 * result in kernel looping; promote them to reject routes
381183c44251SDavid Ahern 		 */
381283c44251SDavid Ahern 		addr_type = ipv6_addr_type(&cfg->fc_dst);
3813f88d8ea6SDavid Ahern 		if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev,
3814f88d8ea6SDavid Ahern 				   addr_type))
381583c44251SDavid Ahern 			rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
3816f88d8ea6SDavid Ahern 	}
3817955ec4cbSDavid Ahern 
3818c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
3819f88d8ea6SDavid Ahern 		struct net_device *dev = fib6_nh->fib_nh_dev;
382083c44251SDavid Ahern 
3821c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
3822d5d531cbSDavid Ahern 			NL_SET_ERR_MSG(extack, "Invalid source address");
3823c3968a85SDaniel Walter 			err = -EINVAL;
3824c3968a85SDaniel Walter 			goto out;
3825c3968a85SDaniel Walter 		}
382693c2fb25SDavid Ahern 		rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
382793c2fb25SDavid Ahern 		rt->fib6_prefsrc.plen = 128;
3828c3968a85SDaniel Walter 	} else
382993c2fb25SDavid Ahern 		rt->fib6_prefsrc.plen = 0;
3830c3968a85SDaniel Walter 
38318c5b83f0SRoopa Prabhu 	return rt;
38321da177e4SLinus Torvalds out:
383393531c67SDavid Ahern 	fib6_info_release(rt);
38348c5b83f0SRoopa Prabhu 	return ERR_PTR(err);
3835821bbf79SCoco Li out_free:
3836821bbf79SCoco Li 	ip_fib_metrics_put(rt->fib6_metrics);
3837821bbf79SCoco Li 	kfree(rt);
3838821bbf79SCoco Li 	return ERR_PTR(err);
38396b9ea5a6SRoopa Prabhu }
38406b9ea5a6SRoopa Prabhu 
ip6_route_add(struct fib6_config * cfg,gfp_t gfp_flags,struct netlink_ext_ack * extack)3841acb54e3cSDavid Ahern int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
3842333c4301SDavid Ahern 		  struct netlink_ext_ack *extack)
38436b9ea5a6SRoopa Prabhu {
38448d1c802bSDavid Ahern 	struct fib6_info *rt;
38456b9ea5a6SRoopa Prabhu 	int err;
38466b9ea5a6SRoopa Prabhu 
3847acb54e3cSDavid Ahern 	rt = ip6_route_info_create(cfg, gfp_flags, extack);
3848d4ead6b3SDavid Ahern 	if (IS_ERR(rt))
3849d4ead6b3SDavid Ahern 		return PTR_ERR(rt);
38506b9ea5a6SRoopa Prabhu 
3851d4ead6b3SDavid Ahern 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
385293531c67SDavid Ahern 	fib6_info_release(rt);
38536b9ea5a6SRoopa Prabhu 
38541da177e4SLinus Torvalds 	return err;
38551da177e4SLinus Torvalds }
38561da177e4SLinus Torvalds 
__ip6_del_rt(struct fib6_info * rt,struct nl_info * info)38578d1c802bSDavid Ahern static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
38581da177e4SLinus Torvalds {
3859afb1d4b5SDavid Ahern 	struct net *net = info->nl_net;
3860c71099acSThomas Graf 	struct fib6_table *table;
3861afb1d4b5SDavid Ahern 	int err;
38621da177e4SLinus Torvalds 
3863421842edSDavid Ahern 	if (rt == net->ipv6.fib6_null_entry) {
38646825a26cSGao feng 		err = -ENOENT;
38656825a26cSGao feng 		goto out;
38666825a26cSGao feng 	}
38676c813a72SPatrick McHardy 
386893c2fb25SDavid Ahern 	table = rt->fib6_table;
386966f5d6ceSWei Wang 	spin_lock_bh(&table->tb6_lock);
387086872cb5SThomas Graf 	err = fib6_del(rt, info);
387166f5d6ceSWei Wang 	spin_unlock_bh(&table->tb6_lock);
38721da177e4SLinus Torvalds 
38736825a26cSGao feng out:
387493531c67SDavid Ahern 	fib6_info_release(rt);
38751da177e4SLinus Torvalds 	return err;
38761da177e4SLinus Torvalds }
38771da177e4SLinus Torvalds 
ip6_del_rt(struct net * net,struct fib6_info * rt,bool skip_notify)387811dd74b3SRoopa Prabhu int ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
3879e0a1ad73SThomas Graf {
388011dd74b3SRoopa Prabhu 	struct nl_info info = {
388111dd74b3SRoopa Prabhu 		.nl_net = net,
388211dd74b3SRoopa Prabhu 		.skip_notify = skip_notify
388311dd74b3SRoopa Prabhu 	};
3884afb1d4b5SDavid Ahern 
3885528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
3886e0a1ad73SThomas Graf }
3887e0a1ad73SThomas Graf 
__ip6_del_rt_siblings(struct fib6_info * rt,struct fib6_config * cfg)38888d1c802bSDavid Ahern static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
38890ae81335SDavid Ahern {
38900ae81335SDavid Ahern 	struct nl_info *info = &cfg->fc_nlinfo;
3891e3330039SWANG Cong 	struct net *net = info->nl_net;
389216a16cd3SDavid Ahern 	struct sk_buff *skb = NULL;
38930ae81335SDavid Ahern 	struct fib6_table *table;
3894e3330039SWANG Cong 	int err = -ENOENT;
38950ae81335SDavid Ahern 
3896421842edSDavid Ahern 	if (rt == net->ipv6.fib6_null_entry)
3897e3330039SWANG Cong 		goto out_put;
389893c2fb25SDavid Ahern 	table = rt->fib6_table;
389966f5d6ceSWei Wang 	spin_lock_bh(&table->tb6_lock);
39000ae81335SDavid Ahern 
390193c2fb25SDavid Ahern 	if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
39028d1c802bSDavid Ahern 		struct fib6_info *sibling, *next_sibling;
39030284696bSIdo Schimmel 		struct fib6_node *fn;
39040ae81335SDavid Ahern 
390516a16cd3SDavid Ahern 		/* prefer to send a single notification with all hops */
390616a16cd3SDavid Ahern 		skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
390716a16cd3SDavid Ahern 		if (skb) {
390816a16cd3SDavid Ahern 			u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
390916a16cd3SDavid Ahern 
3910d4ead6b3SDavid Ahern 			if (rt6_fill_node(net, skb, rt, NULL,
391116a16cd3SDavid Ahern 					  NULL, NULL, 0, RTM_DELROUTE,
391216a16cd3SDavid Ahern 					  info->portid, seq, 0) < 0) {
391316a16cd3SDavid Ahern 				kfree_skb(skb);
391416a16cd3SDavid Ahern 				skb = NULL;
391516a16cd3SDavid Ahern 			} else
391616a16cd3SDavid Ahern 				info->skip_notify = 1;
391716a16cd3SDavid Ahern 		}
391816a16cd3SDavid Ahern 
39190284696bSIdo Schimmel 		/* 'rt' points to the first sibling route. If it is not the
39200284696bSIdo Schimmel 		 * leaf, then we do not need to send a notification. Otherwise,
39210284696bSIdo Schimmel 		 * we need to check if the last sibling has a next route or not
39220284696bSIdo Schimmel 		 * and emit a replace or delete notification, respectively.
39230284696bSIdo Schimmel 		 */
39242881fd61SIdo Schimmel 		info->skip_notify_kernel = 1;
39250284696bSIdo Schimmel 		fn = rcu_dereference_protected(rt->fib6_node,
39260284696bSIdo Schimmel 					    lockdep_is_held(&table->tb6_lock));
39270284696bSIdo Schimmel 		if (rcu_access_pointer(fn->leaf) == rt) {
39280284696bSIdo Schimmel 			struct fib6_info *last_sibling, *replace_rt;
39290284696bSIdo Schimmel 
39300284696bSIdo Schimmel 			last_sibling = list_last_entry(&rt->fib6_siblings,
39310284696bSIdo Schimmel 						       struct fib6_info,
39320284696bSIdo Schimmel 						       fib6_siblings);
39330284696bSIdo Schimmel 			replace_rt = rcu_dereference_protected(
39340284696bSIdo Schimmel 					    last_sibling->fib6_next,
39350284696bSIdo Schimmel 					    lockdep_is_held(&table->tb6_lock));
39360284696bSIdo Schimmel 			if (replace_rt)
39370284696bSIdo Schimmel 				call_fib6_entry_notifiers_replace(net,
39380284696bSIdo Schimmel 								  replace_rt);
39390284696bSIdo Schimmel 			else
39400284696bSIdo Schimmel 				call_fib6_multipath_entry_notifiers(net,
3941caafb250SIdo Schimmel 						       FIB_EVENT_ENTRY_DEL,
39420284696bSIdo Schimmel 						       rt, rt->fib6_nsiblings,
39430284696bSIdo Schimmel 						       NULL);
39440284696bSIdo Schimmel 		}
39450ae81335SDavid Ahern 		list_for_each_entry_safe(sibling, next_sibling,
394693c2fb25SDavid Ahern 					 &rt->fib6_siblings,
394793c2fb25SDavid Ahern 					 fib6_siblings) {
39480ae81335SDavid Ahern 			err = fib6_del(sibling, info);
39490ae81335SDavid Ahern 			if (err)
3950e3330039SWANG Cong 				goto out_unlock;
39510ae81335SDavid Ahern 		}
39520ae81335SDavid Ahern 	}
39530ae81335SDavid Ahern 
39540ae81335SDavid Ahern 	err = fib6_del(rt, info);
3955e3330039SWANG Cong out_unlock:
395666f5d6ceSWei Wang 	spin_unlock_bh(&table->tb6_lock);
3957e3330039SWANG Cong out_put:
395893531c67SDavid Ahern 	fib6_info_release(rt);
395916a16cd3SDavid Ahern 
396016a16cd3SDavid Ahern 	if (skb) {
3961e3330039SWANG Cong 		rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
396216a16cd3SDavid Ahern 			    info->nlh, gfp_any());
396316a16cd3SDavid Ahern 	}
39640ae81335SDavid Ahern 	return err;
39650ae81335SDavid Ahern }
39660ae81335SDavid Ahern 
__ip6_del_cached_rt(struct rt6_info * rt,struct fib6_config * cfg)39670fa6efc5SDavid Ahern static int __ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
396823fb93a4SDavid Ahern {
396923fb93a4SDavid Ahern 	int rc = -ESRCH;
397023fb93a4SDavid Ahern 
397123fb93a4SDavid Ahern 	if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
397223fb93a4SDavid Ahern 		goto out;
397323fb93a4SDavid Ahern 
397423fb93a4SDavid Ahern 	if (cfg->fc_flags & RTF_GATEWAY &&
397523fb93a4SDavid Ahern 	    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
397623fb93a4SDavid Ahern 		goto out;
3977761f6026SXin Long 
397823fb93a4SDavid Ahern 	rc = rt6_remove_exception_rt(rt);
397923fb93a4SDavid Ahern out:
398023fb93a4SDavid Ahern 	return rc;
398123fb93a4SDavid Ahern }
398223fb93a4SDavid Ahern 
ip6_del_cached_rt(struct fib6_config * cfg,struct fib6_info * rt,struct fib6_nh * nh)39830fa6efc5SDavid Ahern static int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt,
39840fa6efc5SDavid Ahern 			     struct fib6_nh *nh)
39850fa6efc5SDavid Ahern {
39860fa6efc5SDavid Ahern 	struct fib6_result res = {
39870fa6efc5SDavid Ahern 		.f6i = rt,
39880fa6efc5SDavid Ahern 		.nh = nh,
39890fa6efc5SDavid Ahern 	};
39900fa6efc5SDavid Ahern 	struct rt6_info *rt_cache;
39910fa6efc5SDavid Ahern 
39920fa6efc5SDavid Ahern 	rt_cache = rt6_find_cached_rt(&res, &cfg->fc_dst, &cfg->fc_src);
39930fa6efc5SDavid Ahern 	if (rt_cache)
39940fa6efc5SDavid Ahern 		return __ip6_del_cached_rt(rt_cache, cfg);
39950fa6efc5SDavid Ahern 
39960fa6efc5SDavid Ahern 	return 0;
39970fa6efc5SDavid Ahern }
39980fa6efc5SDavid Ahern 
39995b98324eSDavid Ahern struct fib6_nh_del_cached_rt_arg {
40005b98324eSDavid Ahern 	struct fib6_config *cfg;
40015b98324eSDavid Ahern 	struct fib6_info *f6i;
40025b98324eSDavid Ahern };
40035b98324eSDavid Ahern 
fib6_nh_del_cached_rt(struct fib6_nh * nh,void * _arg)40045b98324eSDavid Ahern static int fib6_nh_del_cached_rt(struct fib6_nh *nh, void *_arg)
40055b98324eSDavid Ahern {
40065b98324eSDavid Ahern 	struct fib6_nh_del_cached_rt_arg *arg = _arg;
40075b98324eSDavid Ahern 	int rc;
40085b98324eSDavid Ahern 
40095b98324eSDavid Ahern 	rc = ip6_del_cached_rt(arg->cfg, arg->f6i, nh);
40105b98324eSDavid Ahern 	return rc != -ESRCH ? rc : 0;
40115b98324eSDavid Ahern }
40125b98324eSDavid Ahern 
ip6_del_cached_rt_nh(struct fib6_config * cfg,struct fib6_info * f6i)40135b98324eSDavid Ahern static int ip6_del_cached_rt_nh(struct fib6_config *cfg, struct fib6_info *f6i)
40145b98324eSDavid Ahern {
40155b98324eSDavid Ahern 	struct fib6_nh_del_cached_rt_arg arg = {
40165b98324eSDavid Ahern 		.cfg = cfg,
40175b98324eSDavid Ahern 		.f6i = f6i
40185b98324eSDavid Ahern 	};
40195b98324eSDavid Ahern 
40205b98324eSDavid Ahern 	return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_del_cached_rt, &arg);
40215b98324eSDavid Ahern }
40225b98324eSDavid Ahern 
ip6_route_del(struct fib6_config * cfg,struct netlink_ext_ack * extack)4023333c4301SDavid Ahern static int ip6_route_del(struct fib6_config *cfg,
4024333c4301SDavid Ahern 			 struct netlink_ext_ack *extack)
40251da177e4SLinus Torvalds {
4026c71099acSThomas Graf 	struct fib6_table *table;
40278d1c802bSDavid Ahern 	struct fib6_info *rt;
40281da177e4SLinus Torvalds 	struct fib6_node *fn;
40291da177e4SLinus Torvalds 	int err = -ESRCH;
40301da177e4SLinus Torvalds 
40315578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
4032d5d531cbSDavid Ahern 	if (!table) {
4033d5d531cbSDavid Ahern 		NL_SET_ERR_MSG(extack, "FIB table does not exist");
4034c71099acSThomas Graf 		return err;
4035d5d531cbSDavid Ahern 	}
40361da177e4SLinus Torvalds 
403766f5d6ceSWei Wang 	rcu_read_lock();
4038c71099acSThomas Graf 
4039c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
404086872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
404138fbeeeeSWei Wang 			 &cfg->fc_src, cfg->fc_src_len,
40422b760fcfSWei Wang 			 !(cfg->fc_flags & RTF_CACHE));
40431da177e4SLinus Torvalds 
40441da177e4SLinus Torvalds 	if (fn) {
404566f5d6ceSWei Wang 		for_each_fib6_node_rt_rcu(fn) {
4046ad1601aeSDavid Ahern 			struct fib6_nh *nh;
4047ad1601aeSDavid Ahern 
40483401bfb1SStefano Brivio 			if (rt->nh && cfg->fc_nh_id &&
40493401bfb1SStefano Brivio 			    rt->nh->id != cfg->fc_nh_id)
40505b98324eSDavid Ahern 				continue;
405123fb93a4SDavid Ahern 
40525b98324eSDavid Ahern 			if (cfg->fc_flags & RTF_CACHE) {
40535b98324eSDavid Ahern 				int rc = 0;
40545b98324eSDavid Ahern 
40555b98324eSDavid Ahern 				if (rt->nh) {
40565b98324eSDavid Ahern 					rc = ip6_del_cached_rt_nh(cfg, rt);
40575b98324eSDavid Ahern 				} else if (cfg->fc_nh_id) {
40585b98324eSDavid Ahern 					continue;
40595b98324eSDavid Ahern 				} else {
40605b98324eSDavid Ahern 					nh = rt->fib6_nh;
40610fa6efc5SDavid Ahern 					rc = ip6_del_cached_rt(cfg, rt, nh);
40625b98324eSDavid Ahern 				}
40639e575010SEric Dumazet 				if (rc != -ESRCH) {
40649e575010SEric Dumazet 					rcu_read_unlock();
406523fb93a4SDavid Ahern 					return rc;
406623fb93a4SDavid Ahern 				}
40671f56a01fSMartin KaFai Lau 				continue;
40682b760fcfSWei Wang 			}
4069ad1601aeSDavid Ahern 
40705b98324eSDavid Ahern 			if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
40715b98324eSDavid Ahern 				continue;
40725b98324eSDavid Ahern 			if (cfg->fc_protocol &&
40735b98324eSDavid Ahern 			    cfg->fc_protocol != rt->fib6_protocol)
40745b98324eSDavid Ahern 				continue;
40755b98324eSDavid Ahern 
40765b98324eSDavid Ahern 			if (rt->nh) {
40775b98324eSDavid Ahern 				if (!fib6_info_hold_safe(rt))
40785b98324eSDavid Ahern 					continue;
40795b98324eSDavid Ahern 				rcu_read_unlock();
40805b98324eSDavid Ahern 
40815b98324eSDavid Ahern 				return __ip6_del_rt(rt, &cfg->fc_nlinfo);
40825b98324eSDavid Ahern 			}
40835b98324eSDavid Ahern 			if (cfg->fc_nh_id)
40845b98324eSDavid Ahern 				continue;
40855b98324eSDavid Ahern 
40865b98324eSDavid Ahern 			nh = rt->fib6_nh;
408786872cb5SThomas Graf 			if (cfg->fc_ifindex &&
4088ad1601aeSDavid Ahern 			    (!nh->fib_nh_dev ||
4089ad1601aeSDavid Ahern 			     nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
40901da177e4SLinus Torvalds 				continue;
409186872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
4092ad1601aeSDavid Ahern 			    !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
40931da177e4SLinus Torvalds 				continue;
4094e873e4b9SWei Wang 			if (!fib6_info_hold_safe(rt))
4095e873e4b9SWei Wang 				continue;
409666f5d6ceSWei Wang 			rcu_read_unlock();
40971da177e4SLinus Torvalds 
40980ae81335SDavid Ahern 			/* if gateway was specified only delete the one hop */
40990ae81335SDavid Ahern 			if (cfg->fc_flags & RTF_GATEWAY)
410086872cb5SThomas Graf 				return __ip6_del_rt(rt, &cfg->fc_nlinfo);
41010ae81335SDavid Ahern 
41020ae81335SDavid Ahern 			return __ip6_del_rt_siblings(rt, cfg);
41031da177e4SLinus Torvalds 		}
41041da177e4SLinus Torvalds 	}
410566f5d6ceSWei Wang 	rcu_read_unlock();
41061da177e4SLinus Torvalds 
41071da177e4SLinus Torvalds 	return err;
41081da177e4SLinus Torvalds }
41091da177e4SLinus Torvalds 
rt6_do_redirect(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb)41106700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
4111a6279458SYOSHIFUJI Hideaki {
4112a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
4113e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
411485bd05deSDavid Ahern 	struct fib6_result res = {};
4115e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
4116e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
4117e8599ff4SDavid S. Miller 	struct neighbour *neigh;
411871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
41196e157b6aSDavid S. Miller 	int optlen, on_link;
41206e157b6aSDavid S. Miller 	u8 *lladdr;
4121e8599ff4SDavid S. Miller 
412229a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
412371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
4124e8599ff4SDavid S. Miller 
4125e8599ff4SDavid S. Miller 	if (optlen < 0) {
41266e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
4127e8599ff4SDavid S. Miller 		return;
4128e8599ff4SDavid S. Miller 	}
4129e8599ff4SDavid S. Miller 
413071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
4131e8599ff4SDavid S. Miller 
413271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
41336e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
4134e8599ff4SDavid S. Miller 		return;
4135e8599ff4SDavid S. Miller 	}
4136e8599ff4SDavid S. Miller 
41376e157b6aSDavid S. Miller 	on_link = 0;
413871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
4139e8599ff4SDavid S. Miller 		on_link = 1;
414071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
4141e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
41426e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
4143e8599ff4SDavid S. Miller 		return;
4144e8599ff4SDavid S. Miller 	}
4145e8599ff4SDavid S. Miller 
4146e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
4147e8599ff4SDavid S. Miller 	if (!in6_dev)
4148e8599ff4SDavid S. Miller 		return;
4149e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
4150e8599ff4SDavid S. Miller 		return;
4151e8599ff4SDavid S. Miller 
4152e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
4153e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
4154e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
4155e8599ff4SDavid S. Miller 	 */
4156e8599ff4SDavid S. Miller 
4157f997c55cSAlexander Aring 	if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
4158e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
4159e8599ff4SDavid S. Miller 		return;
4160e8599ff4SDavid S. Miller 	}
41616e157b6aSDavid S. Miller 
41626e157b6aSDavid S. Miller 	lladdr = NULL;
4163e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
4164e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
4165e8599ff4SDavid S. Miller 					     skb->dev);
4166e8599ff4SDavid S. Miller 		if (!lladdr) {
4167e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
4168e8599ff4SDavid S. Miller 			return;
4169e8599ff4SDavid S. Miller 		}
4170e8599ff4SDavid S. Miller 	}
4171e8599ff4SDavid S. Miller 
4172797a4c1fSEric Dumazet 	rt = dst_rt6_info(dst);
4173ec13ad1dSMatthias Schiffer 	if (rt->rt6i_flags & RTF_REJECT) {
41746e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
41756e157b6aSDavid S. Miller 		return;
41766e157b6aSDavid S. Miller 	}
41776e157b6aSDavid S. Miller 
41786e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
41796e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
41806e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
41816e157b6aSDavid S. Miller 	 */
41820dec879fSJulian Anastasov 	dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
41836e157b6aSDavid S. Miller 
418471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
4185e8599ff4SDavid S. Miller 	if (!neigh)
4186e8599ff4SDavid S. Miller 		return;
4187e8599ff4SDavid S. Miller 
41881da177e4SLinus Torvalds 	/*
41891da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
41901da177e4SLinus Torvalds 	 */
41911da177e4SLinus Torvalds 
4192f997c55cSAlexander Aring 	ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
41931da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
41941da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
41951da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
4196f997c55cSAlexander Aring 				     NEIGH_UPDATE_F_ISROUTER)),
4197f997c55cSAlexander Aring 		     NDISC_REDIRECT, &ndopts);
41981da177e4SLinus Torvalds 
41994d85cd0cSDavid Ahern 	rcu_read_lock();
420085bd05deSDavid Ahern 	res.f6i = rcu_dereference(rt->from);
4201ff24e498SDavid S. Miller 	if (!res.f6i)
4202886b7a50SMartin KaFai Lau 		goto out;
42038a14e46fSDavid Ahern 
420449d5b8efSDavid Ahern 	if (res.f6i->nh) {
420549d5b8efSDavid Ahern 		struct fib6_nh_match_arg arg = {
420649d5b8efSDavid Ahern 			.dev = dst->dev,
420749d5b8efSDavid Ahern 			.gw = &rt->rt6i_gateway,
420849d5b8efSDavid Ahern 		};
420949d5b8efSDavid Ahern 
421049d5b8efSDavid Ahern 		nexthop_for_each_fib6_nh(res.f6i->nh,
421149d5b8efSDavid Ahern 					 fib6_nh_find_match, &arg);
421249d5b8efSDavid Ahern 
421349d5b8efSDavid Ahern 		/* fib6_info uses a nexthop that does not have fib6_nh
421449d5b8efSDavid Ahern 		 * using the dst->dev. Should be impossible
421549d5b8efSDavid Ahern 		 */
421649d5b8efSDavid Ahern 		if (!arg.match)
421749d5b8efSDavid Ahern 			goto out;
421849d5b8efSDavid Ahern 		res.nh = arg.match;
421949d5b8efSDavid Ahern 	} else {
42201cf844c7SDavid Ahern 		res.nh = res.f6i->fib6_nh;
422149d5b8efSDavid Ahern 	}
422249d5b8efSDavid Ahern 
42237d21fec9SDavid Ahern 	res.fib6_flags = res.f6i->fib6_flags;
42247d21fec9SDavid Ahern 	res.fib6_type = res.f6i->fib6_type;
422585bd05deSDavid Ahern 	nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
422638308473SDavid S. Miller 	if (!nrt)
42271da177e4SLinus Torvalds 		goto out;
42281da177e4SLinus Torvalds 
42291da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
42301da177e4SLinus Torvalds 	if (on_link)
42311da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
42321da177e4SLinus Torvalds 
42334e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
42341da177e4SLinus Torvalds 
4235886b7a50SMartin KaFai Lau 	/* rt6_insert_exception() will take care of duplicated exceptions */
42365012f0a5SDavid Ahern 	if (rt6_insert_exception(nrt, &res)) {
42372b760fcfSWei Wang 		dst_release_immediate(&nrt->dst);
42382b760fcfSWei Wang 		goto out;
42392b760fcfSWei Wang 	}
42401da177e4SLinus Torvalds 
4241d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
4242d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
424371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
424460592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
42458d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
42468d71740cSTom Tucker 
42471da177e4SLinus Torvalds out:
4248886b7a50SMartin KaFai Lau 	rcu_read_unlock();
4249e8599ff4SDavid S. Miller 	neigh_release(neigh);
42506e157b6aSDavid S. Miller }
42516e157b6aSDavid S. Miller 
425270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
rt6_get_route_info(struct net * net,const struct in6_addr * prefix,int prefixlen,const struct in6_addr * gwaddr,struct net_device * dev)42538d1c802bSDavid Ahern static struct fib6_info *rt6_get_route_info(struct net *net,
4254b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
4255830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
4256830218c1SDavid Ahern 					   struct net_device *dev)
425770ceb4f5SYOSHIFUJI Hideaki {
4258830218c1SDavid Ahern 	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
4259830218c1SDavid Ahern 	int ifindex = dev->ifindex;
426070ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
42618d1c802bSDavid Ahern 	struct fib6_info *rt = NULL;
4262c71099acSThomas Graf 	struct fib6_table *table;
426370ceb4f5SYOSHIFUJI Hideaki 
4264830218c1SDavid Ahern 	table = fib6_get_table(net, tb_id);
426538308473SDavid S. Miller 	if (!table)
4266c71099acSThomas Graf 		return NULL;
4267c71099acSThomas Graf 
426866f5d6ceSWei Wang 	rcu_read_lock();
426938fbeeeeSWei Wang 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
427070ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
427170ceb4f5SYOSHIFUJI Hideaki 		goto out;
427270ceb4f5SYOSHIFUJI Hideaki 
427366f5d6ceSWei Wang 	for_each_fib6_node_rt_rcu(fn) {
4274f88d8ea6SDavid Ahern 		/* these routes do not use nexthops */
4275f88d8ea6SDavid Ahern 		if (rt->nh)
4276f88d8ea6SDavid Ahern 			continue;
42771cf844c7SDavid Ahern 		if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex)
427870ceb4f5SYOSHIFUJI Hideaki 			continue;
42792b2450caSDavid Ahern 		if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
42801cf844c7SDavid Ahern 		    !rt->fib6_nh->fib_nh_gw_family)
428170ceb4f5SYOSHIFUJI Hideaki 			continue;
42821cf844c7SDavid Ahern 		if (!ipv6_addr_equal(&rt->fib6_nh->fib_nh_gw6, gwaddr))
428370ceb4f5SYOSHIFUJI Hideaki 			continue;
4284e873e4b9SWei Wang 		if (!fib6_info_hold_safe(rt))
4285e873e4b9SWei Wang 			continue;
428670ceb4f5SYOSHIFUJI Hideaki 		break;
428770ceb4f5SYOSHIFUJI Hideaki 	}
428870ceb4f5SYOSHIFUJI Hideaki out:
428966f5d6ceSWei Wang 	rcu_read_unlock();
429070ceb4f5SYOSHIFUJI Hideaki 	return rt;
429170ceb4f5SYOSHIFUJI Hideaki }
429270ceb4f5SYOSHIFUJI Hideaki 
rt6_add_route_info(struct net * net,const struct in6_addr * prefix,int prefixlen,const struct in6_addr * gwaddr,struct net_device * dev,unsigned int pref)42938d1c802bSDavid Ahern static struct fib6_info *rt6_add_route_info(struct net *net,
4294b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
4295830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
4296830218c1SDavid Ahern 					   struct net_device *dev,
429795c96174SEric Dumazet 					   unsigned int pref)
429870ceb4f5SYOSHIFUJI Hideaki {
429986872cb5SThomas Graf 	struct fib6_config cfg = {
4300238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
4301830218c1SDavid Ahern 		.fc_ifindex	= dev->ifindex,
430286872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
430386872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
430486872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
4305b91d5329SXin Long 		.fc_protocol = RTPROT_RA,
4306e8478e80SDavid Ahern 		.fc_type = RTN_UNICAST,
430715e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
4308efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
4309efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
431086872cb5SThomas Graf 	};
431170ceb4f5SYOSHIFUJI Hideaki 
431291b2c9a0SXu Wang 	cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
43134e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
43144e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
431586872cb5SThomas Graf 
4316e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
4317e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
431886872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
431970ceb4f5SYOSHIFUJI Hideaki 
4320acb54e3cSDavid Ahern 	ip6_route_add(&cfg, GFP_ATOMIC, NULL);
432170ceb4f5SYOSHIFUJI Hideaki 
4322830218c1SDavid Ahern 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
432370ceb4f5SYOSHIFUJI Hideaki }
432470ceb4f5SYOSHIFUJI Hideaki #endif
432570ceb4f5SYOSHIFUJI Hideaki 
rt6_get_dflt_router(struct net * net,const struct in6_addr * addr,struct net_device * dev)43268d1c802bSDavid Ahern struct fib6_info *rt6_get_dflt_router(struct net *net,
4327afb1d4b5SDavid Ahern 				     const struct in6_addr *addr,
4328afb1d4b5SDavid Ahern 				     struct net_device *dev)
43291da177e4SLinus Torvalds {
4330830218c1SDavid Ahern 	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
43318d1c802bSDavid Ahern 	struct fib6_info *rt;
4332c71099acSThomas Graf 	struct fib6_table *table;
43331da177e4SLinus Torvalds 
4334afb1d4b5SDavid Ahern 	table = fib6_get_table(net, tb_id);
433538308473SDavid S. Miller 	if (!table)
4336c71099acSThomas Graf 		return NULL;
43371da177e4SLinus Torvalds 
433866f5d6ceSWei Wang 	rcu_read_lock();
433966f5d6ceSWei Wang 	for_each_fib6_node_rt_rcu(&table->tb6_root) {
4340f88d8ea6SDavid Ahern 		struct fib6_nh *nh;
4341ad1601aeSDavid Ahern 
4342f88d8ea6SDavid Ahern 		/* RA routes do not use nexthops */
4343f88d8ea6SDavid Ahern 		if (rt->nh)
4344f88d8ea6SDavid Ahern 			continue;
4345f88d8ea6SDavid Ahern 
4346f88d8ea6SDavid Ahern 		nh = rt->fib6_nh;
4347ad1601aeSDavid Ahern 		if (dev == nh->fib_nh_dev &&
434893c2fb25SDavid Ahern 		    ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
4349ad1601aeSDavid Ahern 		    ipv6_addr_equal(&nh->fib_nh_gw6, addr))
43501da177e4SLinus Torvalds 			break;
43511da177e4SLinus Torvalds 	}
4352e873e4b9SWei Wang 	if (rt && !fib6_info_hold_safe(rt))
4353e873e4b9SWei Wang 		rt = NULL;
435466f5d6ceSWei Wang 	rcu_read_unlock();
43551da177e4SLinus Torvalds 	return rt;
43561da177e4SLinus Torvalds }
43571da177e4SLinus Torvalds 
rt6_add_dflt_router(struct net * net,const struct in6_addr * gwaddr,struct net_device * dev,unsigned int pref,u32 defrtr_usr_metric)43588d1c802bSDavid Ahern struct fib6_info *rt6_add_dflt_router(struct net *net,
4359afb1d4b5SDavid Ahern 				     const struct in6_addr *gwaddr,
4360ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
43616b2e04bcSPraveen Chaudhary 				     unsigned int pref,
43626b2e04bcSPraveen Chaudhary 				     u32 defrtr_usr_metric)
43631da177e4SLinus Torvalds {
436486872cb5SThomas Graf 	struct fib6_config cfg = {
4365ca254490SDavid Ahern 		.fc_table	= l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
43666b2e04bcSPraveen Chaudhary 		.fc_metric	= defrtr_usr_metric,
436786872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
436886872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
436986872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
4370b91d5329SXin Long 		.fc_protocol = RTPROT_RA,
4371e8478e80SDavid Ahern 		.fc_type = RTN_UNICAST,
437215e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
43735578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
4374afb1d4b5SDavid Ahern 		.fc_nlinfo.nl_net = net,
437586872cb5SThomas Graf 	};
43761da177e4SLinus Torvalds 
43774e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
43781da177e4SLinus Torvalds 
4379acb54e3cSDavid Ahern 	if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
4380830218c1SDavid Ahern 		struct fib6_table *table;
4381830218c1SDavid Ahern 
4382830218c1SDavid Ahern 		table = fib6_get_table(dev_net(dev), cfg.fc_table);
4383830218c1SDavid Ahern 		if (table)
4384830218c1SDavid Ahern 			table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
4385830218c1SDavid Ahern 	}
43861da177e4SLinus Torvalds 
4387afb1d4b5SDavid Ahern 	return rt6_get_dflt_router(net, gwaddr, dev);
43881da177e4SLinus Torvalds }
43891da177e4SLinus Torvalds 
__rt6_purge_dflt_routers(struct net * net,struct fib6_table * table)4390afb1d4b5SDavid Ahern static void __rt6_purge_dflt_routers(struct net *net,
4391afb1d4b5SDavid Ahern 				     struct fib6_table *table)
43921da177e4SLinus Torvalds {
43938d1c802bSDavid Ahern 	struct fib6_info *rt;
43941da177e4SLinus Torvalds 
43951da177e4SLinus Torvalds restart:
439666f5d6ceSWei Wang 	rcu_read_lock();
439766f5d6ceSWei Wang 	for_each_fib6_node_rt_rcu(&table->tb6_root) {
4398dcd1f572SDavid Ahern 		struct net_device *dev = fib6_info_nh_dev(rt);
4399dcd1f572SDavid Ahern 		struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
4400dcd1f572SDavid Ahern 
440193c2fb25SDavid Ahern 		if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
4402e873e4b9SWei Wang 		    (!idev || idev->cnf.accept_ra != 2) &&
4403e873e4b9SWei Wang 		    fib6_info_hold_safe(rt)) {
440466f5d6ceSWei Wang 			rcu_read_unlock();
440511dd74b3SRoopa Prabhu 			ip6_del_rt(net, rt, false);
44061da177e4SLinus Torvalds 			goto restart;
44071da177e4SLinus Torvalds 		}
44081da177e4SLinus Torvalds 	}
440966f5d6ceSWei Wang 	rcu_read_unlock();
4410830218c1SDavid Ahern 
4411830218c1SDavid Ahern 	table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
4412830218c1SDavid Ahern }
4413830218c1SDavid Ahern 
rt6_purge_dflt_routers(struct net * net)4414830218c1SDavid Ahern void rt6_purge_dflt_routers(struct net *net)
4415830218c1SDavid Ahern {
4416830218c1SDavid Ahern 	struct fib6_table *table;
4417830218c1SDavid Ahern 	struct hlist_head *head;
4418830218c1SDavid Ahern 	unsigned int h;
4419830218c1SDavid Ahern 
4420830218c1SDavid Ahern 	rcu_read_lock();
4421830218c1SDavid Ahern 
4422830218c1SDavid Ahern 	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
4423830218c1SDavid Ahern 		head = &net->ipv6.fib_table_hash[h];
4424830218c1SDavid Ahern 		hlist_for_each_entry_rcu(table, head, tb6_hlist) {
4425830218c1SDavid Ahern 			if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
4426afb1d4b5SDavid Ahern 				__rt6_purge_dflt_routers(net, table);
4427830218c1SDavid Ahern 		}
4428830218c1SDavid Ahern 	}
4429830218c1SDavid Ahern 
4430830218c1SDavid Ahern 	rcu_read_unlock();
44311da177e4SLinus Torvalds }
44321da177e4SLinus Torvalds 
rtmsg_to_fib6_config(struct net * net,struct in6_rtmsg * rtmsg,struct fib6_config * cfg)44335578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
44345578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
443586872cb5SThomas Graf 				 struct fib6_config *cfg)
443686872cb5SThomas Graf {
44378823a3acSMaciej Żenczykowski 	*cfg = (struct fib6_config){
44388823a3acSMaciej Żenczykowski 		.fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
44398823a3acSMaciej Żenczykowski 			 : RT6_TABLE_MAIN,
44408823a3acSMaciej Żenczykowski 		.fc_ifindex = rtmsg->rtmsg_ifindex,
4441400b8fb6Sxu xin 		.fc_metric = rtmsg->rtmsg_metric,
44428823a3acSMaciej Żenczykowski 		.fc_expires = rtmsg->rtmsg_info,
44438823a3acSMaciej Żenczykowski 		.fc_dst_len = rtmsg->rtmsg_dst_len,
44448823a3acSMaciej Żenczykowski 		.fc_src_len = rtmsg->rtmsg_src_len,
44458823a3acSMaciej Żenczykowski 		.fc_flags = rtmsg->rtmsg_flags,
44468823a3acSMaciej Żenczykowski 		.fc_type = rtmsg->rtmsg_type,
444786872cb5SThomas Graf 
44488823a3acSMaciej Żenczykowski 		.fc_nlinfo.nl_net = net,
444986872cb5SThomas Graf 
44508823a3acSMaciej Żenczykowski 		.fc_dst = rtmsg->rtmsg_dst,
44518823a3acSMaciej Żenczykowski 		.fc_src = rtmsg->rtmsg_src,
44528823a3acSMaciej Żenczykowski 		.fc_gateway = rtmsg->rtmsg_gateway,
44538823a3acSMaciej Żenczykowski 	};
445486872cb5SThomas Graf }
445586872cb5SThomas Graf 
ipv6_route_ioctl(struct net * net,unsigned int cmd,struct in6_rtmsg * rtmsg)44567c1552daSChristoph Hellwig int ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg)
44571da177e4SLinus Torvalds {
445886872cb5SThomas Graf 	struct fib6_config cfg;
44591da177e4SLinus Torvalds 	int err;
44601da177e4SLinus Torvalds 
44617c1552daSChristoph Hellwig 	if (cmd != SIOCADDRT && cmd != SIOCDELRT)
44627c1552daSChristoph Hellwig 		return -EINVAL;
4463af31f412SEric W. Biederman 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
44641da177e4SLinus Torvalds 		return -EPERM;
44651da177e4SLinus Torvalds 
44667c1552daSChristoph Hellwig 	rtmsg_to_fib6_config(net, rtmsg, &cfg);
446786872cb5SThomas Graf 
44681da177e4SLinus Torvalds 	rtnl_lock();
44691da177e4SLinus Torvalds 	switch (cmd) {
44701da177e4SLinus Torvalds 	case SIOCADDRT:
4471400b8fb6Sxu xin 		/* Only do the default setting of fc_metric in route adding */
4472400b8fb6Sxu xin 		if (cfg.fc_metric == 0)
4473400b8fb6Sxu xin 			cfg.fc_metric = IP6_RT_PRIO_USER;
4474acb54e3cSDavid Ahern 		err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
44751da177e4SLinus Torvalds 		break;
44761da177e4SLinus Torvalds 	case SIOCDELRT:
4477333c4301SDavid Ahern 		err = ip6_route_del(&cfg, NULL);
44781da177e4SLinus Torvalds 		break;
44791da177e4SLinus Torvalds 	}
44801da177e4SLinus Torvalds 	rtnl_unlock();
44811da177e4SLinus Torvalds 	return err;
44823ff50b79SStephen Hemminger }
44831da177e4SLinus Torvalds 
44841da177e4SLinus Torvalds /*
44851da177e4SLinus Torvalds  *	Drop the packet on the floor
44861da177e4SLinus Torvalds  */
44871da177e4SLinus Torvalds 
ip6_pkt_drop(struct sk_buff * skb,u8 code,int ipstats_mib_noroutes)4488d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
44891da177e4SLinus Torvalds {
4490adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
44911d3fd8a1SStephen Suryaputra 	struct net *net = dev_net(dst->dev);
44921d3fd8a1SStephen Suryaputra 	struct inet6_dev *idev;
44933ae42cc8SMenglong Dong 	SKB_DR(reason);
44941d3fd8a1SStephen Suryaputra 	int type;
44951d3fd8a1SStephen Suryaputra 
44961158f79fSDavid Ahern 	if (netif_is_l3_master(skb->dev) ||
44971d3fd8a1SStephen Suryaputra 	    dst->dev == net->loopback_dev)
44981d3fd8a1SStephen Suryaputra 		idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
44991d3fd8a1SStephen Suryaputra 	else
45001d3fd8a1SStephen Suryaputra 		idev = ip6_dst_idev(dst);
45011d3fd8a1SStephen Suryaputra 
4502612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
4503612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
45040660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
450545bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
45063ae42cc8SMenglong Dong 			SKB_DR_SET(reason, IP_INADDRERRORS);
45071d3fd8a1SStephen Suryaputra 			IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
4508612f09e8SYOSHIFUJI Hideaki 			break;
4509612f09e8SYOSHIFUJI Hideaki 		}
45103ae42cc8SMenglong Dong 		SKB_DR_SET(reason, IP_INNOROUTES);
4511a8eceea8SJoe Perches 		fallthrough;
4512612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
45133ae42cc8SMenglong Dong 		SKB_DR_OR(reason, IP_OUTNOROUTES);
45141d3fd8a1SStephen Suryaputra 		IP6_INC_STATS(net, idev, ipstats_mib_noroutes);
4515612f09e8SYOSHIFUJI Hideaki 		break;
4516612f09e8SYOSHIFUJI Hideaki 	}
45171d3fd8a1SStephen Suryaputra 
45181d3fd8a1SStephen Suryaputra 	/* Start over by dropping the dst for l3mdev case */
45191d3fd8a1SStephen Suryaputra 	if (netif_is_l3_master(skb->dev))
45201d3fd8a1SStephen Suryaputra 		skb_dst_drop(skb);
45211d3fd8a1SStephen Suryaputra 
45223ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
45233ae42cc8SMenglong Dong 	kfree_skb_reason(skb, reason);
45241da177e4SLinus Torvalds 	return 0;
45251da177e4SLinus Torvalds }
45261da177e4SLinus Torvalds 
ip6_pkt_discard(struct sk_buff * skb)45279ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
45289ce8ade0SThomas Graf {
4529612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
45309ce8ade0SThomas Graf }
45319ce8ade0SThomas Graf 
ip6_pkt_discard_out(struct net * net,struct sock * sk,struct sk_buff * skb)4532ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
45331da177e4SLinus Torvalds {
4534adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
4535612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
45361da177e4SLinus Torvalds }
45371da177e4SLinus Torvalds 
ip6_pkt_prohibit(struct sk_buff * skb)45389ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
45399ce8ade0SThomas Graf {
4540612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
45419ce8ade0SThomas Graf }
45429ce8ade0SThomas Graf 
ip6_pkt_prohibit_out(struct net * net,struct sock * sk,struct sk_buff * skb)4543ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
45449ce8ade0SThomas Graf {
4545adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
4546612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
45479ce8ade0SThomas Graf }
45489ce8ade0SThomas Graf 
45491da177e4SLinus Torvalds /*
45501da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
45511da177e4SLinus Torvalds  */
45521da177e4SLinus Torvalds 
addrconf_f6i_alloc(struct net * net,struct inet6_dev * idev,const struct in6_addr * addr,bool anycast,gfp_t gfp_flags,struct netlink_ext_ack * extack)4553360a9887SDavid Ahern struct fib6_info *addrconf_f6i_alloc(struct net *net,
4554afb1d4b5SDavid Ahern 				     struct inet6_dev *idev,
45551da177e4SLinus Torvalds 				     const struct in6_addr *addr,
45567f6c4039SHangbin Liu 				     bool anycast, gfp_t gfp_flags,
45577f6c4039SHangbin Liu 				     struct netlink_ext_ack *extack)
45581da177e4SLinus Torvalds {
4559c7a1ce39SDavid Ahern 	struct fib6_config cfg = {
4560c7a1ce39SDavid Ahern 		.fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
4561c7a1ce39SDavid Ahern 		.fc_ifindex = idev->dev->ifindex,
4562d55a2e37SMaciej Żenczykowski 		.fc_flags = RTF_UP | RTF_NONEXTHOP,
4563c7a1ce39SDavid Ahern 		.fc_dst = *addr,
4564c7a1ce39SDavid Ahern 		.fc_dst_len = 128,
4565c7a1ce39SDavid Ahern 		.fc_protocol = RTPROT_KERNEL,
4566c7a1ce39SDavid Ahern 		.fc_nlinfo.nl_net = net,
4567c7a1ce39SDavid Ahern 		.fc_ignore_dev_down = true,
4568c7a1ce39SDavid Ahern 	};
4569d55a2e37SMaciej Żenczykowski 	struct fib6_info *f6i;
45705f02ce24SDavid Ahern 
4571e8478e80SDavid Ahern 	if (anycast) {
4572c7a1ce39SDavid Ahern 		cfg.fc_type = RTN_ANYCAST;
4573c7a1ce39SDavid Ahern 		cfg.fc_flags |= RTF_ANYCAST;
4574e8478e80SDavid Ahern 	} else {
4575c7a1ce39SDavid Ahern 		cfg.fc_type = RTN_LOCAL;
4576c7a1ce39SDavid Ahern 		cfg.fc_flags |= RTF_LOCAL;
4577e8478e80SDavid Ahern 	}
45781da177e4SLinus Torvalds 
45797f6c4039SHangbin Liu 	f6i = ip6_route_info_create(&cfg, gfp_flags, extack);
45803b0dc529SNicolas Dichtel 	if (!IS_ERR(f6i)) {
4581d55a2e37SMaciej Żenczykowski 		f6i->dst_nocount = true;
45823b0dc529SNicolas Dichtel 
45833b0dc529SNicolas Dichtel 		if (!anycast &&
45843b0dc529SNicolas Dichtel 		    (net->ipv6.devconf_all->disable_policy ||
45853b0dc529SNicolas Dichtel 		     idev->cnf.disable_policy))
45863b0dc529SNicolas Dichtel 			f6i->dst_nopolicy = true;
45873b0dc529SNicolas Dichtel 	}
45883b0dc529SNicolas Dichtel 
4589d55a2e37SMaciej Żenczykowski 	return f6i;
45901da177e4SLinus Torvalds }
45911da177e4SLinus Torvalds 
4592c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
4593c3968a85SDaniel Walter struct arg_dev_net_ip {
4594c3968a85SDaniel Walter 	struct net *net;
4595c3968a85SDaniel Walter 	struct in6_addr *addr;
4596c3968a85SDaniel Walter };
4597c3968a85SDaniel Walter 
fib6_remove_prefsrc(struct fib6_info * rt,void * arg)45988d1c802bSDavid Ahern static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
4599c3968a85SDaniel Walter {
4600c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
4601c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
4602c3968a85SDaniel Walter 
4603f88d8ea6SDavid Ahern 	if (!rt->nh &&
4604421842edSDavid Ahern 	    rt != net->ipv6.fib6_null_entry &&
4605b358f57fSHangbin Liu 	    ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr) &&
4606b358f57fSHangbin Liu 	    !ipv6_chk_addr(net, addr, rt->fib6_nh->fib_nh_dev, 0)) {
460760006a48SWei Wang 		spin_lock_bh(&rt6_exception_lock);
4608c3968a85SDaniel Walter 		/* remove prefsrc entry */
460993c2fb25SDavid Ahern 		rt->fib6_prefsrc.plen = 0;
461060006a48SWei Wang 		spin_unlock_bh(&rt6_exception_lock);
4611c3968a85SDaniel Walter 	}
4612c3968a85SDaniel Walter 	return 0;
4613c3968a85SDaniel Walter }
4614c3968a85SDaniel Walter 
rt6_remove_prefsrc(struct inet6_ifaddr * ifp)4615c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
4616c3968a85SDaniel Walter {
4617c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
4618c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
4619c3968a85SDaniel Walter 		.net = net,
4620c3968a85SDaniel Walter 		.addr = &ifp->addr,
4621c3968a85SDaniel Walter 	};
46220c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
4623c3968a85SDaniel Walter }
4624c3968a85SDaniel Walter 
46252b2450caSDavid Ahern #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT)
4626be7a010dSDuan Jiong 
4627be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
fib6_clean_tohost(struct fib6_info * rt,void * arg)46288d1c802bSDavid Ahern static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
4629be7a010dSDuan Jiong {
4630be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
4631f88d8ea6SDavid Ahern 	struct fib6_nh *nh;
4632be7a010dSDuan Jiong 
4633f88d8ea6SDavid Ahern 	/* RA routes do not use nexthops */
4634f88d8ea6SDavid Ahern 	if (rt->nh)
4635f88d8ea6SDavid Ahern 		return 0;
4636f88d8ea6SDavid Ahern 
4637f88d8ea6SDavid Ahern 	nh = rt->fib6_nh;
463893c2fb25SDavid Ahern 	if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
4639cc5c073aSDavid Ahern 	    nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6))
4640be7a010dSDuan Jiong 		return -1;
4641b16cb459SWei Wang 
4642b16cb459SWei Wang 	/* Further clean up cached routes in exception table.
4643b16cb459SWei Wang 	 * This is needed because cached route may have a different
4644b16cb459SWei Wang 	 * gateway than its 'parent' in the case of an ip redirect.
4645b16cb459SWei Wang 	 */
4646cc5c073aSDavid Ahern 	fib6_nh_exceptions_clean_tohost(nh, gateway);
4647b16cb459SWei Wang 
4648be7a010dSDuan Jiong 	return 0;
4649be7a010dSDuan Jiong }
4650be7a010dSDuan Jiong 
rt6_clean_tohost(struct net * net,struct in6_addr * gateway)4651be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
4652be7a010dSDuan Jiong {
4653be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
4654be7a010dSDuan Jiong }
4655be7a010dSDuan Jiong 
46562127d95aSIdo Schimmel struct arg_netdev_event {
46572127d95aSIdo Schimmel 	const struct net_device *dev;
46584c981e28SIdo Schimmel 	union {
4659ecc5663cSDavid Ahern 		unsigned char nh_flags;
46604c981e28SIdo Schimmel 		unsigned long event;
46614c981e28SIdo Schimmel 	};
46622127d95aSIdo Schimmel };
46632127d95aSIdo Schimmel 
rt6_multipath_first_sibling(const struct fib6_info * rt)46648d1c802bSDavid Ahern static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
4665d7dedee1SIdo Schimmel {
46668d1c802bSDavid Ahern 	struct fib6_info *iter;
4667d7dedee1SIdo Schimmel 	struct fib6_node *fn;
4668d7dedee1SIdo Schimmel 
466993c2fb25SDavid Ahern 	fn = rcu_dereference_protected(rt->fib6_node,
467093c2fb25SDavid Ahern 			lockdep_is_held(&rt->fib6_table->tb6_lock));
4671d7dedee1SIdo Schimmel 	iter = rcu_dereference_protected(fn->leaf,
467293c2fb25SDavid Ahern 			lockdep_is_held(&rt->fib6_table->tb6_lock));
4673d7dedee1SIdo Schimmel 	while (iter) {
467493c2fb25SDavid Ahern 		if (iter->fib6_metric == rt->fib6_metric &&
467533bd5ac5SDavid Ahern 		    rt6_qualify_for_ecmp(iter))
4676d7dedee1SIdo Schimmel 			return iter;
46778fb11a9aSDavid Ahern 		iter = rcu_dereference_protected(iter->fib6_next,
467893c2fb25SDavid Ahern 				lockdep_is_held(&rt->fib6_table->tb6_lock));
4679d7dedee1SIdo Schimmel 	}
4680d7dedee1SIdo Schimmel 
4681d7dedee1SIdo Schimmel 	return NULL;
4682d7dedee1SIdo Schimmel }
4683d7dedee1SIdo Schimmel 
4684f88d8ea6SDavid Ahern /* only called for fib entries with builtin fib6_nh */
rt6_is_dead(const struct fib6_info * rt)46858d1c802bSDavid Ahern static bool rt6_is_dead(const struct fib6_info *rt)
4686d7dedee1SIdo Schimmel {
46871cf844c7SDavid Ahern 	if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD ||
46881cf844c7SDavid Ahern 	    (rt->fib6_nh->fib_nh_flags & RTNH_F_LINKDOWN &&
46891cf844c7SDavid Ahern 	     ip6_ignore_linkdown(rt->fib6_nh->fib_nh_dev)))
4690d7dedee1SIdo Schimmel 		return true;
4691d7dedee1SIdo Schimmel 
4692d7dedee1SIdo Schimmel 	return false;
4693d7dedee1SIdo Schimmel }
4694d7dedee1SIdo Schimmel 
rt6_multipath_total_weight(const struct fib6_info * rt)46958d1c802bSDavid Ahern static int rt6_multipath_total_weight(const struct fib6_info *rt)
4696d7dedee1SIdo Schimmel {
46978d1c802bSDavid Ahern 	struct fib6_info *iter;
4698d7dedee1SIdo Schimmel 	int total = 0;
4699d7dedee1SIdo Schimmel 
4700d7dedee1SIdo Schimmel 	if (!rt6_is_dead(rt))
47011cf844c7SDavid Ahern 		total += rt->fib6_nh->fib_nh_weight;
4702d7dedee1SIdo Schimmel 
470393c2fb25SDavid Ahern 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
4704d7dedee1SIdo Schimmel 		if (!rt6_is_dead(iter))
47051cf844c7SDavid Ahern 			total += iter->fib6_nh->fib_nh_weight;
4706d7dedee1SIdo Schimmel 	}
4707d7dedee1SIdo Schimmel 
4708d7dedee1SIdo Schimmel 	return total;
4709d7dedee1SIdo Schimmel }
4710d7dedee1SIdo Schimmel 
rt6_upper_bound_set(struct fib6_info * rt,int * weight,int total)47118d1c802bSDavid Ahern static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
4712d7dedee1SIdo Schimmel {
4713d7dedee1SIdo Schimmel 	int upper_bound = -1;
4714d7dedee1SIdo Schimmel 
4715d7dedee1SIdo Schimmel 	if (!rt6_is_dead(rt)) {
47161cf844c7SDavid Ahern 		*weight += rt->fib6_nh->fib_nh_weight;
4717d7dedee1SIdo Schimmel 		upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
4718d7dedee1SIdo Schimmel 						    total) - 1;
4719d7dedee1SIdo Schimmel 	}
47201cf844c7SDavid Ahern 	atomic_set(&rt->fib6_nh->fib_nh_upper_bound, upper_bound);
4721d7dedee1SIdo Schimmel }
4722d7dedee1SIdo Schimmel 
rt6_multipath_upper_bound_set(struct fib6_info * rt,int total)47238d1c802bSDavid Ahern static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
4724d7dedee1SIdo Schimmel {
47258d1c802bSDavid Ahern 	struct fib6_info *iter;
4726d7dedee1SIdo Schimmel 	int weight = 0;
4727d7dedee1SIdo Schimmel 
4728d7dedee1SIdo Schimmel 	rt6_upper_bound_set(rt, &weight, total);
4729d7dedee1SIdo Schimmel 
473093c2fb25SDavid Ahern 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
4731d7dedee1SIdo Schimmel 		rt6_upper_bound_set(iter, &weight, total);
4732d7dedee1SIdo Schimmel }
4733d7dedee1SIdo Schimmel 
rt6_multipath_rebalance(struct fib6_info * rt)47348d1c802bSDavid Ahern void rt6_multipath_rebalance(struct fib6_info *rt)
4735d7dedee1SIdo Schimmel {
47368d1c802bSDavid Ahern 	struct fib6_info *first;
4737d7dedee1SIdo Schimmel 	int total;
4738d7dedee1SIdo Schimmel 
4739d7dedee1SIdo Schimmel 	/* In case the entire multipath route was marked for flushing,
4740d7dedee1SIdo Schimmel 	 * then there is no need to rebalance upon the removal of every
4741d7dedee1SIdo Schimmel 	 * sibling route.
4742d7dedee1SIdo Schimmel 	 */
474393c2fb25SDavid Ahern 	if (!rt->fib6_nsiblings || rt->should_flush)
4744d7dedee1SIdo Schimmel 		return;
4745d7dedee1SIdo Schimmel 
4746d7dedee1SIdo Schimmel 	/* During lookup routes are evaluated in order, so we need to
4747d7dedee1SIdo Schimmel 	 * make sure upper bounds are assigned from the first sibling
4748d7dedee1SIdo Schimmel 	 * onwards.
4749d7dedee1SIdo Schimmel 	 */
4750d7dedee1SIdo Schimmel 	first = rt6_multipath_first_sibling(rt);
4751d7dedee1SIdo Schimmel 	if (WARN_ON_ONCE(!first))
4752d7dedee1SIdo Schimmel 		return;
4753d7dedee1SIdo Schimmel 
4754d7dedee1SIdo Schimmel 	total = rt6_multipath_total_weight(first);
4755d7dedee1SIdo Schimmel 	rt6_multipath_upper_bound_set(first, total);
4756d7dedee1SIdo Schimmel }
4757d7dedee1SIdo Schimmel 
fib6_ifup(struct fib6_info * rt,void * p_arg)47588d1c802bSDavid Ahern static int fib6_ifup(struct fib6_info *rt, void *p_arg)
47592127d95aSIdo Schimmel {
47602127d95aSIdo Schimmel 	const struct arg_netdev_event *arg = p_arg;
47617aef6859SDavid Ahern 	struct net *net = dev_net(arg->dev);
47622127d95aSIdo Schimmel 
4763f88d8ea6SDavid Ahern 	if (rt != net->ipv6.fib6_null_entry && !rt->nh &&
47641cf844c7SDavid Ahern 	    rt->fib6_nh->fib_nh_dev == arg->dev) {
47651cf844c7SDavid Ahern 		rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags;
47667aef6859SDavid Ahern 		fib6_update_sernum_upto_root(net, rt);
4767d7dedee1SIdo Schimmel 		rt6_multipath_rebalance(rt);
47681de178edSIdo Schimmel 	}
47692127d95aSIdo Schimmel 
47702127d95aSIdo Schimmel 	return 0;
47712127d95aSIdo Schimmel }
47722127d95aSIdo Schimmel 
rt6_sync_up(struct net_device * dev,unsigned char nh_flags)4773ecc5663cSDavid Ahern void rt6_sync_up(struct net_device *dev, unsigned char nh_flags)
47742127d95aSIdo Schimmel {
47752127d95aSIdo Schimmel 	struct arg_netdev_event arg = {
47762127d95aSIdo Schimmel 		.dev = dev,
47776802f3adSIdo Schimmel 		{
47782127d95aSIdo Schimmel 			.nh_flags = nh_flags,
47796802f3adSIdo Schimmel 		},
47802127d95aSIdo Schimmel 	};
47812127d95aSIdo Schimmel 
47822127d95aSIdo Schimmel 	if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
47832127d95aSIdo Schimmel 		arg.nh_flags |= RTNH_F_LINKDOWN;
47842127d95aSIdo Schimmel 
47852127d95aSIdo Schimmel 	fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
47862127d95aSIdo Schimmel }
47872127d95aSIdo Schimmel 
4788f88d8ea6SDavid Ahern /* only called for fib entries with inline fib6_nh */
rt6_multipath_uses_dev(const struct fib6_info * rt,const struct net_device * dev)47898d1c802bSDavid Ahern static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
47901de178edSIdo Schimmel 				   const struct net_device *dev)
47911de178edSIdo Schimmel {
47928d1c802bSDavid Ahern 	struct fib6_info *iter;
47931de178edSIdo Schimmel 
47941cf844c7SDavid Ahern 	if (rt->fib6_nh->fib_nh_dev == dev)
47951de178edSIdo Schimmel 		return true;
479693c2fb25SDavid Ahern 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
47971cf844c7SDavid Ahern 		if (iter->fib6_nh->fib_nh_dev == dev)
47981de178edSIdo Schimmel 			return true;
47991de178edSIdo Schimmel 
48001de178edSIdo Schimmel 	return false;
48011de178edSIdo Schimmel }
48021de178edSIdo Schimmel 
rt6_multipath_flush(struct fib6_info * rt)48038d1c802bSDavid Ahern static void rt6_multipath_flush(struct fib6_info *rt)
48041de178edSIdo Schimmel {
48058d1c802bSDavid Ahern 	struct fib6_info *iter;
48061de178edSIdo Schimmel 
48071de178edSIdo Schimmel 	rt->should_flush = 1;
480893c2fb25SDavid Ahern 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
48091de178edSIdo Schimmel 		iter->should_flush = 1;
48101de178edSIdo Schimmel }
48111de178edSIdo Schimmel 
rt6_multipath_dead_count(const struct fib6_info * rt,const struct net_device * down_dev)48128d1c802bSDavid Ahern static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
48131de178edSIdo Schimmel 					     const struct net_device *down_dev)
48141de178edSIdo Schimmel {
48158d1c802bSDavid Ahern 	struct fib6_info *iter;
48161de178edSIdo Schimmel 	unsigned int dead = 0;
48171de178edSIdo Schimmel 
48181cf844c7SDavid Ahern 	if (rt->fib6_nh->fib_nh_dev == down_dev ||
48191cf844c7SDavid Ahern 	    rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
48201de178edSIdo Schimmel 		dead++;
482193c2fb25SDavid Ahern 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
48221cf844c7SDavid Ahern 		if (iter->fib6_nh->fib_nh_dev == down_dev ||
48231cf844c7SDavid Ahern 		    iter->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
48241de178edSIdo Schimmel 			dead++;
48251de178edSIdo Schimmel 
48261de178edSIdo Schimmel 	return dead;
48271de178edSIdo Schimmel }
48281de178edSIdo Schimmel 
rt6_multipath_nh_flags_set(struct fib6_info * rt,const struct net_device * dev,unsigned char nh_flags)48298d1c802bSDavid Ahern static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
48301de178edSIdo Schimmel 				       const struct net_device *dev,
4831ecc5663cSDavid Ahern 				       unsigned char nh_flags)
48321de178edSIdo Schimmel {
48338d1c802bSDavid Ahern 	struct fib6_info *iter;
48341de178edSIdo Schimmel 
48351cf844c7SDavid Ahern 	if (rt->fib6_nh->fib_nh_dev == dev)
48361cf844c7SDavid Ahern 		rt->fib6_nh->fib_nh_flags |= nh_flags;
483793c2fb25SDavid Ahern 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
48381cf844c7SDavid Ahern 		if (iter->fib6_nh->fib_nh_dev == dev)
48391cf844c7SDavid Ahern 			iter->fib6_nh->fib_nh_flags |= nh_flags;
48401de178edSIdo Schimmel }
48411de178edSIdo Schimmel 
4842a1a22c12SDavid Ahern /* called with write lock held for table with rt */
fib6_ifdown(struct fib6_info * rt,void * p_arg)48438d1c802bSDavid Ahern static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
48441da177e4SLinus Torvalds {
48454c981e28SIdo Schimmel 	const struct arg_netdev_event *arg = p_arg;
48464c981e28SIdo Schimmel 	const struct net_device *dev = arg->dev;
48477aef6859SDavid Ahern 	struct net *net = dev_net(dev);
48488ed67789SDaniel Lezcano 
4849f88d8ea6SDavid Ahern 	if (rt == net->ipv6.fib6_null_entry || rt->nh)
485027c6fa73SIdo Schimmel 		return 0;
485127c6fa73SIdo Schimmel 
485227c6fa73SIdo Schimmel 	switch (arg->event) {
485327c6fa73SIdo Schimmel 	case NETDEV_UNREGISTER:
48541cf844c7SDavid Ahern 		return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
485527c6fa73SIdo Schimmel 	case NETDEV_DOWN:
48561de178edSIdo Schimmel 		if (rt->should_flush)
485727c6fa73SIdo Schimmel 			return -1;
485893c2fb25SDavid Ahern 		if (!rt->fib6_nsiblings)
48591cf844c7SDavid Ahern 			return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
48601de178edSIdo Schimmel 		if (rt6_multipath_uses_dev(rt, dev)) {
48611de178edSIdo Schimmel 			unsigned int count;
48621de178edSIdo Schimmel 
48631de178edSIdo Schimmel 			count = rt6_multipath_dead_count(rt, dev);
486493c2fb25SDavid Ahern 			if (rt->fib6_nsiblings + 1 == count) {
48651de178edSIdo Schimmel 				rt6_multipath_flush(rt);
48661de178edSIdo Schimmel 				return -1;
48671de178edSIdo Schimmel 			}
48681de178edSIdo Schimmel 			rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
48691de178edSIdo Schimmel 						   RTNH_F_LINKDOWN);
48707aef6859SDavid Ahern 			fib6_update_sernum(net, rt);
4871d7dedee1SIdo Schimmel 			rt6_multipath_rebalance(rt);
48721de178edSIdo Schimmel 		}
48731de178edSIdo Schimmel 		return -2;
487427c6fa73SIdo Schimmel 	case NETDEV_CHANGE:
48751cf844c7SDavid Ahern 		if (rt->fib6_nh->fib_nh_dev != dev ||
487693c2fb25SDavid Ahern 		    rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
487727c6fa73SIdo Schimmel 			break;
48781cf844c7SDavid Ahern 		rt->fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
4879d7dedee1SIdo Schimmel 		rt6_multipath_rebalance(rt);
488027c6fa73SIdo Schimmel 		break;
48812b241361SIdo Schimmel 	}
4882c159d30cSDavid S. Miller 
48831da177e4SLinus Torvalds 	return 0;
48841da177e4SLinus Torvalds }
48851da177e4SLinus Torvalds 
rt6_sync_down_dev(struct net_device * dev,unsigned long event)488627c6fa73SIdo Schimmel void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
48871da177e4SLinus Torvalds {
48884c981e28SIdo Schimmel 	struct arg_netdev_event arg = {
48898ed67789SDaniel Lezcano 		.dev = dev,
48906802f3adSIdo Schimmel 		{
48914c981e28SIdo Schimmel 			.event = event,
48926802f3adSIdo Schimmel 		},
48938ed67789SDaniel Lezcano 	};
48947c6bb7d2SDavid Ahern 	struct net *net = dev_net(dev);
48958ed67789SDaniel Lezcano 
48967c6bb7d2SDavid Ahern 	if (net->ipv6.sysctl.skip_notify_on_dev_down)
48977c6bb7d2SDavid Ahern 		fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
48987c6bb7d2SDavid Ahern 	else
48997c6bb7d2SDavid Ahern 		fib6_clean_all(net, fib6_ifdown, &arg);
49004c981e28SIdo Schimmel }
49014c981e28SIdo Schimmel 
rt6_disable_ip(struct net_device * dev,unsigned long event)49024c981e28SIdo Schimmel void rt6_disable_ip(struct net_device *dev, unsigned long event)
49034c981e28SIdo Schimmel {
49044c981e28SIdo Schimmel 	rt6_sync_down_dev(dev, event);
4905e5f80fcfSEric Dumazet 	rt6_uncached_list_flush_dev(dev);
49064c981e28SIdo Schimmel 	neigh_ifdown(&nd_tbl, dev);
49071da177e4SLinus Torvalds }
49081da177e4SLinus Torvalds 
490995c96174SEric Dumazet struct rt6_mtu_change_arg {
49101da177e4SLinus Torvalds 	struct net_device *dev;
491195c96174SEric Dumazet 	unsigned int mtu;
4912c0b220cfSDavid Ahern 	struct fib6_info *f6i;
49131da177e4SLinus Torvalds };
49141da177e4SLinus Torvalds 
fib6_nh_mtu_change(struct fib6_nh * nh,void * _arg)4915cc5c073aSDavid Ahern static int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg)
4916c0b220cfSDavid Ahern {
4917c0b220cfSDavid Ahern 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *)_arg;
4918cc5c073aSDavid Ahern 	struct fib6_info *f6i = arg->f6i;
4919c0b220cfSDavid Ahern 
4920c0b220cfSDavid Ahern 	/* For administrative MTU increase, there is no way to discover
4921c0b220cfSDavid Ahern 	 * IPv6 PMTU increase, so PMTU increase should be updated here.
4922c0b220cfSDavid Ahern 	 * Since RFC 1981 doesn't include administrative MTU increase
4923c0b220cfSDavid Ahern 	 * update PMTU increase is a MUST. (i.e. jumbo frame)
4924c0b220cfSDavid Ahern 	 */
4925c0b220cfSDavid Ahern 	if (nh->fib_nh_dev == arg->dev) {
4926c0b220cfSDavid Ahern 		struct inet6_dev *idev = __in6_dev_get(arg->dev);
4927c0b220cfSDavid Ahern 		u32 mtu = f6i->fib6_pmtu;
4928c0b220cfSDavid Ahern 
4929c0b220cfSDavid Ahern 		if (mtu >= arg->mtu ||
4930c0b220cfSDavid Ahern 		    (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4931c0b220cfSDavid Ahern 			fib6_metric_set(f6i, RTAX_MTU, arg->mtu);
4932c0b220cfSDavid Ahern 
4933c0b220cfSDavid Ahern 		spin_lock_bh(&rt6_exception_lock);
4934cc5c073aSDavid Ahern 		rt6_exceptions_update_pmtu(idev, nh, arg->mtu);
4935c0b220cfSDavid Ahern 		spin_unlock_bh(&rt6_exception_lock);
4936c0b220cfSDavid Ahern 	}
4937c0b220cfSDavid Ahern 
4938c0b220cfSDavid Ahern 	return 0;
4939c0b220cfSDavid Ahern }
4940c0b220cfSDavid Ahern 
rt6_mtu_change_route(struct fib6_info * f6i,void * p_arg)4941c0b220cfSDavid Ahern static int rt6_mtu_change_route(struct fib6_info *f6i, void *p_arg)
49421da177e4SLinus Torvalds {
49431da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
49441da177e4SLinus Torvalds 	struct inet6_dev *idev;
49451da177e4SLinus Torvalds 
49461da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
49471da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
49481da177e4SLinus Torvalds 	   We still use this lock to block changes
49491da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
49501da177e4SLinus Torvalds 	*/
49511da177e4SLinus Torvalds 
49521da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
495338308473SDavid S. Miller 	if (!idev)
49541da177e4SLinus Torvalds 		return 0;
49551da177e4SLinus Torvalds 
4956c0b220cfSDavid Ahern 	if (fib6_metric_locked(f6i, RTAX_MTU))
49571da177e4SLinus Torvalds 		return 0;
4958c0b220cfSDavid Ahern 
4959c0b220cfSDavid Ahern 	arg->f6i = f6i;
49602d44234bSDavid Ahern 	if (f6i->nh) {
49612d44234bSDavid Ahern 		/* fib6_nh_mtu_change only returns 0, so this is safe */
49622d44234bSDavid Ahern 		return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_mtu_change,
49632d44234bSDavid Ahern 						arg);
49642d44234bSDavid Ahern 	}
49652d44234bSDavid Ahern 
49661cf844c7SDavid Ahern 	return fib6_nh_mtu_change(f6i->fib6_nh, arg);
49671da177e4SLinus Torvalds }
49681da177e4SLinus Torvalds 
rt6_mtu_change(struct net_device * dev,unsigned int mtu)496995c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
49701da177e4SLinus Torvalds {
4971c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
4972c71099acSThomas Graf 		.dev = dev,
4973c71099acSThomas Graf 		.mtu = mtu,
4974c71099acSThomas Graf 	};
49751da177e4SLinus Torvalds 
49760c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
49771da177e4SLinus Torvalds }
49781da177e4SLinus Torvalds 
4979ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
498075425657SDavid Ahern 	[RTA_UNSPEC]		= { .strict_start_type = RTA_DPORT + 1 },
49815176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
4982aa8f8778SEric Dumazet 	[RTA_PREFSRC]		= { .len = sizeof(struct in6_addr) },
498386872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
4984ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
498586872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
498686872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
498751ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
4988c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
498919e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
499019e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
499132bc201eSXin Long 	[RTA_EXPIRES]		= { .type = NLA_U32 },
4992622ec2c9SLorenzo Colitti 	[RTA_UID]		= { .type = NLA_U32 },
49933b45a410SLiping Zhang 	[RTA_MARK]		= { .type = NLA_U32 },
4994aa8f8778SEric Dumazet 	[RTA_TABLE]		= { .type = NLA_U32 },
4995eacb9384SRoopa Prabhu 	[RTA_IP_PROTO]		= { .type = NLA_U8 },
4996eacb9384SRoopa Prabhu 	[RTA_SPORT]		= { .type = NLA_U16 },
4997eacb9384SRoopa Prabhu 	[RTA_DPORT]		= { .type = NLA_U16 },
49985b98324eSDavid Ahern 	[RTA_NH_ID]		= { .type = NLA_U32 },
499986872cb5SThomas Graf };
500086872cb5SThomas Graf 
rtm_to_fib6_config(struct sk_buff * skb,struct nlmsghdr * nlh,struct fib6_config * cfg,struct netlink_ext_ack * extack)500186872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
5002333c4301SDavid Ahern 			      struct fib6_config *cfg,
5003333c4301SDavid Ahern 			      struct netlink_ext_ack *extack)
50041da177e4SLinus Torvalds {
500586872cb5SThomas Graf 	struct rtmsg *rtm;
500686872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
5007c78ba6d6SLubomir Rintel 	unsigned int pref;
500886872cb5SThomas Graf 	int err;
50091da177e4SLinus Torvalds 
50108cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
50118cb08174SJohannes Berg 				     rtm_ipv6_policy, extack);
501286872cb5SThomas Graf 	if (err < 0)
501386872cb5SThomas Graf 		goto errout;
50141da177e4SLinus Torvalds 
501586872cb5SThomas Graf 	err = -EINVAL;
501686872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
501786872cb5SThomas Graf 
5018b9605161SGuillaume Nault 	if (rtm->rtm_tos) {
5019b9605161SGuillaume Nault 		NL_SET_ERR_MSG(extack,
5020b9605161SGuillaume Nault 			       "Invalid dsfield (tos): option not available for IPv6");
5021b9605161SGuillaume Nault 		goto errout;
5022b9605161SGuillaume Nault 	}
5023b9605161SGuillaume Nault 
502484db8407SMaciej Żenczykowski 	*cfg = (struct fib6_config){
502584db8407SMaciej Żenczykowski 		.fc_table = rtm->rtm_table,
502684db8407SMaciej Żenczykowski 		.fc_dst_len = rtm->rtm_dst_len,
502784db8407SMaciej Żenczykowski 		.fc_src_len = rtm->rtm_src_len,
502884db8407SMaciej Żenczykowski 		.fc_flags = RTF_UP,
502984db8407SMaciej Żenczykowski 		.fc_protocol = rtm->rtm_protocol,
503084db8407SMaciej Żenczykowski 		.fc_type = rtm->rtm_type,
503184db8407SMaciej Żenczykowski 
503284db8407SMaciej Żenczykowski 		.fc_nlinfo.portid = NETLINK_CB(skb).portid,
503384db8407SMaciej Żenczykowski 		.fc_nlinfo.nlh = nlh,
503484db8407SMaciej Żenczykowski 		.fc_nlinfo.nl_net = sock_net(skb->sk),
503584db8407SMaciej Żenczykowski 	};
503686872cb5SThomas Graf 
5037ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
5038ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
5039b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
5040b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
504186872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
504286872cb5SThomas Graf 
5043ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
5044ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
5045ab79ad14SMaciej Żenczykowski 
50461f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
50471f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
50481f56a01fSMartin KaFai Lau 
5049fc1e64e1SDavid Ahern 	cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
5050fc1e64e1SDavid Ahern 
50515b98324eSDavid Ahern 	if (tb[RTA_NH_ID]) {
50525b98324eSDavid Ahern 		if (tb[RTA_GATEWAY]   || tb[RTA_OIF] ||
50535b98324eSDavid Ahern 		    tb[RTA_MULTIPATH] || tb[RTA_ENCAP]) {
50545b98324eSDavid Ahern 			NL_SET_ERR_MSG(extack,
50555b98324eSDavid Ahern 				       "Nexthop specification and nexthop id are mutually exclusive");
50565b98324eSDavid Ahern 			goto errout;
50575b98324eSDavid Ahern 		}
50585b98324eSDavid Ahern 		cfg->fc_nh_id = nla_get_u32(tb[RTA_NH_ID]);
50595b98324eSDavid Ahern 	}
50605b98324eSDavid Ahern 
506186872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
506267b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
506386872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
50641da177e4SLinus Torvalds 	}
5065e3818541SDavid Ahern 	if (tb[RTA_VIA]) {
5066e3818541SDavid Ahern 		NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
5067e3818541SDavid Ahern 		goto errout;
5068e3818541SDavid Ahern 	}
506986872cb5SThomas Graf 
507086872cb5SThomas Graf 	if (tb[RTA_DST]) {
507186872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
507286872cb5SThomas Graf 
507386872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
507486872cb5SThomas Graf 			goto errout;
507586872cb5SThomas Graf 
507686872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
50771da177e4SLinus Torvalds 	}
507886872cb5SThomas Graf 
507986872cb5SThomas Graf 	if (tb[RTA_SRC]) {
508086872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
508186872cb5SThomas Graf 
508286872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
508386872cb5SThomas Graf 			goto errout;
508486872cb5SThomas Graf 
508586872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
50861da177e4SLinus Torvalds 	}
508786872cb5SThomas Graf 
5088c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
508967b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
5090c3968a85SDaniel Walter 
509186872cb5SThomas Graf 	if (tb[RTA_OIF])
509286872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
509386872cb5SThomas Graf 
509486872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
509586872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
509686872cb5SThomas Graf 
509786872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
509886872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
509986872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
51001da177e4SLinus Torvalds 	}
510186872cb5SThomas Graf 
510286872cb5SThomas Graf 	if (tb[RTA_TABLE])
510386872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
510486872cb5SThomas Graf 
510551ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
510651ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
510751ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
51089ed59592SDavid Ahern 
51099ed59592SDavid Ahern 		err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
5110c255bd68SDavid Ahern 						     cfg->fc_mp_len, extack);
51119ed59592SDavid Ahern 		if (err < 0)
51129ed59592SDavid Ahern 			goto errout;
511351ebd318SNicolas Dichtel 	}
511451ebd318SNicolas Dichtel 
5115c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
5116c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
5117c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
5118c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
5119c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
5120c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
5121c78ba6d6SLubomir Rintel 	}
5122c78ba6d6SLubomir Rintel 
512319e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
512419e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
512519e42e45SRoopa Prabhu 
51269ed59592SDavid Ahern 	if (tb[RTA_ENCAP_TYPE]) {
512719e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
512819e42e45SRoopa Prabhu 
5129c255bd68SDavid Ahern 		err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
51309ed59592SDavid Ahern 		if (err < 0)
51319ed59592SDavid Ahern 			goto errout;
51329ed59592SDavid Ahern 	}
51339ed59592SDavid Ahern 
513432bc201eSXin Long 	if (tb[RTA_EXPIRES]) {
513532bc201eSXin Long 		unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
513632bc201eSXin Long 
513732bc201eSXin Long 		if (addrconf_finite_timeout(timeout)) {
513832bc201eSXin Long 			cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
513932bc201eSXin Long 			cfg->fc_flags |= RTF_EXPIRES;
514032bc201eSXin Long 		}
514132bc201eSXin Long 	}
514232bc201eSXin Long 
514386872cb5SThomas Graf 	err = 0;
514486872cb5SThomas Graf errout:
514586872cb5SThomas Graf 	return err;
51461da177e4SLinus Torvalds }
51471da177e4SLinus Torvalds 
51486b9ea5a6SRoopa Prabhu struct rt6_nh {
51498d1c802bSDavid Ahern 	struct fib6_info *fib6_info;
51506b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
51516b9ea5a6SRoopa Prabhu 	struct list_head next;
51526b9ea5a6SRoopa Prabhu };
51536b9ea5a6SRoopa Prabhu 
ip6_route_info_append(struct net * net,struct list_head * rt6_nh_list,struct fib6_info * rt,struct fib6_config * r_cfg)5154d4ead6b3SDavid Ahern static int ip6_route_info_append(struct net *net,
5155d4ead6b3SDavid Ahern 				 struct list_head *rt6_nh_list,
51568d1c802bSDavid Ahern 				 struct fib6_info *rt,
51578d1c802bSDavid Ahern 				 struct fib6_config *r_cfg)
51586b9ea5a6SRoopa Prabhu {
51596b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
51606b9ea5a6SRoopa Prabhu 	int err = -EEXIST;
51616b9ea5a6SRoopa Prabhu 
51626b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
51638d1c802bSDavid Ahern 		/* check if fib6_info already exists */
51648d1c802bSDavid Ahern 		if (rt6_duplicate_nexthop(nh->fib6_info, rt))
51656b9ea5a6SRoopa Prabhu 			return err;
51666b9ea5a6SRoopa Prabhu 	}
51676b9ea5a6SRoopa Prabhu 
51686b9ea5a6SRoopa Prabhu 	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
51696b9ea5a6SRoopa Prabhu 	if (!nh)
51706b9ea5a6SRoopa Prabhu 		return -ENOMEM;
51718d1c802bSDavid Ahern 	nh->fib6_info = rt;
51726b9ea5a6SRoopa Prabhu 	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
51736b9ea5a6SRoopa Prabhu 	list_add_tail(&nh->next, rt6_nh_list);
51746b9ea5a6SRoopa Prabhu 
51756b9ea5a6SRoopa Prabhu 	return 0;
51766b9ea5a6SRoopa Prabhu }
51776b9ea5a6SRoopa Prabhu 
ip6_route_mpath_notify(struct fib6_info * rt,struct fib6_info * rt_last,struct nl_info * info,__u16 nlflags)51788d1c802bSDavid Ahern static void ip6_route_mpath_notify(struct fib6_info *rt,
51798d1c802bSDavid Ahern 				   struct fib6_info *rt_last,
51803b1137feSDavid Ahern 				   struct nl_info *info,
51813b1137feSDavid Ahern 				   __u16 nlflags)
51823b1137feSDavid Ahern {
51833b1137feSDavid Ahern 	/* if this is an APPEND route, then rt points to the first route
51843b1137feSDavid Ahern 	 * inserted and rt_last points to last route inserted. Userspace
51853b1137feSDavid Ahern 	 * wants a consistent dump of the route which starts at the first
51863b1137feSDavid Ahern 	 * nexthop. Since sibling routes are always added at the end of
51873b1137feSDavid Ahern 	 * the list, find the first sibling of the last route appended
51883b1137feSDavid Ahern 	 */
518993c2fb25SDavid Ahern 	if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
519093c2fb25SDavid Ahern 		rt = list_first_entry(&rt_last->fib6_siblings,
51918d1c802bSDavid Ahern 				      struct fib6_info,
519293c2fb25SDavid Ahern 				      fib6_siblings);
51933b1137feSDavid Ahern 	}
51943b1137feSDavid Ahern 
51953b1137feSDavid Ahern 	if (rt)
51963b1137feSDavid Ahern 		inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
51973b1137feSDavid Ahern }
51983b1137feSDavid Ahern 
ip6_route_mpath_should_notify(const struct fib6_info * rt)51990ee0f47cSIdo Schimmel static bool ip6_route_mpath_should_notify(const struct fib6_info *rt)
52000ee0f47cSIdo Schimmel {
52010ee0f47cSIdo Schimmel 	bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
52020ee0f47cSIdo Schimmel 	bool should_notify = false;
52030ee0f47cSIdo Schimmel 	struct fib6_info *leaf;
52040ee0f47cSIdo Schimmel 	struct fib6_node *fn;
52050ee0f47cSIdo Schimmel 
52060ee0f47cSIdo Schimmel 	rcu_read_lock();
52070ee0f47cSIdo Schimmel 	fn = rcu_dereference(rt->fib6_node);
52080ee0f47cSIdo Schimmel 	if (!fn)
52090ee0f47cSIdo Schimmel 		goto out;
52100ee0f47cSIdo Schimmel 
52110ee0f47cSIdo Schimmel 	leaf = rcu_dereference(fn->leaf);
52120ee0f47cSIdo Schimmel 	if (!leaf)
52130ee0f47cSIdo Schimmel 		goto out;
52140ee0f47cSIdo Schimmel 
52150ee0f47cSIdo Schimmel 	if (rt == leaf ||
52160ee0f47cSIdo Schimmel 	    (rt_can_ecmp && rt->fib6_metric == leaf->fib6_metric &&
52170ee0f47cSIdo Schimmel 	     rt6_qualify_for_ecmp(leaf)))
52180ee0f47cSIdo Schimmel 		should_notify = true;
52190ee0f47cSIdo Schimmel out:
52200ee0f47cSIdo Schimmel 	rcu_read_unlock();
52210ee0f47cSIdo Schimmel 
52220ee0f47cSIdo Schimmel 	return should_notify;
52230ee0f47cSIdo Schimmel }
52240ee0f47cSIdo Schimmel 
fib6_gw_from_attr(struct in6_addr * gw,struct nlattr * nla,struct netlink_ext_ack * extack)52254619bcf9SDavid Ahern static int fib6_gw_from_attr(struct in6_addr *gw, struct nlattr *nla,
52264619bcf9SDavid Ahern 			     struct netlink_ext_ack *extack)
52274619bcf9SDavid Ahern {
52284619bcf9SDavid Ahern 	if (nla_len(nla) < sizeof(*gw)) {
52294619bcf9SDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_GATEWAY");
52304619bcf9SDavid Ahern 		return -EINVAL;
52314619bcf9SDavid Ahern 	}
52324619bcf9SDavid Ahern 
52334619bcf9SDavid Ahern 	*gw = nla_get_in6_addr(nla);
52344619bcf9SDavid Ahern 
52354619bcf9SDavid Ahern 	return 0;
52364619bcf9SDavid Ahern }
52374619bcf9SDavid Ahern 
ip6_route_multipath_add(struct fib6_config * cfg,struct netlink_ext_ack * extack)5238333c4301SDavid Ahern static int ip6_route_multipath_add(struct fib6_config *cfg,
5239333c4301SDavid Ahern 				   struct netlink_ext_ack *extack)
524051ebd318SNicolas Dichtel {
52418d1c802bSDavid Ahern 	struct fib6_info *rt_notif = NULL, *rt_last = NULL;
52423b1137feSDavid Ahern 	struct nl_info *info = &cfg->fc_nlinfo;
524351ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
524451ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
52458d1c802bSDavid Ahern 	struct fib6_info *rt;
52466b9ea5a6SRoopa Prabhu 	struct rt6_nh *err_nh;
52476b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh, *nh_safe;
52483b1137feSDavid Ahern 	__u16 nlflags;
524951ebd318SNicolas Dichtel 	int remaining;
525051ebd318SNicolas Dichtel 	int attrlen;
52516b9ea5a6SRoopa Prabhu 	int err = 1;
52526b9ea5a6SRoopa Prabhu 	int nhn = 0;
52536b9ea5a6SRoopa Prabhu 	int replace = (cfg->fc_nlinfo.nlh &&
52546b9ea5a6SRoopa Prabhu 		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
52556b9ea5a6SRoopa Prabhu 	LIST_HEAD(rt6_nh_list);
525651ebd318SNicolas Dichtel 
52573b1137feSDavid Ahern 	nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
52583b1137feSDavid Ahern 	if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
52593b1137feSDavid Ahern 		nlflags |= NLM_F_APPEND;
52603b1137feSDavid Ahern 
526135f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
526251ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
526351ebd318SNicolas Dichtel 
52646b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
52658d1c802bSDavid Ahern 	 * fib6_info structs per nexthop
52666b9ea5a6SRoopa Prabhu 	 */
526751ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
526851ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
526951ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
527051ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
527151ebd318SNicolas Dichtel 
527251ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
527351ebd318SNicolas Dichtel 		if (attrlen > 0) {
527451ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
527551ebd318SNicolas Dichtel 
527651ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
527751ebd318SNicolas Dichtel 			if (nla) {
527895bdba23SDavid Ahern 				err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
52794619bcf9SDavid Ahern 							extack);
528095bdba23SDavid Ahern 				if (err)
528195bdba23SDavid Ahern 					goto cleanup;
52824619bcf9SDavid Ahern 
528351ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
528451ebd318SNicolas Dichtel 			}
528519e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
52868bda81a4SDavid Ahern 
52878bda81a4SDavid Ahern 			/* RTA_ENCAP_TYPE length checked in
52888bda81a4SDavid Ahern 			 * lwtunnel_valid_encap_type_attr
52898bda81a4SDavid Ahern 			 */
529019e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
529119e42e45SRoopa Prabhu 			if (nla)
529219e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
529351ebd318SNicolas Dichtel 		}
52946b9ea5a6SRoopa Prabhu 
529568e2ffdeSDavid Ahern 		r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
5296acb54e3cSDavid Ahern 		rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
52978c5b83f0SRoopa Prabhu 		if (IS_ERR(rt)) {
52988c5b83f0SRoopa Prabhu 			err = PTR_ERR(rt);
52998c5b83f0SRoopa Prabhu 			rt = NULL;
53006b9ea5a6SRoopa Prabhu 			goto cleanup;
53018c5b83f0SRoopa Prabhu 		}
5302b5d2d75eSDavid Ahern 		if (!rt6_qualify_for_ecmp(rt)) {
5303b5d2d75eSDavid Ahern 			err = -EINVAL;
5304b5d2d75eSDavid Ahern 			NL_SET_ERR_MSG(extack,
5305b5d2d75eSDavid Ahern 				       "Device only routes can not be added for IPv6 using the multipath API.");
5306b5d2d75eSDavid Ahern 			fib6_info_release(rt);
5307b5d2d75eSDavid Ahern 			goto cleanup;
5308b5d2d75eSDavid Ahern 		}
53096b9ea5a6SRoopa Prabhu 
53101cf844c7SDavid Ahern 		rt->fib6_nh->fib_nh_weight = rtnh->rtnh_hops + 1;
5311398958aeSIdo Schimmel 
5312d4ead6b3SDavid Ahern 		err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
5313d4ead6b3SDavid Ahern 					    rt, &r_cfg);
531451ebd318SNicolas Dichtel 		if (err) {
531593531c67SDavid Ahern 			fib6_info_release(rt);
53166b9ea5a6SRoopa Prabhu 			goto cleanup;
531751ebd318SNicolas Dichtel 		}
53186b9ea5a6SRoopa Prabhu 
53196b9ea5a6SRoopa Prabhu 		rtnh = rtnh_next(rtnh, &remaining);
532051ebd318SNicolas Dichtel 	}
53216b9ea5a6SRoopa Prabhu 
53229eee3b49SIdo Schimmel 	if (list_empty(&rt6_nh_list)) {
53239eee3b49SIdo Schimmel 		NL_SET_ERR_MSG(extack,
53249eee3b49SIdo Schimmel 			       "Invalid nexthop configuration - no valid nexthops");
53259eee3b49SIdo Schimmel 		return -EINVAL;
53269eee3b49SIdo Schimmel 	}
53279eee3b49SIdo Schimmel 
53283b1137feSDavid Ahern 	/* for add and replace send one notification with all nexthops.
53293b1137feSDavid Ahern 	 * Skip the notification in fib6_add_rt2node and send one with
53303b1137feSDavid Ahern 	 * the full route when done
53313b1137feSDavid Ahern 	 */
53323b1137feSDavid Ahern 	info->skip_notify = 1;
53333b1137feSDavid Ahern 
5334ebee3cadSIdo Schimmel 	/* For add and replace, send one notification with all nexthops. For
5335ebee3cadSIdo Schimmel 	 * append, send one notification with all appended nexthops.
5336ebee3cadSIdo Schimmel 	 */
5337ebee3cadSIdo Schimmel 	info->skip_notify_kernel = 1;
5338ebee3cadSIdo Schimmel 
53396b9ea5a6SRoopa Prabhu 	err_nh = NULL;
53406b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
53418d1c802bSDavid Ahern 		err = __ip6_ins_rt(nh->fib6_info, info, extack);
53423b1137feSDavid Ahern 
53436b9ea5a6SRoopa Prabhu 		if (err) {
53446b9ea5a6SRoopa Prabhu 			if (replace && nhn)
5345a5a82d84SJakub Kicinski 				NL_SET_ERR_MSG_MOD(extack,
5346a5a82d84SJakub Kicinski 						   "multipath route replace failed (check consistency of installed routes)");
53476b9ea5a6SRoopa Prabhu 			err_nh = nh;
53486b9ea5a6SRoopa Prabhu 			goto add_errout;
53496b9ea5a6SRoopa Prabhu 		}
5350ed883060SEric Dumazet 		/* save reference to last route successfully inserted */
5351ed883060SEric Dumazet 		rt_last = nh->fib6_info;
5352ed883060SEric Dumazet 
5353ed883060SEric Dumazet 		/* save reference to first route for notification */
5354ed883060SEric Dumazet 		if (!rt_notif)
5355ed883060SEric Dumazet 			rt_notif = nh->fib6_info;
53566b9ea5a6SRoopa Prabhu 
53571a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
535827596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
535927596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
536027596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
536127596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
536227596472SMichal Kubeček 		 * be added to it.
53631a72418bSNicolas Dichtel 		 */
5364864db232SMuhammad Usama Anjum 		if (cfg->fc_nlinfo.nlh) {
536527596472SMichal Kubeček 			cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
536627596472SMichal Kubeček 							     NLM_F_REPLACE);
5367afecdb37SBenjamin Poirier 			cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_CREATE;
5368864db232SMuhammad Usama Anjum 		}
53696b9ea5a6SRoopa Prabhu 		nhn++;
53706b9ea5a6SRoopa Prabhu 	}
53716b9ea5a6SRoopa Prabhu 
53720ee0f47cSIdo Schimmel 	/* An in-kernel notification should only be sent in case the new
53730ee0f47cSIdo Schimmel 	 * multipath route is added as the first route in the node, or if
53740ee0f47cSIdo Schimmel 	 * it was appended to it. We pass 'rt_notif' since it is the first
53750ee0f47cSIdo Schimmel 	 * sibling and might allow us to skip some checks in the replace case.
53760ee0f47cSIdo Schimmel 	 */
53770ee0f47cSIdo Schimmel 	if (ip6_route_mpath_should_notify(rt_notif)) {
53780ee0f47cSIdo Schimmel 		enum fib_event_type fib_event;
53790ee0f47cSIdo Schimmel 
53800ee0f47cSIdo Schimmel 		if (rt_notif->fib6_nsiblings != nhn - 1)
53810ee0f47cSIdo Schimmel 			fib_event = FIB_EVENT_ENTRY_APPEND;
53820ee0f47cSIdo Schimmel 		else
5383caafb250SIdo Schimmel 			fib_event = FIB_EVENT_ENTRY_REPLACE;
53840ee0f47cSIdo Schimmel 
53850ee0f47cSIdo Schimmel 		err = call_fib6_multipath_entry_notifiers(info->nl_net,
53860ee0f47cSIdo Schimmel 							  fib_event, rt_notif,
53870ee0f47cSIdo Schimmel 							  nhn - 1, extack);
53880ee0f47cSIdo Schimmel 		if (err) {
53890ee0f47cSIdo Schimmel 			/* Delete all the siblings that were just added */
53900ee0f47cSIdo Schimmel 			err_nh = NULL;
53910ee0f47cSIdo Schimmel 			goto add_errout;
53920ee0f47cSIdo Schimmel 		}
53930ee0f47cSIdo Schimmel 	}
5394ebee3cadSIdo Schimmel 
53953b1137feSDavid Ahern 	/* success ... tell user about new route */
53963b1137feSDavid Ahern 	ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
53976b9ea5a6SRoopa Prabhu 	goto cleanup;
53986b9ea5a6SRoopa Prabhu 
53996b9ea5a6SRoopa Prabhu add_errout:
54003b1137feSDavid Ahern 	/* send notification for routes that were added so that
54013b1137feSDavid Ahern 	 * the delete notifications sent by ip6_route_del are
54023b1137feSDavid Ahern 	 * coherent
54033b1137feSDavid Ahern 	 */
54043b1137feSDavid Ahern 	if (rt_notif)
54053b1137feSDavid Ahern 		ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
54063b1137feSDavid Ahern 
54076b9ea5a6SRoopa Prabhu 	/* Delete routes that were already added */
54086b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
54096b9ea5a6SRoopa Prabhu 		if (err_nh == nh)
54106b9ea5a6SRoopa Prabhu 			break;
5411333c4301SDavid Ahern 		ip6_route_del(&nh->r_cfg, extack);
54126b9ea5a6SRoopa Prabhu 	}
54136b9ea5a6SRoopa Prabhu 
54146b9ea5a6SRoopa Prabhu cleanup:
54156b9ea5a6SRoopa Prabhu 	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
54168d1c802bSDavid Ahern 		fib6_info_release(nh->fib6_info);
54176b9ea5a6SRoopa Prabhu 		list_del(&nh->next);
54186b9ea5a6SRoopa Prabhu 		kfree(nh);
54196b9ea5a6SRoopa Prabhu 	}
54206b9ea5a6SRoopa Prabhu 
54216b9ea5a6SRoopa Prabhu 	return err;
54226b9ea5a6SRoopa Prabhu }
54236b9ea5a6SRoopa Prabhu 
ip6_route_multipath_del(struct fib6_config * cfg,struct netlink_ext_ack * extack)5424333c4301SDavid Ahern static int ip6_route_multipath_del(struct fib6_config *cfg,
5425333c4301SDavid Ahern 				   struct netlink_ext_ack *extack)
54266b9ea5a6SRoopa Prabhu {
54276b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
54286b9ea5a6SRoopa Prabhu 	struct rtnexthop *rtnh;
54292291267eSColin Ian King 	int last_err = 0;
54306b9ea5a6SRoopa Prabhu 	int remaining;
54316b9ea5a6SRoopa Prabhu 	int attrlen;
54322291267eSColin Ian King 	int err;
54336b9ea5a6SRoopa Prabhu 
54346b9ea5a6SRoopa Prabhu 	remaining = cfg->fc_mp_len;
54356b9ea5a6SRoopa Prabhu 	rtnh = (struct rtnexthop *)cfg->fc_mp;
54366b9ea5a6SRoopa Prabhu 
54376b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry */
54386b9ea5a6SRoopa Prabhu 	while (rtnh_ok(rtnh, remaining)) {
54396b9ea5a6SRoopa Prabhu 		memcpy(&r_cfg, cfg, sizeof(*cfg));
54406b9ea5a6SRoopa Prabhu 		if (rtnh->rtnh_ifindex)
54416b9ea5a6SRoopa Prabhu 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
54426b9ea5a6SRoopa Prabhu 
54436b9ea5a6SRoopa Prabhu 		attrlen = rtnh_attrlen(rtnh);
54446b9ea5a6SRoopa Prabhu 		if (attrlen > 0) {
54456b9ea5a6SRoopa Prabhu 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
54466b9ea5a6SRoopa Prabhu 
54476b9ea5a6SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
54486b9ea5a6SRoopa Prabhu 			if (nla) {
54491ff15a71SDavid Ahern 				err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla,
54501ff15a71SDavid Ahern 							extack);
5451e30a845bSDavid Ahern 				if (err) {
5452e30a845bSDavid Ahern 					last_err = err;
5453e30a845bSDavid Ahern 					goto next_rtnh;
5454e30a845bSDavid Ahern 				}
54551ff15a71SDavid Ahern 
54566b9ea5a6SRoopa Prabhu 				r_cfg.fc_flags |= RTF_GATEWAY;
54576b9ea5a6SRoopa Prabhu 			}
54586b9ea5a6SRoopa Prabhu 		}
5459333c4301SDavid Ahern 		err = ip6_route_del(&r_cfg, extack);
54606b9ea5a6SRoopa Prabhu 		if (err)
54616b9ea5a6SRoopa Prabhu 			last_err = err;
54626b9ea5a6SRoopa Prabhu 
5463e30a845bSDavid Ahern next_rtnh:
546451ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
546551ebd318SNicolas Dichtel 	}
546651ebd318SNicolas Dichtel 
546751ebd318SNicolas Dichtel 	return last_err;
546851ebd318SNicolas Dichtel }
546951ebd318SNicolas Dichtel 
inet6_rtm_delroute(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)5470c21ef3e3SDavid Ahern static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
5471c21ef3e3SDavid Ahern 			      struct netlink_ext_ack *extack)
54721da177e4SLinus Torvalds {
547386872cb5SThomas Graf 	struct fib6_config cfg;
547486872cb5SThomas Graf 	int err;
54751da177e4SLinus Torvalds 
5476333c4301SDavid Ahern 	err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
547786872cb5SThomas Graf 	if (err < 0)
547886872cb5SThomas Graf 		return err;
547986872cb5SThomas Graf 
54805b98324eSDavid Ahern 	if (cfg.fc_nh_id &&
54815b98324eSDavid Ahern 	    !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) {
54825b98324eSDavid Ahern 		NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
54835b98324eSDavid Ahern 		return -EINVAL;
54845b98324eSDavid Ahern 	}
54855b98324eSDavid Ahern 
548651ebd318SNicolas Dichtel 	if (cfg.fc_mp)
5487333c4301SDavid Ahern 		return ip6_route_multipath_del(&cfg, extack);
54880ae81335SDavid Ahern 	else {
54890ae81335SDavid Ahern 		cfg.fc_delete_all_nh = 1;
5490333c4301SDavid Ahern 		return ip6_route_del(&cfg, extack);
54911da177e4SLinus Torvalds 	}
54920ae81335SDavid Ahern }
54931da177e4SLinus Torvalds 
inet6_rtm_newroute(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)5494c21ef3e3SDavid Ahern static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
5495c21ef3e3SDavid Ahern 			      struct netlink_ext_ack *extack)
54961da177e4SLinus Torvalds {
549786872cb5SThomas Graf 	struct fib6_config cfg;
549886872cb5SThomas Graf 	int err;
54991da177e4SLinus Torvalds 
5500333c4301SDavid Ahern 	err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
550186872cb5SThomas Graf 	if (err < 0)
550286872cb5SThomas Graf 		return err;
550386872cb5SThomas Graf 
550467f69513SDavid Ahern 	if (cfg.fc_metric == 0)
550567f69513SDavid Ahern 		cfg.fc_metric = IP6_RT_PRIO_USER;
550667f69513SDavid Ahern 
550751ebd318SNicolas Dichtel 	if (cfg.fc_mp)
5508333c4301SDavid Ahern 		return ip6_route_multipath_add(&cfg, extack);
550951ebd318SNicolas Dichtel 	else
5510acb54e3cSDavid Ahern 		return ip6_route_add(&cfg, GFP_KERNEL, extack);
55111da177e4SLinus Torvalds }
55121da177e4SLinus Torvalds 
5513a1b7a1f0SDavid Ahern /* add the overhead of this fib6_nh to nexthop_len */
rt6_nh_nlmsg_size(struct fib6_nh * nh,void * arg)5514a1b7a1f0SDavid Ahern static int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg)
5515339bf98fSThomas Graf {
5516a1b7a1f0SDavid Ahern 	int *nexthop_len = arg;
5517beb1afacSDavid Ahern 
5518a1b7a1f0SDavid Ahern 	*nexthop_len += nla_total_size(0)	 /* RTA_MULTIPATH */
5519a1b7a1f0SDavid Ahern 		     + NLA_ALIGN(sizeof(struct rtnexthop))
5520a1b7a1f0SDavid Ahern 		     + nla_total_size(16); /* RTA_GATEWAY */
5521f88d8ea6SDavid Ahern 
5522a1b7a1f0SDavid Ahern 	if (nh->fib_nh_lws) {
5523a1b7a1f0SDavid Ahern 		/* RTA_ENCAP_TYPE */
5524a1b7a1f0SDavid Ahern 		*nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
5525a1b7a1f0SDavid Ahern 		/* RTA_ENCAP */
5526a1b7a1f0SDavid Ahern 		*nexthop_len += nla_total_size(2);
5527a1b7a1f0SDavid Ahern 	}
5528a1b7a1f0SDavid Ahern 
5529a1b7a1f0SDavid Ahern 	return 0;
5530a1b7a1f0SDavid Ahern }
5531a1b7a1f0SDavid Ahern 
rt6_nlmsg_size(struct fib6_info * f6i)5532a1b7a1f0SDavid Ahern static size_t rt6_nlmsg_size(struct fib6_info *f6i)
5533a1b7a1f0SDavid Ahern {
5534a1b7a1f0SDavid Ahern 	int nexthop_len;
5535a1b7a1f0SDavid Ahern 
5536a1b7a1f0SDavid Ahern 	if (f6i->nh) {
5537a1b7a1f0SDavid Ahern 		nexthop_len = nla_total_size(4); /* RTA_NH_ID */
5538a1b7a1f0SDavid Ahern 		nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size,
5539a1b7a1f0SDavid Ahern 					 &nexthop_len);
5540a1b7a1f0SDavid Ahern 	} else {
55414cc59f38SLu Wei 		struct fib6_info *sibling, *next_sibling;
5542a1b7a1f0SDavid Ahern 		struct fib6_nh *nh = f6i->fib6_nh;
5543a1b7a1f0SDavid Ahern 
5544a1b7a1f0SDavid Ahern 		nexthop_len = 0;
5545a1b7a1f0SDavid Ahern 		if (f6i->fib6_nsiblings) {
55464cc59f38SLu Wei 			rt6_nh_nlmsg_size(nh, &nexthop_len);
5547beb1afacSDavid Ahern 
55484cc59f38SLu Wei 			list_for_each_entry_safe(sibling, next_sibling,
55494cc59f38SLu Wei 						 &f6i->fib6_siblings, fib6_siblings) {
55504cc59f38SLu Wei 				rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len);
55514cc59f38SLu Wei 			}
5552a1b7a1f0SDavid Ahern 		}
5553a1b7a1f0SDavid Ahern 		nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
5554beb1afacSDavid Ahern 	}
5555beb1afacSDavid Ahern 
5556339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
5557339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
5558339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
5559339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
5560339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
5561339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
5562339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
5563339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
5564339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
55656a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
5566ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
5567c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
556819e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
5569beb1afacSDavid Ahern 	       + nexthop_len;
5570beb1afacSDavid Ahern }
5571beb1afacSDavid Ahern 
rt6_fill_node_nexthop(struct sk_buff * skb,struct nexthop * nh,unsigned char * flags)5572f88d8ea6SDavid Ahern static int rt6_fill_node_nexthop(struct sk_buff *skb, struct nexthop *nh,
5573f88d8ea6SDavid Ahern 				 unsigned char *flags)
5574f88d8ea6SDavid Ahern {
5575f88d8ea6SDavid Ahern 	if (nexthop_is_multipath(nh)) {
5576f88d8ea6SDavid Ahern 		struct nlattr *mp;
5577f88d8ea6SDavid Ahern 
55784255ff05SDavid Ahern 		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
5579f88d8ea6SDavid Ahern 		if (!mp)
5580f88d8ea6SDavid Ahern 			goto nla_put_failure;
5581f88d8ea6SDavid Ahern 
55827bdf4de1SDonald Sharp 		if (nexthop_mpath_fill_node(skb, nh, AF_INET6))
5583f88d8ea6SDavid Ahern 			goto nla_put_failure;
5584f88d8ea6SDavid Ahern 
5585f88d8ea6SDavid Ahern 		nla_nest_end(skb, mp);
5586f88d8ea6SDavid Ahern 	} else {
5587f88d8ea6SDavid Ahern 		struct fib6_nh *fib6_nh;
5588f88d8ea6SDavid Ahern 
5589f88d8ea6SDavid Ahern 		fib6_nh = nexthop_fib6_nh(nh);
55907bdf4de1SDonald Sharp 		if (fib_nexthop_info(skb, &fib6_nh->nh_common, AF_INET6,
5591f88d8ea6SDavid Ahern 				     flags, false) < 0)
5592f88d8ea6SDavid Ahern 			goto nla_put_failure;
5593f88d8ea6SDavid Ahern 	}
5594f88d8ea6SDavid Ahern 
5595f88d8ea6SDavid Ahern 	return 0;
5596f88d8ea6SDavid Ahern 
5597f88d8ea6SDavid Ahern nla_put_failure:
5598f88d8ea6SDavid Ahern 	return -EMSGSIZE;
5599f88d8ea6SDavid Ahern }
5600f88d8ea6SDavid Ahern 
rt6_fill_node(struct net * net,struct sk_buff * skb,struct fib6_info * rt,struct dst_entry * dst,struct in6_addr * dest,struct in6_addr * src,int iif,int type,u32 portid,u32 seq,unsigned int flags)5601d4ead6b3SDavid Ahern static int rt6_fill_node(struct net *net, struct sk_buff *skb,
56028d1c802bSDavid Ahern 			 struct fib6_info *rt, struct dst_entry *dst,
5603d4ead6b3SDavid Ahern 			 struct in6_addr *dest, struct in6_addr *src,
560415e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
5605f8cfe2ceSDavid Ahern 			 unsigned int flags)
56061da177e4SLinus Torvalds {
5607797a4c1fSEric Dumazet 	struct rt6_info *rt6 = dst_rt6_info(dst);
560822d0bd82SXin Long 	struct rt6key *rt6_dst, *rt6_src;
560922d0bd82SXin Long 	u32 *pmetrics, table, rt6_flags;
5610f88d8ea6SDavid Ahern 	unsigned char nh_flags = 0;
56111da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
561222d0bd82SXin Long 	struct rtmsg *rtm;
5613d4ead6b3SDavid Ahern 	long expires = 0;
56141da177e4SLinus Torvalds 
561515e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
561638308473SDavid S. Miller 	if (!nlh)
561726932566SPatrick McHardy 		return -EMSGSIZE;
56182d7202bfSThomas Graf 
561922d0bd82SXin Long 	if (rt6) {
562022d0bd82SXin Long 		rt6_dst = &rt6->rt6i_dst;
562122d0bd82SXin Long 		rt6_src = &rt6->rt6i_src;
562222d0bd82SXin Long 		rt6_flags = rt6->rt6i_flags;
562322d0bd82SXin Long 	} else {
562422d0bd82SXin Long 		rt6_dst = &rt->fib6_dst;
562522d0bd82SXin Long 		rt6_src = &rt->fib6_src;
562622d0bd82SXin Long 		rt6_flags = rt->fib6_flags;
562722d0bd82SXin Long 	}
562822d0bd82SXin Long 
56292d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
56301da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
563122d0bd82SXin Long 	rtm->rtm_dst_len = rt6_dst->plen;
563222d0bd82SXin Long 	rtm->rtm_src_len = rt6_src->plen;
56331da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
563493c2fb25SDavid Ahern 	if (rt->fib6_table)
563593c2fb25SDavid Ahern 		table = rt->fib6_table->tb6_id;
5636c71099acSThomas Graf 	else
56379e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
563897f0082aSKalash Nainwal 	rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
5639c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
5640c78679e8SDavid S. Miller 		goto nla_put_failure;
5641e8478e80SDavid Ahern 
5642e8478e80SDavid Ahern 	rtm->rtm_type = rt->fib6_type;
56431da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
56441da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
564593c2fb25SDavid Ahern 	rtm->rtm_protocol = rt->fib6_protocol;
56461da177e4SLinus Torvalds 
564722d0bd82SXin Long 	if (rt6_flags & RTF_CACHE)
56481da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
56491da177e4SLinus Torvalds 
5650d4ead6b3SDavid Ahern 	if (dest) {
5651d4ead6b3SDavid Ahern 		if (nla_put_in6_addr(skb, RTA_DST, dest))
5652c78679e8SDavid S. Miller 			goto nla_put_failure;
56531da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
56541da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
565522d0bd82SXin Long 		if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
5656c78679e8SDavid S. Miller 			goto nla_put_failure;
56571da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
56581da177e4SLinus Torvalds 	if (src) {
5659930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
5660c78679e8SDavid S. Miller 			goto nla_put_failure;
56611da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
5662c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
566322d0bd82SXin Long 		   nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
5664c78679e8SDavid S. Miller 		goto nla_put_failure;
56651da177e4SLinus Torvalds #endif
56667bc570c8SYOSHIFUJI Hideaki 	if (iif) {
56677bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
566822d0bd82SXin Long 		if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
5669fd61c6baSDavid Ahern 			int err = ip6mr_get_route(net, skb, rtm, portid);
56702cf75070SNikolay Aleksandrov 
56717bc570c8SYOSHIFUJI Hideaki 			if (err == 0)
56727bc570c8SYOSHIFUJI Hideaki 				return 0;
5673fd61c6baSDavid Ahern 			if (err < 0)
56747bc570c8SYOSHIFUJI Hideaki 				goto nla_put_failure;
56757bc570c8SYOSHIFUJI Hideaki 		} else
56767bc570c8SYOSHIFUJI Hideaki #endif
5677c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
5678c78679e8SDavid S. Miller 				goto nla_put_failure;
5679d4ead6b3SDavid Ahern 	} else if (dest) {
56801da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
5681e7f3e5fbSNicolas Dichtel 		if (ip6_route_get_saddr(net, rt, dest, 0, 0, &saddr_buf) == 0 &&
5682930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
5683c78679e8SDavid S. Miller 			goto nla_put_failure;
5684c3968a85SDaniel Walter 	}
5685c3968a85SDaniel Walter 
568693c2fb25SDavid Ahern 	if (rt->fib6_prefsrc.plen) {
5687c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
568893c2fb25SDavid Ahern 		saddr_buf = rt->fib6_prefsrc.addr;
5689930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
5690c78679e8SDavid S. Miller 			goto nla_put_failure;
56911da177e4SLinus Torvalds 	}
56922d7202bfSThomas Graf 
5693d4ead6b3SDavid Ahern 	pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
5694d4ead6b3SDavid Ahern 	if (rtnetlink_put_metrics(skb, pmetrics) < 0)
56952d7202bfSThomas Graf 		goto nla_put_failure;
56962d7202bfSThomas Graf 
569793c2fb25SDavid Ahern 	if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
5698beb1afacSDavid Ahern 		goto nla_put_failure;
5699beb1afacSDavid Ahern 
5700beb1afacSDavid Ahern 	/* For multipath routes, walk the siblings list and add
5701beb1afacSDavid Ahern 	 * each as a nexthop within RTA_MULTIPATH.
5702beb1afacSDavid Ahern 	 */
570322d0bd82SXin Long 	if (rt6) {
570422d0bd82SXin Long 		if (rt6_flags & RTF_GATEWAY &&
570522d0bd82SXin Long 		    nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
570622d0bd82SXin Long 			goto nla_put_failure;
570722d0bd82SXin Long 
570822d0bd82SXin Long 		if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
570922d0bd82SXin Long 			goto nla_put_failure;
57106b13d8f7SOliver Herms 
57116b13d8f7SOliver Herms 		if (dst->lwtstate &&
57126b13d8f7SOliver Herms 		    lwtunnel_fill_encap(skb, dst->lwtstate, RTA_ENCAP, RTA_ENCAP_TYPE) < 0)
57136b13d8f7SOliver Herms 			goto nla_put_failure;
571422d0bd82SXin Long 	} else if (rt->fib6_nsiblings) {
57158d1c802bSDavid Ahern 		struct fib6_info *sibling, *next_sibling;
5716beb1afacSDavid Ahern 		struct nlattr *mp;
5717beb1afacSDavid Ahern 
5718ae0be8deSMichal Kubecek 		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
5719beb1afacSDavid Ahern 		if (!mp)
5720beb1afacSDavid Ahern 			goto nla_put_failure;
5721beb1afacSDavid Ahern 
57221cf844c7SDavid Ahern 		if (fib_add_nexthop(skb, &rt->fib6_nh->nh_common,
5723597aa16cSXiao Liang 				    rt->fib6_nh->fib_nh_weight, AF_INET6,
5724597aa16cSXiao Liang 				    0) < 0)
5725beb1afacSDavid Ahern 			goto nla_put_failure;
5726beb1afacSDavid Ahern 
5727beb1afacSDavid Ahern 		list_for_each_entry_safe(sibling, next_sibling,
572893c2fb25SDavid Ahern 					 &rt->fib6_siblings, fib6_siblings) {
57291cf844c7SDavid Ahern 			if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common,
57307bdf4de1SDonald Sharp 					    sibling->fib6_nh->fib_nh_weight,
5731597aa16cSXiao Liang 					    AF_INET6, 0) < 0)
573294f826b8SEric Dumazet 				goto nla_put_failure;
573394f826b8SEric Dumazet 		}
57342d7202bfSThomas Graf 
5735beb1afacSDavid Ahern 		nla_nest_end(skb, mp);
5736f88d8ea6SDavid Ahern 	} else if (rt->nh) {
5737f88d8ea6SDavid Ahern 		if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id))
5738f88d8ea6SDavid Ahern 			goto nla_put_failure;
5739ecc5663cSDavid Ahern 
5740f88d8ea6SDavid Ahern 		if (nexthop_is_blackhole(rt->nh))
5741f88d8ea6SDavid Ahern 			rtm->rtm_type = RTN_BLACKHOLE;
5742f88d8ea6SDavid Ahern 
5743bdf00bf2SKuniyuki Iwashima 		if (READ_ONCE(net->ipv4.sysctl_nexthop_compat_mode) &&
57444f80116dSRoopa Prabhu 		    rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0)
5745f88d8ea6SDavid Ahern 			goto nla_put_failure;
5746f88d8ea6SDavid Ahern 
5747f88d8ea6SDavid Ahern 		rtm->rtm_flags |= nh_flags;
5748f88d8ea6SDavid Ahern 	} else {
57497bdf4de1SDonald Sharp 		if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, AF_INET6,
5750ecc5663cSDavid Ahern 				     &nh_flags, false) < 0)
5751c78679e8SDavid S. Miller 			goto nla_put_failure;
5752ecc5663cSDavid Ahern 
5753ecc5663cSDavid Ahern 		rtm->rtm_flags |= nh_flags;
5754beb1afacSDavid Ahern 	}
57558253947eSLi Wei 
575622d0bd82SXin Long 	if (rt6_flags & RTF_EXPIRES) {
575714895687SDavid Ahern 		expires = dst ? dst->expires : rt->expires;
575814895687SDavid Ahern 		expires -= jiffies;
575914895687SDavid Ahern 	}
576069cdf8f9SYOSHIFUJI Hideaki 
5761bb3c4ab9SIdo Schimmel 	if (!dst) {
5762d95d6320SEric Dumazet 		if (READ_ONCE(rt->offload))
5763bb3c4ab9SIdo Schimmel 			rtm->rtm_flags |= RTM_F_OFFLOAD;
5764d95d6320SEric Dumazet 		if (READ_ONCE(rt->trap))
5765bb3c4ab9SIdo Schimmel 			rtm->rtm_flags |= RTM_F_TRAP;
5766d95d6320SEric Dumazet 		if (READ_ONCE(rt->offload_failed))
57670c5fcf9eSAmit Cohen 			rtm->rtm_flags |= RTM_F_OFFLOAD_FAILED;
5768bb3c4ab9SIdo Schimmel 	}
5769bb3c4ab9SIdo Schimmel 
5770d4ead6b3SDavid Ahern 	if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
5771e3703b3dSThomas Graf 		goto nla_put_failure;
57721da177e4SLinus Torvalds 
577322d0bd82SXin Long 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
5774c78ba6d6SLubomir Rintel 		goto nla_put_failure;
5775c78ba6d6SLubomir Rintel 
577619e42e45SRoopa Prabhu 
5777053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
5778053c095aSJohannes Berg 	return 0;
57792d7202bfSThomas Graf 
57802d7202bfSThomas Graf nla_put_failure:
578126932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
578226932566SPatrick McHardy 	return -EMSGSIZE;
57831da177e4SLinus Torvalds }
57841da177e4SLinus Torvalds 
fib6_info_nh_uses_dev(struct fib6_nh * nh,void * arg)57852c170e07SDavid Ahern static int fib6_info_nh_uses_dev(struct fib6_nh *nh, void *arg)
57862c170e07SDavid Ahern {
57872c170e07SDavid Ahern 	const struct net_device *dev = arg;
57882c170e07SDavid Ahern 
57892c170e07SDavid Ahern 	if (nh->fib_nh_dev == dev)
57902c170e07SDavid Ahern 		return 1;
57912c170e07SDavid Ahern 
57922c170e07SDavid Ahern 	return 0;
57932c170e07SDavid Ahern }
57942c170e07SDavid Ahern 
fib6_info_uses_dev(const struct fib6_info * f6i,const struct net_device * dev)579513e38901SDavid Ahern static bool fib6_info_uses_dev(const struct fib6_info *f6i,
579613e38901SDavid Ahern 			       const struct net_device *dev)
579713e38901SDavid Ahern {
57982c170e07SDavid Ahern 	if (f6i->nh) {
57992c170e07SDavid Ahern 		struct net_device *_dev = (struct net_device *)dev;
58002c170e07SDavid Ahern 
58012c170e07SDavid Ahern 		return !!nexthop_for_each_fib6_nh(f6i->nh,
58022c170e07SDavid Ahern 						  fib6_info_nh_uses_dev,
58032c170e07SDavid Ahern 						  _dev);
58042c170e07SDavid Ahern 	}
58052c170e07SDavid Ahern 
58061cf844c7SDavid Ahern 	if (f6i->fib6_nh->fib_nh_dev == dev)
580713e38901SDavid Ahern 		return true;
580813e38901SDavid Ahern 
580913e38901SDavid Ahern 	if (f6i->fib6_nsiblings) {
581013e38901SDavid Ahern 		struct fib6_info *sibling, *next_sibling;
581113e38901SDavid Ahern 
581213e38901SDavid Ahern 		list_for_each_entry_safe(sibling, next_sibling,
581313e38901SDavid Ahern 					 &f6i->fib6_siblings, fib6_siblings) {
58141cf844c7SDavid Ahern 			if (sibling->fib6_nh->fib_nh_dev == dev)
581513e38901SDavid Ahern 				return true;
581613e38901SDavid Ahern 		}
581713e38901SDavid Ahern 	}
581813e38901SDavid Ahern 
581913e38901SDavid Ahern 	return false;
582013e38901SDavid Ahern }
582113e38901SDavid Ahern 
58221e47b483SStefano Brivio struct fib6_nh_exception_dump_walker {
58231e47b483SStefano Brivio 	struct rt6_rtnl_dump_arg *dump;
58241e47b483SStefano Brivio 	struct fib6_info *rt;
58251e47b483SStefano Brivio 	unsigned int flags;
58261e47b483SStefano Brivio 	unsigned int skip;
58271e47b483SStefano Brivio 	unsigned int count;
58281e47b483SStefano Brivio };
58291e47b483SStefano Brivio 
rt6_nh_dump_exceptions(struct fib6_nh * nh,void * arg)58301e47b483SStefano Brivio static int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg)
58311e47b483SStefano Brivio {
58321e47b483SStefano Brivio 	struct fib6_nh_exception_dump_walker *w = arg;
58331e47b483SStefano Brivio 	struct rt6_rtnl_dump_arg *dump = w->dump;
58341e47b483SStefano Brivio 	struct rt6_exception_bucket *bucket;
58351e47b483SStefano Brivio 	struct rt6_exception *rt6_ex;
58361e47b483SStefano Brivio 	int i, err;
58371e47b483SStefano Brivio 
58381e47b483SStefano Brivio 	bucket = fib6_nh_get_excptn_bucket(nh, NULL);
58391e47b483SStefano Brivio 	if (!bucket)
58401e47b483SStefano Brivio 		return 0;
58411e47b483SStefano Brivio 
58421e47b483SStefano Brivio 	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
58431e47b483SStefano Brivio 		hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
58441e47b483SStefano Brivio 			if (w->skip) {
58451e47b483SStefano Brivio 				w->skip--;
58461e47b483SStefano Brivio 				continue;
58471e47b483SStefano Brivio 			}
58481e47b483SStefano Brivio 
58491e47b483SStefano Brivio 			/* Expiration of entries doesn't bump sernum, insertion
58501e47b483SStefano Brivio 			 * does. Removal is triggered by insertion, so we can
58511e47b483SStefano Brivio 			 * rely on the fact that if entries change between two
58521e47b483SStefano Brivio 			 * partial dumps, this node is scanned again completely,
58531e47b483SStefano Brivio 			 * see rt6_insert_exception() and fib6_dump_table().
58541e47b483SStefano Brivio 			 *
58551e47b483SStefano Brivio 			 * Count expired entries we go through as handled
58561e47b483SStefano Brivio 			 * entries that we'll skip next time, in case of partial
58571e47b483SStefano Brivio 			 * node dump. Otherwise, if entries expire meanwhile,
58581e47b483SStefano Brivio 			 * we'll skip the wrong amount.
58591e47b483SStefano Brivio 			 */
58601e47b483SStefano Brivio 			if (rt6_check_expired(rt6_ex->rt6i)) {
58611e47b483SStefano Brivio 				w->count++;
58621e47b483SStefano Brivio 				continue;
58631e47b483SStefano Brivio 			}
58641e47b483SStefano Brivio 
58651e47b483SStefano Brivio 			err = rt6_fill_node(dump->net, dump->skb, w->rt,
58661e47b483SStefano Brivio 					    &rt6_ex->rt6i->dst, NULL, NULL, 0,
58671e47b483SStefano Brivio 					    RTM_NEWROUTE,
58681e47b483SStefano Brivio 					    NETLINK_CB(dump->cb->skb).portid,
58691e47b483SStefano Brivio 					    dump->cb->nlh->nlmsg_seq, w->flags);
58701e47b483SStefano Brivio 			if (err)
58711e47b483SStefano Brivio 				return err;
58721e47b483SStefano Brivio 
58731e47b483SStefano Brivio 			w->count++;
58741e47b483SStefano Brivio 		}
58751e47b483SStefano Brivio 		bucket++;
58761e47b483SStefano Brivio 	}
58771e47b483SStefano Brivio 
58781e47b483SStefano Brivio 	return 0;
58791e47b483SStefano Brivio }
58801e47b483SStefano Brivio 
5881bf9a8a06SStefano Brivio /* Return -1 if done with node, number of handled routes on partial dump */
rt6_dump_route(struct fib6_info * rt,void * p_arg,unsigned int skip)58821e47b483SStefano Brivio int rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip)
58831da177e4SLinus Torvalds {
58841da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
588513e38901SDavid Ahern 	struct fib_dump_filter *filter = &arg->filter;
588613e38901SDavid Ahern 	unsigned int flags = NLM_F_MULTI;
58871f17e2f2SDavid Ahern 	struct net *net = arg->net;
58881e47b483SStefano Brivio 	int count = 0;
58891f17e2f2SDavid Ahern 
5890421842edSDavid Ahern 	if (rt == net->ipv6.fib6_null_entry)
5891bf9a8a06SStefano Brivio 		return -1;
58921da177e4SLinus Torvalds 
589313e38901SDavid Ahern 	if ((filter->flags & RTM_F_PREFIX) &&
589493c2fb25SDavid Ahern 	    !(rt->fib6_flags & RTF_PREFIX_RT)) {
5895f8cfe2ceSDavid Ahern 		/* success since this is not a prefix route */
5896bf9a8a06SStefano Brivio 		return -1;
5897f8cfe2ceSDavid Ahern 	}
58981e47b483SStefano Brivio 	if (filter->filter_set &&
58991e47b483SStefano Brivio 	    ((filter->rt_type  && rt->fib6_type != filter->rt_type) ||
590013e38901SDavid Ahern 	     (filter->dev      && !fib6_info_uses_dev(rt, filter->dev)) ||
59011e47b483SStefano Brivio 	     (filter->protocol && rt->fib6_protocol != filter->protocol))) {
5902bf9a8a06SStefano Brivio 		return -1;
590313e38901SDavid Ahern 	}
59041e47b483SStefano Brivio 
59051e47b483SStefano Brivio 	if (filter->filter_set ||
59061e47b483SStefano Brivio 	    !filter->dump_routes || !filter->dump_exceptions) {
590713e38901SDavid Ahern 		flags |= NLM_F_DUMP_FILTERED;
5908f8cfe2ceSDavid Ahern 	}
59091da177e4SLinus Torvalds 
59101e47b483SStefano Brivio 	if (filter->dump_routes) {
59111e47b483SStefano Brivio 		if (skip) {
59121e47b483SStefano Brivio 			skip--;
59131e47b483SStefano Brivio 		} else {
59141e47b483SStefano Brivio 			if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL,
59151e47b483SStefano Brivio 					  0, RTM_NEWROUTE,
5916bf9a8a06SStefano Brivio 					  NETLINK_CB(arg->cb->skb).portid,
59171e47b483SStefano Brivio 					  arg->cb->nlh->nlmsg_seq, flags)) {
5918bf9a8a06SStefano Brivio 				return 0;
59191e47b483SStefano Brivio 			}
59201e47b483SStefano Brivio 			count++;
59211e47b483SStefano Brivio 		}
59221e47b483SStefano Brivio 	}
59231e47b483SStefano Brivio 
59241e47b483SStefano Brivio 	if (filter->dump_exceptions) {
59251e47b483SStefano Brivio 		struct fib6_nh_exception_dump_walker w = { .dump = arg,
59261e47b483SStefano Brivio 							   .rt = rt,
59271e47b483SStefano Brivio 							   .flags = flags,
59281e47b483SStefano Brivio 							   .skip = skip,
59291e47b483SStefano Brivio 							   .count = 0 };
59301e47b483SStefano Brivio 		int err;
59311e47b483SStefano Brivio 
59323b525691SEric Dumazet 		rcu_read_lock();
59331e47b483SStefano Brivio 		if (rt->nh) {
59341e47b483SStefano Brivio 			err = nexthop_for_each_fib6_nh(rt->nh,
59351e47b483SStefano Brivio 						       rt6_nh_dump_exceptions,
59361e47b483SStefano Brivio 						       &w);
59371e47b483SStefano Brivio 		} else {
59381e47b483SStefano Brivio 			err = rt6_nh_dump_exceptions(rt->fib6_nh, &w);
59391e47b483SStefano Brivio 		}
59403b525691SEric Dumazet 		rcu_read_unlock();
59411e47b483SStefano Brivio 
59421e47b483SStefano Brivio 		if (err)
594374fd304fSColin Ian King 			return count + w.count;
59441e47b483SStefano Brivio 	}
5945bf9a8a06SStefano Brivio 
5946bf9a8a06SStefano Brivio 	return -1;
59471da177e4SLinus Torvalds }
59481da177e4SLinus Torvalds 
inet6_rtm_valid_getroute_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)59490eff0a27SJakub Kicinski static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
59500eff0a27SJakub Kicinski 					const struct nlmsghdr *nlh,
59510eff0a27SJakub Kicinski 					struct nlattr **tb,
59520eff0a27SJakub Kicinski 					struct netlink_ext_ack *extack)
59530eff0a27SJakub Kicinski {
59540eff0a27SJakub Kicinski 	struct rtmsg *rtm;
59550eff0a27SJakub Kicinski 	int i, err;
59560eff0a27SJakub Kicinski 
59570eff0a27SJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
59580eff0a27SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack,
59590eff0a27SJakub Kicinski 				   "Invalid header for get route request");
59600eff0a27SJakub Kicinski 		return -EINVAL;
59610eff0a27SJakub Kicinski 	}
59620eff0a27SJakub Kicinski 
59630eff0a27SJakub Kicinski 	if (!netlink_strict_get_check(skb))
59648cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
59650eff0a27SJakub Kicinski 					      rtm_ipv6_policy, extack);
59660eff0a27SJakub Kicinski 
59670eff0a27SJakub Kicinski 	rtm = nlmsg_data(nlh);
59680eff0a27SJakub Kicinski 	if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
59690eff0a27SJakub Kicinski 	    (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
59700eff0a27SJakub Kicinski 	    rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
59710eff0a27SJakub Kicinski 	    rtm->rtm_type) {
59720eff0a27SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
59730eff0a27SJakub Kicinski 		return -EINVAL;
59740eff0a27SJakub Kicinski 	}
59750eff0a27SJakub Kicinski 	if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
59760eff0a27SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack,
59770eff0a27SJakub Kicinski 				   "Invalid flags for get route request");
59780eff0a27SJakub Kicinski 		return -EINVAL;
59790eff0a27SJakub Kicinski 	}
59800eff0a27SJakub Kicinski 
59818cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
59820eff0a27SJakub Kicinski 					    rtm_ipv6_policy, extack);
59830eff0a27SJakub Kicinski 	if (err)
59840eff0a27SJakub Kicinski 		return err;
59850eff0a27SJakub Kicinski 
59860eff0a27SJakub Kicinski 	if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
59870eff0a27SJakub Kicinski 	    (tb[RTA_DST] && !rtm->rtm_dst_len)) {
59880eff0a27SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
59890eff0a27SJakub Kicinski 		return -EINVAL;
59900eff0a27SJakub Kicinski 	}
59910eff0a27SJakub Kicinski 
59920eff0a27SJakub Kicinski 	for (i = 0; i <= RTA_MAX; i++) {
59930eff0a27SJakub Kicinski 		if (!tb[i])
59940eff0a27SJakub Kicinski 			continue;
59950eff0a27SJakub Kicinski 
59960eff0a27SJakub Kicinski 		switch (i) {
59970eff0a27SJakub Kicinski 		case RTA_SRC:
59980eff0a27SJakub Kicinski 		case RTA_DST:
59990eff0a27SJakub Kicinski 		case RTA_IIF:
60000eff0a27SJakub Kicinski 		case RTA_OIF:
60010eff0a27SJakub Kicinski 		case RTA_MARK:
60020eff0a27SJakub Kicinski 		case RTA_UID:
60030eff0a27SJakub Kicinski 		case RTA_SPORT:
60040eff0a27SJakub Kicinski 		case RTA_DPORT:
60050eff0a27SJakub Kicinski 		case RTA_IP_PROTO:
60060eff0a27SJakub Kicinski 			break;
60070eff0a27SJakub Kicinski 		default:
60080eff0a27SJakub Kicinski 			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
60090eff0a27SJakub Kicinski 			return -EINVAL;
60100eff0a27SJakub Kicinski 		}
60110eff0a27SJakub Kicinski 	}
60120eff0a27SJakub Kicinski 
60130eff0a27SJakub Kicinski 	return 0;
60140eff0a27SJakub Kicinski }
60150eff0a27SJakub Kicinski 
inet6_rtm_getroute(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)6016c21ef3e3SDavid Ahern static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
6017c21ef3e3SDavid Ahern 			      struct netlink_ext_ack *extack)
60181da177e4SLinus Torvalds {
60193b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
6020ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
602118c3a61cSRoopa Prabhu 	int err, iif = 0, oif = 0;
6022a68886a6SDavid Ahern 	struct fib6_info *from;
602318c3a61cSRoopa Prabhu 	struct dst_entry *dst;
60241da177e4SLinus Torvalds 	struct rt6_info *rt;
6025ab364a6fSThomas Graf 	struct sk_buff *skb;
6026ab364a6fSThomas Graf 	struct rtmsg *rtm;
6027744486d4SMaciej Żenczykowski 	struct flowi6 fl6 = {};
602818c3a61cSRoopa Prabhu 	bool fibmatch;
6029ab364a6fSThomas Graf 
60300eff0a27SJakub Kicinski 	err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
6031ab364a6fSThomas Graf 	if (err < 0)
6032ab364a6fSThomas Graf 		goto errout;
6033ab364a6fSThomas Graf 
6034ab364a6fSThomas Graf 	err = -EINVAL;
603538b7097bSHannes Frederic Sowa 	rtm = nlmsg_data(nlh);
603638b7097bSHannes Frederic Sowa 	fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
603718c3a61cSRoopa Prabhu 	fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
6038ab364a6fSThomas Graf 
6039ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
6040ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
6041ab364a6fSThomas Graf 			goto errout;
6042ab364a6fSThomas Graf 
60434e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
6044ab364a6fSThomas Graf 	}
6045ab364a6fSThomas Graf 
6046ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
6047ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
6048ab364a6fSThomas Graf 			goto errout;
6049ab364a6fSThomas Graf 
60504e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
6051ab364a6fSThomas Graf 	}
6052ab364a6fSThomas Graf 
6053ab364a6fSThomas Graf 	if (tb[RTA_IIF])
6054ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
6055ab364a6fSThomas Graf 
6056ab364a6fSThomas Graf 	if (tb[RTA_OIF])
605772331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
6058ab364a6fSThomas Graf 
60592e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
60602e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
60612e47b291SLorenzo Colitti 
6062622ec2c9SLorenzo Colitti 	if (tb[RTA_UID])
6063622ec2c9SLorenzo Colitti 		fl6.flowi6_uid = make_kuid(current_user_ns(),
6064622ec2c9SLorenzo Colitti 					   nla_get_u32(tb[RTA_UID]));
6065622ec2c9SLorenzo Colitti 	else
6066622ec2c9SLorenzo Colitti 		fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
6067622ec2c9SLorenzo Colitti 
6068eacb9384SRoopa Prabhu 	if (tb[RTA_SPORT])
6069eacb9384SRoopa Prabhu 		fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
6070eacb9384SRoopa Prabhu 
6071eacb9384SRoopa Prabhu 	if (tb[RTA_DPORT])
6072eacb9384SRoopa Prabhu 		fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
6073eacb9384SRoopa Prabhu 
6074eacb9384SRoopa Prabhu 	if (tb[RTA_IP_PROTO]) {
6075eacb9384SRoopa Prabhu 		err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
60765e1a99eaSHangbin Liu 						  &fl6.flowi6_proto, AF_INET6,
60775e1a99eaSHangbin Liu 						  extack);
6078eacb9384SRoopa Prabhu 		if (err)
6079eacb9384SRoopa Prabhu 			goto errout;
6080eacb9384SRoopa Prabhu 	}
6081eacb9384SRoopa Prabhu 
6082ab364a6fSThomas Graf 	if (iif) {
6083ab364a6fSThomas Graf 		struct net_device *dev;
608472331bc0SShmulik Ladkani 		int flags = 0;
608572331bc0SShmulik Ladkani 
6086121622dbSFlorian Westphal 		rcu_read_lock();
6087121622dbSFlorian Westphal 
6088121622dbSFlorian Westphal 		dev = dev_get_by_index_rcu(net, iif);
6089ab364a6fSThomas Graf 		if (!dev) {
6090121622dbSFlorian Westphal 			rcu_read_unlock();
6091ab364a6fSThomas Graf 			err = -ENODEV;
6092ab364a6fSThomas Graf 			goto errout;
6093ab364a6fSThomas Graf 		}
609472331bc0SShmulik Ladkani 
609572331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
609672331bc0SShmulik Ladkani 
609772331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
609872331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
609972331bc0SShmulik Ladkani 
6100b75cc8f9SDavid Ahern 		dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
6101121622dbSFlorian Westphal 
6102121622dbSFlorian Westphal 		rcu_read_unlock();
610372331bc0SShmulik Ladkani 	} else {
610472331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
610572331bc0SShmulik Ladkani 
610618c3a61cSRoopa Prabhu 		dst = ip6_route_output(net, NULL, &fl6);
610718c3a61cSRoopa Prabhu 	}
610818c3a61cSRoopa Prabhu 
610918c3a61cSRoopa Prabhu 
6110797a4c1fSEric Dumazet 	rt = dst_rt6_info(dst);
611118c3a61cSRoopa Prabhu 	if (rt->dst.error) {
611218c3a61cSRoopa Prabhu 		err = rt->dst.error;
611318c3a61cSRoopa Prabhu 		ip6_rt_put(rt);
611418c3a61cSRoopa Prabhu 		goto errout;
6115ab364a6fSThomas Graf 	}
61161da177e4SLinus Torvalds 
61179d6acb3bSWANG Cong 	if (rt == net->ipv6.ip6_null_entry) {
61189d6acb3bSWANG Cong 		err = rt->dst.error;
61199d6acb3bSWANG Cong 		ip6_rt_put(rt);
61209d6acb3bSWANG Cong 		goto errout;
61219d6acb3bSWANG Cong 	}
61229d6acb3bSWANG Cong 
61231da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
612438308473SDavid S. Miller 	if (!skb) {
612594e187c0SAmerigo Wang 		ip6_rt_put(rt);
6126ab364a6fSThomas Graf 		err = -ENOBUFS;
6127ab364a6fSThomas Graf 		goto errout;
6128ab364a6fSThomas Graf 	}
61291da177e4SLinus Torvalds 
6130d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
6131a68886a6SDavid Ahern 
6132a68886a6SDavid Ahern 	rcu_read_lock();
6133a68886a6SDavid Ahern 	from = rcu_dereference(rt->from);
6134886b7a50SMartin KaFai Lau 	if (from) {
613518c3a61cSRoopa Prabhu 		if (fibmatch)
6136886b7a50SMartin KaFai Lau 			err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
6137886b7a50SMartin KaFai Lau 					    iif, RTM_NEWROUTE,
6138886b7a50SMartin KaFai Lau 					    NETLINK_CB(in_skb).portid,
613918c3a61cSRoopa Prabhu 					    nlh->nlmsg_seq, 0);
614018c3a61cSRoopa Prabhu 		else
6141a68886a6SDavid Ahern 			err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
6142a68886a6SDavid Ahern 					    &fl6.saddr, iif, RTM_NEWROUTE,
6143886b7a50SMartin KaFai Lau 					    NETLINK_CB(in_skb).portid,
6144886b7a50SMartin KaFai Lau 					    nlh->nlmsg_seq, 0);
6145886b7a50SMartin KaFai Lau 	} else {
6146886b7a50SMartin KaFai Lau 		err = -ENETUNREACH;
6147886b7a50SMartin KaFai Lau 	}
6148a68886a6SDavid Ahern 	rcu_read_unlock();
6149a68886a6SDavid Ahern 
61501da177e4SLinus Torvalds 	if (err < 0) {
6151ab364a6fSThomas Graf 		kfree_skb(skb);
6152ab364a6fSThomas Graf 		goto errout;
61531da177e4SLinus Torvalds 	}
61541da177e4SLinus Torvalds 
615515e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
6156ab364a6fSThomas Graf errout:
61571da177e4SLinus Torvalds 	return err;
61581da177e4SLinus Torvalds }
61591da177e4SLinus Torvalds 
inet6_rt_notify(int event,struct fib6_info * rt,struct nl_info * info,unsigned int nlm_flags)61608d1c802bSDavid Ahern void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
616137a1d361SRoopa Prabhu 		     unsigned int nlm_flags)
61621da177e4SLinus Torvalds {
61631da177e4SLinus Torvalds 	struct sk_buff *skb;
61645578689aSDaniel Lezcano 	struct net *net = info->nl_net;
6165528c4cebSDenis V. Lunev 	u32 seq;
6166528c4cebSDenis V. Lunev 	int err;
61670d51aa80SJamal Hadi Salim 
6168528c4cebSDenis V. Lunev 	err = -ENOBUFS;
616938308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
617086872cb5SThomas Graf 
617119e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
617238308473SDavid S. Miller 	if (!skb)
617321713ebcSThomas Graf 		goto errout;
61741da177e4SLinus Torvalds 
6175d4ead6b3SDavid Ahern 	err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
6176f8cfe2ceSDavid Ahern 			    event, info->portid, seq, nlm_flags);
617726932566SPatrick McHardy 	if (err < 0) {
617826932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
617926932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
618026932566SPatrick McHardy 		kfree_skb(skb);
618126932566SPatrick McHardy 		goto errout;
618226932566SPatrick McHardy 	}
618315e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
61845578689aSDaniel Lezcano 		    info->nlh, gfp_any());
61851ce85fe4SPablo Neira Ayuso 	return;
618621713ebcSThomas Graf errout:
618721713ebcSThomas Graf 	if (err < 0)
61885578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
61891da177e4SLinus Torvalds }
61901da177e4SLinus Torvalds 
fib6_rt_update(struct net * net,struct fib6_info * rt,struct nl_info * info)619119a3b7eeSDavid Ahern void fib6_rt_update(struct net *net, struct fib6_info *rt,
619219a3b7eeSDavid Ahern 		    struct nl_info *info)
619319a3b7eeSDavid Ahern {
619419a3b7eeSDavid Ahern 	u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
619519a3b7eeSDavid Ahern 	struct sk_buff *skb;
619619a3b7eeSDavid Ahern 	int err = -ENOBUFS;
619719a3b7eeSDavid Ahern 
619819a3b7eeSDavid Ahern 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
619919a3b7eeSDavid Ahern 	if (!skb)
620019a3b7eeSDavid Ahern 		goto errout;
620119a3b7eeSDavid Ahern 
620219a3b7eeSDavid Ahern 	err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
620319a3b7eeSDavid Ahern 			    RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE);
620419a3b7eeSDavid Ahern 	if (err < 0) {
620519a3b7eeSDavid Ahern 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
620619a3b7eeSDavid Ahern 		WARN_ON(err == -EMSGSIZE);
620719a3b7eeSDavid Ahern 		kfree_skb(skb);
620819a3b7eeSDavid Ahern 		goto errout;
620919a3b7eeSDavid Ahern 	}
621019a3b7eeSDavid Ahern 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
621119a3b7eeSDavid Ahern 		    info->nlh, gfp_any());
621219a3b7eeSDavid Ahern 	return;
621319a3b7eeSDavid Ahern errout:
621419a3b7eeSDavid Ahern 	if (err < 0)
621519a3b7eeSDavid Ahern 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
621619a3b7eeSDavid Ahern }
621719a3b7eeSDavid Ahern 
fib6_info_hw_flags_set(struct net * net,struct fib6_info * f6i,bool offload,bool trap,bool offload_failed)6218907eea48SAmit Cohen void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
62190c5fcf9eSAmit Cohen 			    bool offload, bool trap, bool offload_failed)
6220907eea48SAmit Cohen {
6221907eea48SAmit Cohen 	struct sk_buff *skb;
6222907eea48SAmit Cohen 	int err;
6223907eea48SAmit Cohen 
6224d95d6320SEric Dumazet 	if (READ_ONCE(f6i->offload) == offload &&
6225d95d6320SEric Dumazet 	    READ_ONCE(f6i->trap) == trap &&
6226d95d6320SEric Dumazet 	    READ_ONCE(f6i->offload_failed) == offload_failed)
6227907eea48SAmit Cohen 		return;
6228907eea48SAmit Cohen 
6229d95d6320SEric Dumazet 	WRITE_ONCE(f6i->offload, offload);
6230d95d6320SEric Dumazet 	WRITE_ONCE(f6i->trap, trap);
62316fad361aSAmit Cohen 
62326fad361aSAmit Cohen 	/* 2 means send notifications only if offload_failed was changed. */
62336fad361aSAmit Cohen 	if (net->ipv6.sysctl.fib_notify_on_flag_change == 2 &&
6234d95d6320SEric Dumazet 	    READ_ONCE(f6i->offload_failed) == offload_failed)
62356fad361aSAmit Cohen 		return;
62366fad361aSAmit Cohen 
6237d95d6320SEric Dumazet 	WRITE_ONCE(f6i->offload_failed, offload_failed);
6238907eea48SAmit Cohen 
6239907eea48SAmit Cohen 	if (!rcu_access_pointer(f6i->fib6_node))
6240907eea48SAmit Cohen 		/* The route was removed from the tree, do not send
624189e8347fSBhaskar Chowdhury 		 * notification.
6242907eea48SAmit Cohen 		 */
6243907eea48SAmit Cohen 		return;
6244907eea48SAmit Cohen 
6245907eea48SAmit Cohen 	if (!net->ipv6.sysctl.fib_notify_on_flag_change)
6246907eea48SAmit Cohen 		return;
6247907eea48SAmit Cohen 
6248907eea48SAmit Cohen 	skb = nlmsg_new(rt6_nlmsg_size(f6i), GFP_KERNEL);
6249907eea48SAmit Cohen 	if (!skb) {
6250907eea48SAmit Cohen 		err = -ENOBUFS;
6251907eea48SAmit Cohen 		goto errout;
6252907eea48SAmit Cohen 	}
6253907eea48SAmit Cohen 
6254907eea48SAmit Cohen 	err = rt6_fill_node(net, skb, f6i, NULL, NULL, NULL, 0, RTM_NEWROUTE, 0,
6255907eea48SAmit Cohen 			    0, 0);
6256907eea48SAmit Cohen 	if (err < 0) {
6257907eea48SAmit Cohen 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
6258907eea48SAmit Cohen 		WARN_ON(err == -EMSGSIZE);
6259907eea48SAmit Cohen 		kfree_skb(skb);
6260907eea48SAmit Cohen 		goto errout;
6261907eea48SAmit Cohen 	}
6262907eea48SAmit Cohen 
6263907eea48SAmit Cohen 	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_ROUTE, NULL, GFP_KERNEL);
6264907eea48SAmit Cohen 	return;
6265907eea48SAmit Cohen 
6266907eea48SAmit Cohen errout:
6267907eea48SAmit Cohen 	rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
6268907eea48SAmit Cohen }
6269907eea48SAmit Cohen EXPORT_SYMBOL(fib6_info_hw_flags_set);
6270907eea48SAmit Cohen 
ip6_route_dev_notify(struct notifier_block * this,unsigned long event,void * ptr)62718ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
6272351638e7SJiri Pirko 				unsigned long event, void *ptr)
62738ed67789SDaniel Lezcano {
6274351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
6275c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
62768ed67789SDaniel Lezcano 
6277242d3a49SWANG Cong 	if (!(dev->flags & IFF_LOOPBACK))
6278242d3a49SWANG Cong 		return NOTIFY_OK;
6279242d3a49SWANG Cong 
6280242d3a49SWANG Cong 	if (event == NETDEV_REGISTER) {
62811cf844c7SDavid Ahern 		net->ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = dev;
6282d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
62838ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
62848ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
6285d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
62868ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
6287d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
62888ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
62898ed67789SDaniel Lezcano #endif
629076da0704SWANG Cong 	 } else if (event == NETDEV_UNREGISTER &&
629176da0704SWANG Cong 		    dev->reg_state != NETREG_UNREGISTERED) {
629276da0704SWANG Cong 		/* NETDEV_UNREGISTER could be fired for multiple times by
629376da0704SWANG Cong 		 * netdev_wait_allrefs(). Make sure we only call this once.
629476da0704SWANG Cong 		 */
629512d94a80SEric Dumazet 		in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
6296242d3a49SWANG Cong #ifdef CONFIG_IPV6_MULTIPLE_TABLES
629712d94a80SEric Dumazet 		in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
629812d94a80SEric Dumazet 		in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
6299242d3a49SWANG Cong #endif
63008ed67789SDaniel Lezcano 	}
63018ed67789SDaniel Lezcano 
63028ed67789SDaniel Lezcano 	return NOTIFY_OK;
63038ed67789SDaniel Lezcano }
63048ed67789SDaniel Lezcano 
63051da177e4SLinus Torvalds /*
63061da177e4SLinus Torvalds  *	/proc
63071da177e4SLinus Torvalds  */
63081da177e4SLinus Torvalds 
63091da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
rt6_stats_seq_show(struct seq_file * seq,void * v)63101da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
63111da177e4SLinus Torvalds {
631269ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
63131da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
631469ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
631569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
631681eb8447SWei Wang 		   atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
631769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
631869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
6319fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
632069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
63211da177e4SLinus Torvalds 
63221da177e4SLinus Torvalds 	return 0;
63231da177e4SLinus Torvalds }
63241da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
63251da177e4SLinus Torvalds 
63261da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
63271da177e4SLinus Torvalds 
ipv6_sysctl_rtcache_flush(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)632832927393SChristoph Hellwig static int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
632932927393SChristoph Hellwig 			      void *buffer, size_t *lenp, loff_t *ppos)
63301da177e4SLinus Torvalds {
6331c486da34SLucian Adrian Grijincu 	struct net *net;
6332c486da34SLucian Adrian Grijincu 	int delay;
6333f0fb9b28SAditya Pakki 	int ret;
6334c486da34SLucian Adrian Grijincu 	if (!write)
6335c486da34SLucian Adrian Grijincu 		return -EINVAL;
6336c486da34SLucian Adrian Grijincu 
6337f0fb9b28SAditya Pakki 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
6338f0fb9b28SAditya Pakki 	if (ret)
6339f0fb9b28SAditya Pakki 		return ret;
6340f0fb9b28SAditya Pakki 
634196d3265fSPetr Pavlu 	net = (struct net *)ctl->extra1;
634296d3265fSPetr Pavlu 	delay = net->ipv6.sysctl.flush_delay;
63432ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
63441da177e4SLinus Torvalds 	return 0;
63451da177e4SLinus Torvalds }
63461da177e4SLinus Torvalds 
6347ed792e28SDavid Ahern static struct ctl_table ipv6_route_table_template[] = {
63481da177e4SLinus Torvalds 	{
634906e6c88fSAlexander Kuznetsov 		.procname	=	"max_size",
635006e6c88fSAlexander Kuznetsov 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
63511da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
635206e6c88fSAlexander Kuznetsov 		.mode		=	0644,
635306e6c88fSAlexander Kuznetsov 		.proc_handler	=	proc_dointvec,
63541da177e4SLinus Torvalds 	},
63551da177e4SLinus Torvalds 	{
63561da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
63579a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
63581da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
63591da177e4SLinus Torvalds 		.mode		=	0644,
63606d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
63611da177e4SLinus Torvalds 	},
63621da177e4SLinus Torvalds 	{
636306e6c88fSAlexander Kuznetsov 		.procname	=	"flush",
636406e6c88fSAlexander Kuznetsov 		.data		=	&init_net.ipv6.sysctl.flush_delay,
63651da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
636606e6c88fSAlexander Kuznetsov 		.mode		=	0200,
636706e6c88fSAlexander Kuznetsov 		.proc_handler	=	ipv6_sysctl_rtcache_flush
63681da177e4SLinus Torvalds 	},
63691da177e4SLinus Torvalds 	{
63701da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
63714990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
63721da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
63731da177e4SLinus Torvalds 		.mode		=	0644,
63746d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
63751da177e4SLinus Torvalds 	},
63761da177e4SLinus Torvalds 	{
63771da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
63784990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
63791da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
63801da177e4SLinus Torvalds 		.mode		=	0644,
63816d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
63821da177e4SLinus Torvalds 	},
63831da177e4SLinus Torvalds 	{
63841da177e4SLinus Torvalds 		.procname	=	"gc_interval",
63854990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
63861da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
63871da177e4SLinus Torvalds 		.mode		=	0644,
63886d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
63891da177e4SLinus Torvalds 	},
63901da177e4SLinus Torvalds 	{
63911da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
63924990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
63931da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
63941da177e4SLinus Torvalds 		.mode		=	0644,
6395f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
63961da177e4SLinus Torvalds 	},
63971da177e4SLinus Torvalds 	{
63981da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
63994990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
64001da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
64011da177e4SLinus Torvalds 		.mode		=	0644,
64026d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
64031da177e4SLinus Torvalds 	},
64041da177e4SLinus Torvalds 	{
64051da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
64064990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
64071da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
64081da177e4SLinus Torvalds 		.mode		=	0644,
6409f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
64101da177e4SLinus Torvalds 	},
64111da177e4SLinus Torvalds 	{
64121da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
64134990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
64141da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
64151da177e4SLinus Torvalds 		.mode		=	0644,
64166d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
64171da177e4SLinus Torvalds 	},
64187c6bb7d2SDavid Ahern 	{
64197c6bb7d2SDavid Ahern 		.procname	=	"skip_notify_on_dev_down",
64207c6bb7d2SDavid Ahern 		.data		=	&init_net.ipv6.sysctl.skip_notify_on_dev_down,
6421ef62c0aeSEric Dumazet 		.maxlen		=	sizeof(u8),
64227c6bb7d2SDavid Ahern 		.mode		=	0644,
6423ef62c0aeSEric Dumazet 		.proc_handler	=	proc_dou8vec_minmax,
6424eec4844fSMatteo Croce 		.extra1		=	SYSCTL_ZERO,
6425eec4844fSMatteo Croce 		.extra2		=	SYSCTL_ONE,
64267c6bb7d2SDavid Ahern 	},
6427f8572d8fSEric W. Biederman 	{ }
64281da177e4SLinus Torvalds };
64291da177e4SLinus Torvalds 
ipv6_route_sysctl_init(struct net * net)64302c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
6431760f2d01SDaniel Lezcano {
6432760f2d01SDaniel Lezcano 	struct ctl_table *table;
6433760f2d01SDaniel Lezcano 
6434760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
6435760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
6436760f2d01SDaniel Lezcano 			GFP_KERNEL);
64375ee09105SYOSHIFUJI Hideaki 
64385ee09105SYOSHIFUJI Hideaki 	if (table) {
643906e6c88fSAlexander Kuznetsov 		table[0].data = &net->ipv6.sysctl.ip6_rt_max_size;
644086393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
644106e6c88fSAlexander Kuznetsov 		table[2].data = &net->ipv6.sysctl.flush_delay;
644206e6c88fSAlexander Kuznetsov 		table[2].extra1 = net;
64435ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
64445ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
64455ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
64465ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
64475ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
64485ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
64499c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
64507c6bb7d2SDavid Ahern 		table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
6451464dc801SEric W. Biederman 
6452464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
6453464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
645406e6c88fSAlexander Kuznetsov 			table[1].procname = NULL;
64555ee09105SYOSHIFUJI Hideaki 	}
64565ee09105SYOSHIFUJI Hideaki 
6457760f2d01SDaniel Lezcano 	return table;
6458760f2d01SDaniel Lezcano }
6459c899710fSJoel Granados 
ipv6_route_sysctl_table_size(struct net * net)6460c899710fSJoel Granados size_t ipv6_route_sysctl_table_size(struct net *net)
6461c899710fSJoel Granados {
6462c899710fSJoel Granados 	/* Don't export sysctls to unprivileged users */
6463c899710fSJoel Granados 	if (net->user_ns != &init_user_ns)
6464c899710fSJoel Granados 		return 1;
6465c899710fSJoel Granados 
6466c899710fSJoel Granados 	return ARRAY_SIZE(ipv6_route_table_template);
6467c899710fSJoel Granados }
64681da177e4SLinus Torvalds #endif
64691da177e4SLinus Torvalds 
ip6_route_net_init(struct net * net)64702c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
6471cdb18761SDaniel Lezcano {
6472633d424bSPavel Emelyanov 	int ret = -ENOMEM;
64738ed67789SDaniel Lezcano 
647486393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
647586393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
6476f2fc6a54SBenjamin Thery 
6477fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
6478fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
6479fc66f95cSEric Dumazet 
64801cf844c7SDavid Ahern 	net->ipv6.fib6_null_entry = fib6_info_alloc(GFP_KERNEL, true);
6481421842edSDavid Ahern 	if (!net->ipv6.fib6_null_entry)
6482421842edSDavid Ahern 		goto out_ip6_dst_entries;
64831cf844c7SDavid Ahern 	memcpy(net->ipv6.fib6_null_entry, &fib6_null_entry_template,
64841cf844c7SDavid Ahern 	       sizeof(*net->ipv6.fib6_null_entry));
6485421842edSDavid Ahern 
64868ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
64878ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
64888ed67789SDaniel Lezcano 					   GFP_KERNEL);
64898ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
6490421842edSDavid Ahern 		goto out_fib6_null_entry;
6491d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
649262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
649362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
6494d288a162SWangyang Guo 	INIT_LIST_HEAD(&net->ipv6.ip6_null_entry->dst.rt_uncached);
64958ed67789SDaniel Lezcano 
64968ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
6497feca7d8cSVincent Bernat 	net->ipv6.fib6_has_custom_rules = false;
64988ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
64998ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
65008ed67789SDaniel Lezcano 					       GFP_KERNEL);
650168fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
650268fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
6503d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
650462fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
650562fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
6506d288a162SWangyang Guo 	INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->dst.rt_uncached);
65078ed67789SDaniel Lezcano 
65088ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
65098ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
65108ed67789SDaniel Lezcano 					       GFP_KERNEL);
651168fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
651268fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
6513d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
651462fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
651562fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
6516d288a162SWangyang Guo 	INIT_LIST_HEAD(&net->ipv6.ip6_blk_hole_entry->dst.rt_uncached);
6517b9b33e7cSPaolo Abeni #ifdef CONFIG_IPV6_SUBTREES
6518b9b33e7cSPaolo Abeni 	net->ipv6.fib6_routes_require_src = 0;
6519b9b33e7cSPaolo Abeni #endif
65208ed67789SDaniel Lezcano #endif
65218ed67789SDaniel Lezcano 
6522b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
6523af6d1034SJon Maxwell 	net->ipv6.sysctl.ip6_rt_max_size = INT_MAX;
6524b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
6525b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
6526b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
6527b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
6528b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
6529b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
65307c6bb7d2SDavid Ahern 	net->ipv6.sysctl.skip_notify_on_dev_down = 0;
6531b339a47cSPeter Zijlstra 
65329cb7c013SEric Dumazet 	atomic_set(&net->ipv6.ip6_rt_gc_expire, 30*HZ);
65336891a346SBenjamin Thery 
65348ed67789SDaniel Lezcano 	ret = 0;
65358ed67789SDaniel Lezcano out:
65368ed67789SDaniel Lezcano 	return ret;
6537f2fc6a54SBenjamin Thery 
653868fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
653968fffc67SPeter Zijlstra out_ip6_prohibit_entry:
654068fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
654168fffc67SPeter Zijlstra out_ip6_null_entry:
654268fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
654368fffc67SPeter Zijlstra #endif
6544421842edSDavid Ahern out_fib6_null_entry:
6545421842edSDavid Ahern 	kfree(net->ipv6.fib6_null_entry);
6546fc66f95cSEric Dumazet out_ip6_dst_entries:
6547fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
6548f2fc6a54SBenjamin Thery out_ip6_dst_ops:
6549f2fc6a54SBenjamin Thery 	goto out;
6550cdb18761SDaniel Lezcano }
6551cdb18761SDaniel Lezcano 
ip6_route_net_exit(struct net * net)65522c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
6553cdb18761SDaniel Lezcano {
6554421842edSDavid Ahern 	kfree(net->ipv6.fib6_null_entry);
65558ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
65568ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
65578ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
65588ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
65598ed67789SDaniel Lezcano #endif
656041bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
6561cdb18761SDaniel Lezcano }
6562cdb18761SDaniel Lezcano 
ip6_route_net_init_late(struct net * net)6563d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
6564d189634eSThomas Graf {
6565d189634eSThomas Graf #ifdef CONFIG_PROC_FS
6566768b3c74SZhengchao Shao 	if (!proc_create_net("ipv6_route", 0, net->proc_net,
6567768b3c74SZhengchao Shao 			     &ipv6_route_seq_ops,
6568768b3c74SZhengchao Shao 			     sizeof(struct ipv6_route_iter)))
6569768b3c74SZhengchao Shao 		return -ENOMEM;
6570768b3c74SZhengchao Shao 
6571768b3c74SZhengchao Shao 	if (!proc_create_net_single("rt6_stats", 0444, net->proc_net,
6572768b3c74SZhengchao Shao 				    rt6_stats_seq_show, NULL)) {
6573768b3c74SZhengchao Shao 		remove_proc_entry("ipv6_route", net->proc_net);
6574768b3c74SZhengchao Shao 		return -ENOMEM;
6575768b3c74SZhengchao Shao 	}
6576d189634eSThomas Graf #endif
6577d189634eSThomas Graf 	return 0;
6578d189634eSThomas Graf }
6579d189634eSThomas Graf 
ip6_route_net_exit_late(struct net * net)6580d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
6581d189634eSThomas Graf {
6582d189634eSThomas Graf #ifdef CONFIG_PROC_FS
6583ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
6584ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
6585d189634eSThomas Graf #endif
6586d189634eSThomas Graf }
6587d189634eSThomas Graf 
6588cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
6589cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
6590cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
6591cdb18761SDaniel Lezcano };
6592cdb18761SDaniel Lezcano 
ipv6_inetpeer_init(struct net * net)6593c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
6594c3426b47SDavid S. Miller {
6595c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
6596c3426b47SDavid S. Miller 
6597c3426b47SDavid S. Miller 	if (!bp)
6598c3426b47SDavid S. Miller 		return -ENOMEM;
6599c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
6600c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
6601c3426b47SDavid S. Miller 	return 0;
6602c3426b47SDavid S. Miller }
6603c3426b47SDavid S. Miller 
ipv6_inetpeer_exit(struct net * net)6604c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
6605c3426b47SDavid S. Miller {
6606c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
6607c3426b47SDavid S. Miller 
6608c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
660956a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
6610c3426b47SDavid S. Miller 	kfree(bp);
6611c3426b47SDavid S. Miller }
6612c3426b47SDavid S. Miller 
66132b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
6614c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
6615c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
6616c3426b47SDavid S. Miller };
6617c3426b47SDavid S. Miller 
6618d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
6619d189634eSThomas Graf 	.init = ip6_route_net_init_late,
6620d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
6621d189634eSThomas Graf };
6622d189634eSThomas Graf 
66238ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
66248ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
6625242d3a49SWANG Cong 	.priority = ADDRCONF_NOTIFY_PRIORITY - 10,
66268ed67789SDaniel Lezcano };
66278ed67789SDaniel Lezcano 
ip6_route_init_special_entries(void)66282f460933SWANG Cong void __init ip6_route_init_special_entries(void)
66292f460933SWANG Cong {
66302f460933SWANG Cong 	/* Registering of the loopback is done before this portion of code,
66312f460933SWANG Cong 	 * the loopback reference in rt6_info will not be taken, do it
66322f460933SWANG Cong 	 * manually for init_net */
66331cf844c7SDavid Ahern 	init_net.ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = init_net.loopback_dev;
66342f460933SWANG Cong 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
66352f460933SWANG Cong 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
66362f460933SWANG Cong   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
66372f460933SWANG Cong 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
66382f460933SWANG Cong 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
66392f460933SWANG Cong 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
66402f460933SWANG Cong 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
66412f460933SWANG Cong   #endif
66422f460933SWANG Cong }
66432f460933SWANG Cong 
6644138d0be3SYonghong Song #if IS_BUILTIN(CONFIG_IPV6)
6645138d0be3SYonghong Song #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
6646138d0be3SYonghong Song DEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt)
6647138d0be3SYonghong Song 
6648951cf368SYonghong Song BTF_ID_LIST(btf_fib6_info_id)
6649951cf368SYonghong Song BTF_ID(struct, fib6_info)
6650951cf368SYonghong Song 
665114fc6bd6SYonghong Song static const struct bpf_iter_seq_info ipv6_route_seq_info = {
6652138d0be3SYonghong Song 	.seq_ops		= &ipv6_route_seq_ops,
6653138d0be3SYonghong Song 	.init_seq_private	= bpf_iter_init_seq_net,
6654138d0be3SYonghong Song 	.fini_seq_private	= bpf_iter_fini_seq_net,
6655138d0be3SYonghong Song 	.seq_priv_size		= sizeof(struct ipv6_route_iter),
665614fc6bd6SYonghong Song };
665714fc6bd6SYonghong Song 
665814fc6bd6SYonghong Song static struct bpf_iter_reg ipv6_route_reg_info = {
665914fc6bd6SYonghong Song 	.target			= "ipv6_route",
66603c32cc1bSYonghong Song 	.ctx_arg_info_size	= 1,
66613c32cc1bSYonghong Song 	.ctx_arg_info		= {
66623c32cc1bSYonghong Song 		{ offsetof(struct bpf_iter__ipv6_route, rt),
66633c32cc1bSYonghong Song 		  PTR_TO_BTF_ID_OR_NULL },
66643c32cc1bSYonghong Song 	},
666514fc6bd6SYonghong Song 	.seq_info		= &ipv6_route_seq_info,
6666138d0be3SYonghong Song };
6667138d0be3SYonghong Song 
bpf_iter_register(void)666815172a46SYonghong Song static int __init bpf_iter_register(void)
666915172a46SYonghong Song {
6670951cf368SYonghong Song 	ipv6_route_reg_info.ctx_arg_info[0].btf_id = *btf_fib6_info_id;
667115172a46SYonghong Song 	return bpf_iter_reg_target(&ipv6_route_reg_info);
6672138d0be3SYonghong Song }
6673138d0be3SYonghong Song 
bpf_iter_unregister(void)6674138d0be3SYonghong Song static void bpf_iter_unregister(void)
6675138d0be3SYonghong Song {
6676ab2ee4fcSYonghong Song 	bpf_iter_unreg_target(&ipv6_route_reg_info);
6677138d0be3SYonghong Song }
6678138d0be3SYonghong Song #endif
6679138d0be3SYonghong Song #endif
6680138d0be3SYonghong Song 
ip6_route_init(void)6681433d49c3SDaniel Lezcano int __init ip6_route_init(void)
66821da177e4SLinus Torvalds {
6683433d49c3SDaniel Lezcano 	int ret;
66848d0b94afSMartin KaFai Lau 	int cpu;
6685433d49c3SDaniel Lezcano 
66869a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
66879a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
66889a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
66896126891cSVasily Averin 				  SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);
66909a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
6691c19a28e1SFernando Carrijo 		goto out;
669214e50e57SDavid S. Miller 
6693fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
66948ed67789SDaniel Lezcano 	if (ret)
6695bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
6696bdb3289fSDaniel Lezcano 
6697c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
6698c3426b47SDavid S. Miller 	if (ret)
6699e8803b6cSDavid S. Miller 		goto out_dst_entries;
67002a0c451aSThomas Graf 
67017e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
67027e52b33bSDavid S. Miller 	if (ret)
67037e52b33bSDavid S. Miller 		goto out_register_inetpeer;
6704c3426b47SDavid S. Miller 
67055dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
67065dc121e9SArnaud Ebalard 
6707e8803b6cSDavid S. Miller 	ret = fib6_init();
6708433d49c3SDaniel Lezcano 	if (ret)
67098ed67789SDaniel Lezcano 		goto out_register_subsys;
6710433d49c3SDaniel Lezcano 
6711433d49c3SDaniel Lezcano 	ret = xfrm6_init();
6712433d49c3SDaniel Lezcano 	if (ret)
6713e8803b6cSDavid S. Miller 		goto out_fib6_init;
6714c35b7e72SDaniel Lezcano 
6715433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
6716433d49c3SDaniel Lezcano 	if (ret)
6717433d49c3SDaniel Lezcano 		goto xfrm6_init;
67187e5449c2SDaniel Lezcano 
6719d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
6720d189634eSThomas Graf 	if (ret)
6721d189634eSThomas Graf 		goto fib6_rules_init;
6722d189634eSThomas Graf 
672316feebcfSFlorian Westphal 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
672416feebcfSFlorian Westphal 				   inet6_rtm_newroute, NULL, 0);
672516feebcfSFlorian Westphal 	if (ret < 0)
672616feebcfSFlorian Westphal 		goto out_register_late_subsys;
672716feebcfSFlorian Westphal 
672816feebcfSFlorian Westphal 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
672916feebcfSFlorian Westphal 				   inet6_rtm_delroute, NULL, 0);
673016feebcfSFlorian Westphal 	if (ret < 0)
673116feebcfSFlorian Westphal 		goto out_register_late_subsys;
673216feebcfSFlorian Westphal 
673316feebcfSFlorian Westphal 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
673416feebcfSFlorian Westphal 				   inet6_rtm_getroute, NULL,
673516feebcfSFlorian Westphal 				   RTNL_FLAG_DOIT_UNLOCKED);
673616feebcfSFlorian Westphal 	if (ret < 0)
6737d189634eSThomas Graf 		goto out_register_late_subsys;
6738433d49c3SDaniel Lezcano 
67398ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
6740cdb18761SDaniel Lezcano 	if (ret)
6741d189634eSThomas Graf 		goto out_register_late_subsys;
67428ed67789SDaniel Lezcano 
6743138d0be3SYonghong Song #if IS_BUILTIN(CONFIG_IPV6)
6744138d0be3SYonghong Song #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
6745138d0be3SYonghong Song 	ret = bpf_iter_register();
6746138d0be3SYonghong Song 	if (ret)
6747138d0be3SYonghong Song 		goto out_register_late_subsys;
6748138d0be3SYonghong Song #endif
6749138d0be3SYonghong Song #endif
6750138d0be3SYonghong Song 
67518d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
67528d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
67538d0b94afSMartin KaFai Lau 
67548d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
6755ba55ef81SEric Dumazet 		INIT_LIST_HEAD(&ul->quarantine);
67568d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
67578d0b94afSMartin KaFai Lau 	}
67588d0b94afSMartin KaFai Lau 
6759433d49c3SDaniel Lezcano out:
6760433d49c3SDaniel Lezcano 	return ret;
6761433d49c3SDaniel Lezcano 
6762d189634eSThomas Graf out_register_late_subsys:
676316feebcfSFlorian Westphal 	rtnl_unregister_all(PF_INET6);
6764d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
6765433d49c3SDaniel Lezcano fib6_rules_init:
6766433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
6767433d49c3SDaniel Lezcano xfrm6_init:
6768433d49c3SDaniel Lezcano 	xfrm6_fini();
67692a0c451aSThomas Graf out_fib6_init:
67702a0c451aSThomas Graf 	fib6_gc_cleanup();
67718ed67789SDaniel Lezcano out_register_subsys:
67728ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
67737e52b33bSDavid S. Miller out_register_inetpeer:
67747e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
6775fc66f95cSEric Dumazet out_dst_entries:
6776fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
6777433d49c3SDaniel Lezcano out_kmem_cache:
6778f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
6779433d49c3SDaniel Lezcano 	goto out;
67801da177e4SLinus Torvalds }
67811da177e4SLinus Torvalds 
ip6_route_cleanup(void)67821da177e4SLinus Torvalds void ip6_route_cleanup(void)
67831da177e4SLinus Torvalds {
6784138d0be3SYonghong Song #if IS_BUILTIN(CONFIG_IPV6)
6785138d0be3SYonghong Song #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
6786138d0be3SYonghong Song 	bpf_iter_unregister();
6787138d0be3SYonghong Song #endif
6788138d0be3SYonghong Song #endif
67898ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
6790d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
6791101367c2SThomas Graf 	fib6_rules_cleanup();
67921da177e4SLinus Torvalds 	xfrm6_fini();
67931da177e4SLinus Torvalds 	fib6_gc_cleanup();
6794c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
67958ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
679641bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
6797f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
67981da177e4SLinus Torvalds }
6799