xref: /openbmc/linux/net/ipv6/route.c (revision 30357d7d8aaf2a980ab17c2ce054b2b87e60af88)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Linux INET6 implementation
31da177e4SLinus Torvalds  *	FIB front-end.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
91da177e4SLinus Torvalds  *      modify it under the terms of the GNU General Public License
101da177e4SLinus Torvalds  *      as published by the Free Software Foundation; either version
111da177e4SLinus Torvalds  *      2 of the License, or (at your option) any later version.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds /*	Changes:
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki @USAGI
171da177e4SLinus Torvalds  *		reworked default router selection.
181da177e4SLinus Torvalds  *		- respect outgoing interface
191da177e4SLinus Torvalds  *		- select from (probably) reachable routers (i.e.
201da177e4SLinus Torvalds  *		routers in REACHABLE, STALE, DELAY or PROBE states).
211da177e4SLinus Torvalds  *		- always select the same router if it is (probably)
221da177e4SLinus Torvalds  *		reachable.  otherwise, round-robin the list.
23c0bece9fSYOSHIFUJI Hideaki  *	Ville Nuorvala
24c0bece9fSYOSHIFUJI Hideaki  *		Fixed routing subtrees.
251da177e4SLinus Torvalds  */
261da177e4SLinus Torvalds 
27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt
28f3213831SJoe Perches 
294fc268d2SRandy Dunlap #include <linux/capability.h>
301da177e4SLinus Torvalds #include <linux/errno.h>
31bc3b2d7fSPaul Gortmaker #include <linux/export.h>
321da177e4SLinus Torvalds #include <linux/types.h>
331da177e4SLinus Torvalds #include <linux/times.h>
341da177e4SLinus Torvalds #include <linux/socket.h>
351da177e4SLinus Torvalds #include <linux/sockios.h>
361da177e4SLinus Torvalds #include <linux/net.h>
371da177e4SLinus Torvalds #include <linux/route.h>
381da177e4SLinus Torvalds #include <linux/netdevice.h>
391da177e4SLinus Torvalds #include <linux/in6.h>
407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
411da177e4SLinus Torvalds #include <linux/init.h>
421da177e4SLinus Torvalds #include <linux/if_arp.h>
431da177e4SLinus Torvalds #include <linux/proc_fs.h>
441da177e4SLinus Torvalds #include <linux/seq_file.h>
455b7c931dSDaniel Lezcano #include <linux/nsproxy.h>
465a0e3ad6STejun Heo #include <linux/slab.h>
47457c4cbcSEric W. Biederman #include <net/net_namespace.h>
481da177e4SLinus Torvalds #include <net/snmp.h>
491da177e4SLinus Torvalds #include <net/ipv6.h>
501da177e4SLinus Torvalds #include <net/ip6_fib.h>
511da177e4SLinus Torvalds #include <net/ip6_route.h>
521da177e4SLinus Torvalds #include <net/ndisc.h>
531da177e4SLinus Torvalds #include <net/addrconf.h>
541da177e4SLinus Torvalds #include <net/tcp.h>
551da177e4SLinus Torvalds #include <linux/rtnetlink.h>
561da177e4SLinus Torvalds #include <net/dst.h>
57904af04dSJiri Benc #include <net/dst_metadata.h>
581da177e4SLinus Torvalds #include <net/xfrm.h>
598d71740cSTom Tucker #include <net/netevent.h>
6021713ebcSThomas Graf #include <net/netlink.h>
6151ebd318SNicolas Dichtel #include <net/nexthop.h>
6219e42e45SRoopa Prabhu #include <net/lwtunnel.h>
63904af04dSJiri Benc #include <net/ip_tunnels.h>
64ca254490SDavid Ahern #include <net/l3mdev.h>
65b811580dSDavid Ahern #include <trace/events/fib6.h>
661da177e4SLinus Torvalds 
677c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
701da177e4SLinus Torvalds #include <linux/sysctl.h>
711da177e4SLinus Torvalds #endif
721da177e4SLinus Torvalds 
73afc154e9SHannes Frederic Sowa enum rt6_nud_state {
747e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
757e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
767e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
77afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
78afc154e9SHannes Frederic Sowa };
79afc154e9SHannes Frederic Sowa 
8083a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort);
811da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
820dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
83ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
841da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
851da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
861da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
871da177e4SLinus Torvalds 				       struct net_device *dev, int how);
88569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
91ede2059dSEric W. Biederman static int		ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
927150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
93ede2059dSEric W. Biederman static int		ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
941da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
956700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
966700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
976700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
986700c270SDavid S. Miller 					struct sk_buff *skb);
994b32b5adSMartin KaFai Lau static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
10052bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
1011da177e4SLinus Torvalds 
10270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
104b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
105830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
106830218c1SDavid Ahern 					   struct net_device *dev,
10795c96174SEric Dumazet 					   unsigned int pref);
108efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
109b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
110830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
111830218c1SDavid Ahern 					   struct net_device *dev);
11270ceb4f5SYOSHIFUJI Hideaki #endif
11370ceb4f5SYOSHIFUJI Hideaki 
1148d0b94afSMartin KaFai Lau struct uncached_list {
1158d0b94afSMartin KaFai Lau 	spinlock_t		lock;
1168d0b94afSMartin KaFai Lau 	struct list_head	head;
1178d0b94afSMartin KaFai Lau };
1188d0b94afSMartin KaFai Lau 
1198d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1208d0b94afSMartin KaFai Lau 
1218d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt)
1228d0b94afSMartin KaFai Lau {
1238d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1248d0b94afSMartin KaFai Lau 
1258d0b94afSMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
1268d0b94afSMartin KaFai Lau 	rt->rt6i_uncached_list = ul;
1278d0b94afSMartin KaFai Lau 
1288d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
1298d0b94afSMartin KaFai Lau 	list_add_tail(&rt->rt6i_uncached, &ul->head);
1308d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
1318d0b94afSMartin KaFai Lau }
1328d0b94afSMartin KaFai Lau 
1338d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt)
1348d0b94afSMartin KaFai Lau {
1358d0b94afSMartin KaFai Lau 	if (!list_empty(&rt->rt6i_uncached)) {
1368d0b94afSMartin KaFai Lau 		struct uncached_list *ul = rt->rt6i_uncached_list;
1378d0b94afSMartin KaFai Lau 
1388d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1398d0b94afSMartin KaFai Lau 		list_del(&rt->rt6i_uncached);
1408d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1418d0b94afSMartin KaFai Lau 	}
1428d0b94afSMartin KaFai Lau }
1438d0b94afSMartin KaFai Lau 
1448d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
1458d0b94afSMartin KaFai Lau {
1468d0b94afSMartin KaFai Lau 	struct net_device *loopback_dev = net->loopback_dev;
1478d0b94afSMartin KaFai Lau 	int cpu;
1488d0b94afSMartin KaFai Lau 
149e332bc67SEric W. Biederman 	if (dev == loopback_dev)
150e332bc67SEric W. Biederman 		return;
151e332bc67SEric W. Biederman 
1528d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
1538d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
1548d0b94afSMartin KaFai Lau 		struct rt6_info *rt;
1558d0b94afSMartin KaFai Lau 
1568d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1578d0b94afSMartin KaFai Lau 		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
1588d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
1598d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
1608d0b94afSMartin KaFai Lau 
161e332bc67SEric W. Biederman 			if (rt_idev->dev == dev) {
1628d0b94afSMartin KaFai Lau 				rt->rt6i_idev = in6_dev_get(loopback_dev);
1638d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
1648d0b94afSMartin KaFai Lau 			}
1658d0b94afSMartin KaFai Lau 
166e332bc67SEric W. Biederman 			if (rt_dev == dev) {
1678d0b94afSMartin KaFai Lau 				rt->dst.dev = loopback_dev;
1688d0b94afSMartin KaFai Lau 				dev_hold(rt->dst.dev);
1698d0b94afSMartin KaFai Lau 				dev_put(rt_dev);
1708d0b94afSMartin KaFai Lau 			}
1718d0b94afSMartin KaFai Lau 		}
1728d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1738d0b94afSMartin KaFai Lau 	}
1748d0b94afSMartin KaFai Lau }
1758d0b94afSMartin KaFai Lau 
176d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
177d52d3997SMartin KaFai Lau {
178d52d3997SMartin KaFai Lau 	return dst_metrics_write_ptr(rt->dst.from);
179d52d3997SMartin KaFai Lau }
180d52d3997SMartin KaFai Lau 
18106582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
18206582540SDavid S. Miller {
18306582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
18406582540SDavid S. Miller 
185d52d3997SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU)
186d52d3997SMartin KaFai Lau 		return rt6_pcpu_cow_metrics(rt);
187d52d3997SMartin KaFai Lau 	else if (rt->rt6i_flags & RTF_CACHE)
1884b32b5adSMartin KaFai Lau 		return NULL;
1894b32b5adSMartin KaFai Lau 	else
1903b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
19106582540SDavid S. Miller }
19206582540SDavid S. Miller 
193f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
194f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
195f894cbf8SDavid S. Miller 					     const void *daddr)
19639232973SDavid S. Miller {
19739232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
19839232973SDavid S. Miller 
199a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
20039232973SDavid S. Miller 		return (const void *) p;
201f894cbf8SDavid S. Miller 	else if (skb)
202f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
20339232973SDavid S. Miller 	return daddr;
20439232973SDavid S. Miller }
20539232973SDavid S. Miller 
206f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
207f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
208f894cbf8SDavid S. Miller 					  const void *daddr)
209d3aaeb38SDavid S. Miller {
21039232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
21139232973SDavid S. Miller 	struct neighbour *n;
21239232973SDavid S. Miller 
213f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
2148e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
215f83c7790SDavid S. Miller 	if (n)
216f83c7790SDavid S. Miller 		return n;
217f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
218f83c7790SDavid S. Miller }
219f83c7790SDavid S. Miller 
2209a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2211da177e4SLinus Torvalds 	.family			=	AF_INET6,
2221da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2231da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2241da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2250dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
226ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
22706582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
2281da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2291da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2301da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2311da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2321da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2336e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2349f8955ccSEric W. Biederman 	.local_out		=	__ip6_local_out,
235d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
2361da177e4SLinus Torvalds };
2371da177e4SLinus Torvalds 
238ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
239ec831ea7SRoland Dreier {
240618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
241618f9bc7SSteffen Klassert 
242618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
243ec831ea7SRoland Dreier }
244ec831ea7SRoland Dreier 
2456700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2466700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
24714e50e57SDavid S. Miller {
24814e50e57SDavid S. Miller }
24914e50e57SDavid S. Miller 
2506700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2516700c270SDavid S. Miller 				      struct sk_buff *skb)
252b587ee3bSDavid S. Miller {
253b587ee3bSDavid S. Miller }
254b587ee3bSDavid S. Miller 
25514e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
25614e50e57SDavid S. Miller 	.family			=	AF_INET6,
25714e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
25814e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
259ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
260214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
26114e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
262b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2630a1f5962SMartin KaFai Lau 	.cow_metrics		=	dst_cow_metrics_generic,
264d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
26514e50e57SDavid S. Miller };
26614e50e57SDavid S. Miller 
26762fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
26814edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
26962fa8a84SDavid S. Miller };
27062fa8a84SDavid S. Miller 
271fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2721da177e4SLinus Torvalds 	.dst = {
2731da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2741da177e4SLinus Torvalds 		.__use		= 1,
2752c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2761da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2771da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2781da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2791da177e4SLinus Torvalds 	},
2801da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2814f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2821da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2831da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2841da177e4SLinus Torvalds };
2851da177e4SLinus Torvalds 
286101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
287101367c2SThomas Graf 
288fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
289101367c2SThomas Graf 	.dst = {
290101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
291101367c2SThomas Graf 		.__use		= 1,
2922c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
293101367c2SThomas Graf 		.error		= -EACCES,
2949ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2959ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
296101367c2SThomas Graf 	},
297101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2984f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
299101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
300101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
301101367c2SThomas Graf };
302101367c2SThomas Graf 
303fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
304101367c2SThomas Graf 	.dst = {
305101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
306101367c2SThomas Graf 		.__use		= 1,
3072c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
308101367c2SThomas Graf 		.error		= -EINVAL,
309352e512cSHerbert Xu 		.input		= dst_discard,
310ede2059dSEric W. Biederman 		.output		= dst_discard_out,
311101367c2SThomas Graf 	},
312101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3134f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
314101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
315101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
316101367c2SThomas Graf };
317101367c2SThomas Graf 
318101367c2SThomas Graf #endif
319101367c2SThomas Graf 
320ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt)
321ebfa45f0SMartin KaFai Lau {
322ebfa45f0SMartin KaFai Lau 	struct dst_entry *dst = &rt->dst;
323ebfa45f0SMartin KaFai Lau 
324ebfa45f0SMartin KaFai Lau 	memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
325ebfa45f0SMartin KaFai Lau 	INIT_LIST_HEAD(&rt->rt6i_siblings);
326ebfa45f0SMartin KaFai Lau 	INIT_LIST_HEAD(&rt->rt6i_uncached);
327ebfa45f0SMartin KaFai Lau }
328ebfa45f0SMartin KaFai Lau 
3291da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
330d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net,
331957c665fSDavid S. Miller 					struct net_device *dev,
332ad706862SMartin KaFai Lau 					int flags)
3331da177e4SLinus Torvalds {
33497bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3356f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
336cf911662SDavid S. Miller 
337ebfa45f0SMartin KaFai Lau 	if (rt)
338ebfa45f0SMartin KaFai Lau 		rt6_info_init(rt);
3398104891bSSteffen Klassert 
340cf911662SDavid S. Miller 	return rt;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
3439ab179d8SDavid Ahern struct rt6_info *ip6_dst_alloc(struct net *net,
344d52d3997SMartin KaFai Lau 			       struct net_device *dev,
345ad706862SMartin KaFai Lau 			       int flags)
346d52d3997SMartin KaFai Lau {
347ad706862SMartin KaFai Lau 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
348d52d3997SMartin KaFai Lau 
349d52d3997SMartin KaFai Lau 	if (rt) {
350d52d3997SMartin KaFai Lau 		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
351d52d3997SMartin KaFai Lau 		if (rt->rt6i_pcpu) {
352d52d3997SMartin KaFai Lau 			int cpu;
353d52d3997SMartin KaFai Lau 
354d52d3997SMartin KaFai Lau 			for_each_possible_cpu(cpu) {
355d52d3997SMartin KaFai Lau 				struct rt6_info **p;
356d52d3997SMartin KaFai Lau 
357d52d3997SMartin KaFai Lau 				p = per_cpu_ptr(rt->rt6i_pcpu, cpu);
358d52d3997SMartin KaFai Lau 				/* no one shares rt */
359d52d3997SMartin KaFai Lau 				*p =  NULL;
360d52d3997SMartin KaFai Lau 			}
361d52d3997SMartin KaFai Lau 		} else {
362d52d3997SMartin KaFai Lau 			dst_destroy((struct dst_entry *)rt);
363d52d3997SMartin KaFai Lau 			return NULL;
364d52d3997SMartin KaFai Lau 		}
365d52d3997SMartin KaFai Lau 	}
366d52d3997SMartin KaFai Lau 
367d52d3997SMartin KaFai Lau 	return rt;
368d52d3997SMartin KaFai Lau }
3699ab179d8SDavid Ahern EXPORT_SYMBOL(ip6_dst_alloc);
370d52d3997SMartin KaFai Lau 
3711da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3721da177e4SLinus Torvalds {
3731da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
374ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
3758d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3761da177e4SLinus Torvalds 
3778e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
378d52d3997SMartin KaFai Lau 	free_percpu(rt->rt6i_pcpu);
3798d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
3808d0b94afSMartin KaFai Lau 
3818d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
38238308473SDavid S. Miller 	if (idev) {
3831da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3841da177e4SLinus Torvalds 		in6_dev_put(idev);
3851da177e4SLinus Torvalds 	}
3861716a961SGao feng 
387ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
388ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
389b3419363SDavid S. Miller }
390b3419363SDavid S. Miller 
3911da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3921da177e4SLinus Torvalds 			   int how)
3931da177e4SLinus Torvalds {
3941da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3951da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3965a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
397c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3981da177e4SLinus Torvalds 
39997cac082SDavid S. Miller 	if (dev != loopback_dev) {
40097cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
4015a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
4025a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
40338308473SDavid S. Miller 			if (loopback_idev) {
4041da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
4051da177e4SLinus Torvalds 				in6_dev_put(idev);
4061da177e4SLinus Torvalds 			}
4071da177e4SLinus Torvalds 		}
40897cac082SDavid S. Miller 	}
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
4115973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt)
4125973fb1eSMartin KaFai Lau {
4135973fb1eSMartin KaFai Lau 	if (rt->rt6i_flags & RTF_EXPIRES)
4145973fb1eSMartin KaFai Lau 		return time_after(jiffies, rt->dst.expires);
4155973fb1eSMartin KaFai Lau 	else
4165973fb1eSMartin KaFai Lau 		return false;
4175973fb1eSMartin KaFai Lau }
4185973fb1eSMartin KaFai Lau 
419a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4201da177e4SLinus Torvalds {
4211716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4221716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
423a50feda5SEric Dumazet 			return true;
4241716a961SGao feng 	} else if (rt->dst.from) {
4253fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
4261716a961SGao feng 	}
427a50feda5SEric Dumazet 	return false;
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
43051ebd318SNicolas Dichtel /* Multipath route selection:
43151ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
43251ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
43351ebd318SNicolas Dichtel  */
43451ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
43551ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
43651ebd318SNicolas Dichtel {
437644d0e65STom Herbert 	return get_hash_from_flowi6(fl6) % candidate_count;
43851ebd318SNicolas Dichtel }
43951ebd318SNicolas Dichtel 
44051ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
44152bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
44252bd4c0cSNicolas Dichtel 					     int strict)
44351ebd318SNicolas Dichtel {
44451ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
44551ebd318SNicolas Dichtel 	int route_choosen;
44651ebd318SNicolas Dichtel 
44751ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
44851ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
44951ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
45051ebd318SNicolas Dichtel 	 */
45151ebd318SNicolas Dichtel 	if (route_choosen)
45251ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
45351ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
45451ebd318SNicolas Dichtel 			route_choosen--;
45551ebd318SNicolas Dichtel 			if (route_choosen == 0) {
45652bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
45752bd4c0cSNicolas Dichtel 					break;
45851ebd318SNicolas Dichtel 				match = sibling;
45951ebd318SNicolas Dichtel 				break;
46051ebd318SNicolas Dichtel 			}
46151ebd318SNicolas Dichtel 		}
46251ebd318SNicolas Dichtel 	return match;
46351ebd318SNicolas Dichtel }
46451ebd318SNicolas Dichtel 
4651da177e4SLinus Torvalds /*
466c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4671da177e4SLinus Torvalds  */
4681da177e4SLinus Torvalds 
4698ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4708ed67789SDaniel Lezcano 						    struct rt6_info *rt,
471b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4721da177e4SLinus Torvalds 						    int oif,
473d420895eSYOSHIFUJI Hideaki 						    int flags)
4741da177e4SLinus Torvalds {
4751da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4761da177e4SLinus Torvalds 	struct rt6_info *sprt;
4771da177e4SLinus Torvalds 
478dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
479dd3abc4eSYOSHIFUJI Hideaki 		goto out;
480dd3abc4eSYOSHIFUJI Hideaki 
481d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
482d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
483dd3abc4eSYOSHIFUJI Hideaki 
484dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4851da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4861da177e4SLinus Torvalds 				return sprt;
4871da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
48838308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4891da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
49017fb0b2bSDavid Ahern 					if (flags & RT6_LOOKUP_F_IFACE)
4911da177e4SLinus Torvalds 						continue;
49217fb0b2bSDavid Ahern 					if (local &&
49317fb0b2bSDavid Ahern 					    local->rt6i_idev->dev->ifindex == oif)
4941da177e4SLinus Torvalds 						continue;
4951da177e4SLinus Torvalds 				}
4961da177e4SLinus Torvalds 				local = sprt;
4971da177e4SLinus Torvalds 			}
498dd3abc4eSYOSHIFUJI Hideaki 		} else {
499dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
500dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
501dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
502dd3abc4eSYOSHIFUJI Hideaki 		}
5031da177e4SLinus Torvalds 	}
5041da177e4SLinus Torvalds 
505dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
5061da177e4SLinus Torvalds 		if (local)
5071da177e4SLinus Torvalds 			return local;
5081da177e4SLinus Torvalds 
509d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
5108ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
5111da177e4SLinus Torvalds 	}
512dd3abc4eSYOSHIFUJI Hideaki out:
5131da177e4SLinus Torvalds 	return rt;
5141da177e4SLinus Torvalds }
5151da177e4SLinus Torvalds 
51627097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
517c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
518c2f17e82SHannes Frederic Sowa 	struct work_struct work;
519c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
520c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
521c2f17e82SHannes Frederic Sowa };
522c2f17e82SHannes Frederic Sowa 
523c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
524c2f17e82SHannes Frederic Sowa {
525c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
526c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
527c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
528c2f17e82SHannes Frederic Sowa 
529c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
530adc176c5SErik Nordmark 	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
531c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
532662f5533SMichael Büsch 	kfree(work);
533c2f17e82SHannes Frederic Sowa }
534c2f17e82SHannes Frederic Sowa 
53527097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
53627097255SYOSHIFUJI Hideaki {
537990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
538f2c31e32SEric Dumazet 	struct neighbour *neigh;
53927097255SYOSHIFUJI Hideaki 	/*
54027097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
54127097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
54227097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
54327097255SYOSHIFUJI Hideaki 	 *
54427097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
54527097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
54627097255SYOSHIFUJI Hideaki 	 */
5472152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
548fdd6681dSAmerigo Wang 		return;
5492152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5502152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5512152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5528d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5538d6c31bfSMartin KaFai Lau 			goto out;
5548d6c31bfSMartin KaFai Lau 
555990edb42SMartin KaFai Lau 		work = NULL;
5562152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
557990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
558990edb42SMartin KaFai Lau 		    time_after(jiffies,
559990edb42SMartin KaFai Lau 			       neigh->updated +
560990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
561c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
562990edb42SMartin KaFai Lau 			if (work)
5637e980569SJiri Benc 				__neigh_set_probe_once(neigh);
564990edb42SMartin KaFai Lau 		}
565c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
566990edb42SMartin KaFai Lau 	} else {
567990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
568990edb42SMartin KaFai Lau 	}
569c2f17e82SHannes Frederic Sowa 
570c2f17e82SHannes Frederic Sowa 	if (work) {
571c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
572c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
573c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
574c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
575c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
576c2f17e82SHannes Frederic Sowa 	}
577990edb42SMartin KaFai Lau 
5788d6c31bfSMartin KaFai Lau out:
5792152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
580f2c31e32SEric Dumazet }
58127097255SYOSHIFUJI Hideaki #else
58227097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
58327097255SYOSHIFUJI Hideaki {
58427097255SYOSHIFUJI Hideaki }
58527097255SYOSHIFUJI Hideaki #endif
58627097255SYOSHIFUJI Hideaki 
5871da177e4SLinus Torvalds /*
588554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5891da177e4SLinus Torvalds  */
590b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5911da177e4SLinus Torvalds {
592d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
593161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
594554cfb7eSYOSHIFUJI Hideaki 		return 2;
595161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
596161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
597161980f4SDavid S. Miller 		return 1;
598554cfb7eSYOSHIFUJI Hideaki 	return 0;
5991da177e4SLinus Torvalds }
6001da177e4SLinus Torvalds 
601afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
6021da177e4SLinus Torvalds {
603f2c31e32SEric Dumazet 	struct neighbour *neigh;
604afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
605f2c31e32SEric Dumazet 
6064d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
6074d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
608afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
609145a3621SYOSHIFUJI Hideaki / 吉藤英明 
610145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
611145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
612145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
613145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
614554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
615afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
616398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
617a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
618afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6197e980569SJiri Benc 		else
6207e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
621398bcbebSYOSHIFUJI Hideaki #endif
622145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
623afc154e9SHannes Frederic Sowa 	} else {
624afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6257e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
626a5a81f0bSPaul Marks 	}
627145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
628145a3621SYOSHIFUJI Hideaki / 吉藤英明 
629a5a81f0bSPaul Marks 	return ret;
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds 
632554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
633554cfb7eSYOSHIFUJI Hideaki 			   int strict)
634554cfb7eSYOSHIFUJI Hideaki {
635a5a81f0bSPaul Marks 	int m;
6364d0c5911SYOSHIFUJI Hideaki 
6374d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
63877d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
639afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
640ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
641ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
642ebacaaa0SYOSHIFUJI Hideaki #endif
643afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
644afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
645afc154e9SHannes Frederic Sowa 		if (n < 0)
646afc154e9SHannes Frederic Sowa 			return n;
647afc154e9SHannes Frederic Sowa 	}
648554cfb7eSYOSHIFUJI Hideaki 	return m;
649554cfb7eSYOSHIFUJI Hideaki }
650554cfb7eSYOSHIFUJI Hideaki 
651f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
652afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
653afc154e9SHannes Frederic Sowa 				   bool *do_rr)
654554cfb7eSYOSHIFUJI Hideaki {
655554cfb7eSYOSHIFUJI Hideaki 	int m;
656afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
65735103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
65835103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
65935103d11SAndy Gospodarek 
66035103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
661d5d32e4bSDavid Ahern 	    idev->cnf.ignore_routes_with_linkdown &&
662d5d32e4bSDavid Ahern 	    !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
66335103d11SAndy Gospodarek 		goto out;
664554cfb7eSYOSHIFUJI Hideaki 
665554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
666f11e6659SDavid S. Miller 		goto out;
667554cfb7eSYOSHIFUJI Hideaki 
668554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6697e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
670afc154e9SHannes Frederic Sowa 		match_do_rr = true;
671afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6727e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
673f11e6659SDavid S. Miller 		goto out;
6741da177e4SLinus Torvalds 	}
675f11e6659SDavid S. Miller 
676afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
677afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
678afc154e9SHannes Frederic Sowa 
6797e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
680afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
681afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
682afc154e9SHannes Frederic Sowa 		*mpri = m;
683afc154e9SHannes Frederic Sowa 		match = rt;
684afc154e9SHannes Frederic Sowa 	}
685f11e6659SDavid S. Miller out:
686f11e6659SDavid S. Miller 	return match;
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds 
689f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
690f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
691afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
692afc154e9SHannes Frederic Sowa 				     bool *do_rr)
693f11e6659SDavid S. Miller {
6949fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
695f11e6659SDavid S. Miller 	int mpri = -1;
696f11e6659SDavid S. Miller 
697f11e6659SDavid S. Miller 	match = NULL;
6989fbdcfafSSteffen Klassert 	cont = NULL;
6999fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
7009fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7019fbdcfafSSteffen Klassert 			cont = rt;
7029fbdcfafSSteffen Klassert 			break;
7039fbdcfafSSteffen Klassert 		}
7049fbdcfafSSteffen Klassert 
705afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7069fbdcfafSSteffen Klassert 	}
7079fbdcfafSSteffen Klassert 
7089fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
7099fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7109fbdcfafSSteffen Klassert 			cont = rt;
7119fbdcfafSSteffen Klassert 			break;
7129fbdcfafSSteffen Klassert 		}
7139fbdcfafSSteffen Klassert 
7149fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7159fbdcfafSSteffen Klassert 	}
7169fbdcfafSSteffen Klassert 
7179fbdcfafSSteffen Klassert 	if (match || !cont)
7189fbdcfafSSteffen Klassert 		return match;
7199fbdcfafSSteffen Klassert 
7209fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
721afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
722f11e6659SDavid S. Miller 
723f11e6659SDavid S. Miller 	return match;
724f11e6659SDavid S. Miller }
725f11e6659SDavid S. Miller 
726f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
727f11e6659SDavid S. Miller {
728f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7298ed67789SDaniel Lezcano 	struct net *net;
730afc154e9SHannes Frederic Sowa 	bool do_rr = false;
731f11e6659SDavid S. Miller 
732f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
733f11e6659SDavid S. Miller 	if (!rt0)
734f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
735f11e6659SDavid S. Miller 
736afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
737afc154e9SHannes Frederic Sowa 			     &do_rr);
738f11e6659SDavid S. Miller 
739afc154e9SHannes Frederic Sowa 	if (do_rr) {
740d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
741f11e6659SDavid S. Miller 
742554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
743f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
744f11e6659SDavid S. Miller 			next = fn->leaf;
745f11e6659SDavid S. Miller 
746f11e6659SDavid S. Miller 		if (next != rt0)
747f11e6659SDavid S. Miller 			fn->rr_ptr = next;
748554cfb7eSYOSHIFUJI Hideaki 	}
749554cfb7eSYOSHIFUJI Hideaki 
750d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
751a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7521da177e4SLinus Torvalds }
7531da177e4SLinus Torvalds 
7548b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7558b9df265SMartin KaFai Lau {
7568b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7578b9df265SMartin KaFai Lau }
7588b9df265SMartin KaFai Lau 
75970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
76070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
761b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
76270ceb4f5SYOSHIFUJI Hideaki {
763c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
76470ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
76570ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
76670ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7674bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
76870ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
76970ceb4f5SYOSHIFUJI Hideaki 
77070ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
77170ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
77270ceb4f5SYOSHIFUJI Hideaki 	}
77370ceb4f5SYOSHIFUJI Hideaki 
77470ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
77570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
77670ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
77770ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
77870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
77970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
78070ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
78170ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
78270ceb4f5SYOSHIFUJI Hideaki 		}
78370ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
78470ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
78570ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
78670ceb4f5SYOSHIFUJI Hideaki 		}
78770ceb4f5SYOSHIFUJI Hideaki 	}
78870ceb4f5SYOSHIFUJI Hideaki 
78970ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
79070ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
7913933fc95SJens Rosenboom 		return -EINVAL;
79270ceb4f5SYOSHIFUJI Hideaki 
7934bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
79470ceb4f5SYOSHIFUJI Hideaki 
79570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
79670ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
79770ceb4f5SYOSHIFUJI Hideaki 	else {
79870ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
79970ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
80070ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
80170ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
80270ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
80370ceb4f5SYOSHIFUJI Hideaki 	}
80470ceb4f5SYOSHIFUJI Hideaki 
805f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
806f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
807f104a567SDuan Jiong 	else
808f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
809830218c1SDavid Ahern 					gwaddr, dev);
81070ceb4f5SYOSHIFUJI Hideaki 
81170ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
812e0a1ad73SThomas Graf 		ip6_del_rt(rt);
81370ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
81470ceb4f5SYOSHIFUJI Hideaki 	}
81570ceb4f5SYOSHIFUJI Hideaki 
81670ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
817830218c1SDavid Ahern 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
818830218c1SDavid Ahern 					dev, pref);
81970ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
82070ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
82170ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
82270ceb4f5SYOSHIFUJI Hideaki 
82370ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8241716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8251716a961SGao feng 			rt6_clean_expires(rt);
8261716a961SGao feng 		else
8271716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8281716a961SGao feng 
82994e187c0SAmerigo Wang 		ip6_rt_put(rt);
83070ceb4f5SYOSHIFUJI Hideaki 	}
83170ceb4f5SYOSHIFUJI Hideaki 	return 0;
83270ceb4f5SYOSHIFUJI Hideaki }
83370ceb4f5SYOSHIFUJI Hideaki #endif
83470ceb4f5SYOSHIFUJI Hideaki 
835a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
836a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
837a3c00e46SMartin KaFai Lau {
838a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
839a3c00e46SMartin KaFai Lau 	while (1) {
840a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
841a3c00e46SMartin KaFai Lau 			return NULL;
842a3c00e46SMartin KaFai Lau 		pn = fn->parent;
843a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
844a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
845a3c00e46SMartin KaFai Lau 		else
846a3c00e46SMartin KaFai Lau 			fn = pn;
847a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
848a3c00e46SMartin KaFai Lau 			return fn;
849a3c00e46SMartin KaFai Lau 	}
850a3c00e46SMartin KaFai Lau }
851c71099acSThomas Graf 
8528ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8538ed67789SDaniel Lezcano 					     struct fib6_table *table,
8544c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8551da177e4SLinus Torvalds {
8561da177e4SLinus Torvalds 	struct fib6_node *fn;
8571da177e4SLinus Torvalds 	struct rt6_info *rt;
8581da177e4SLinus Torvalds 
859c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8604c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
861c71099acSThomas Graf restart:
862c71099acSThomas Graf 	rt = fn->leaf;
8634c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
86451ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
86552bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
866a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
867a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
868a3c00e46SMartin KaFai Lau 		if (fn)
869a3c00e46SMartin KaFai Lau 			goto restart;
870a3c00e46SMartin KaFai Lau 	}
871d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
872c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
873b811580dSDavid Ahern 
874b811580dSDavid Ahern 	trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
875b811580dSDavid Ahern 
8761da177e4SLinus Torvalds 	return rt;
877c71099acSThomas Graf 
878c71099acSThomas Graf }
879c71099acSThomas Graf 
880ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
881ea6e574eSFlorian Westphal 				    int flags)
882ea6e574eSFlorian Westphal {
883ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
884ea6e574eSFlorian Westphal }
885ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
886ea6e574eSFlorian Westphal 
8879acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8889acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
889c71099acSThomas Graf {
8904c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8914c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8924c9483b2SDavid S. Miller 		.daddr = *daddr,
893c71099acSThomas Graf 	};
894c71099acSThomas Graf 	struct dst_entry *dst;
89577d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
896c71099acSThomas Graf 
897adaa70bbSThomas Graf 	if (saddr) {
8984c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
899adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
900adaa70bbSThomas Graf 	}
901adaa70bbSThomas Graf 
9024c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
903c71099acSThomas Graf 	if (dst->error == 0)
904c71099acSThomas Graf 		return (struct rt6_info *) dst;
905c71099acSThomas Graf 
906c71099acSThomas Graf 	dst_release(dst);
907c71099acSThomas Graf 
9081da177e4SLinus Torvalds 	return NULL;
9091da177e4SLinus Torvalds }
9107159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
9117159039aSYOSHIFUJI Hideaki 
912c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
9131da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
9141da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
9151da177e4SLinus Torvalds    be destroyed.
9161da177e4SLinus Torvalds  */
9171da177e4SLinus Torvalds 
918e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
919e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9201da177e4SLinus Torvalds {
9211da177e4SLinus Torvalds 	int err;
922c71099acSThomas Graf 	struct fib6_table *table;
9231da177e4SLinus Torvalds 
924c71099acSThomas Graf 	table = rt->rt6i_table;
925c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
926e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
927c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 	return err;
9301da177e4SLinus Torvalds }
9311da177e4SLinus Torvalds 
93240e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
93340e22e8fSThomas Graf {
934e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
935e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
936e715b6d3SFlorian Westphal 
937e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
93840e22e8fSThomas Graf }
93940e22e8fSThomas Graf 
9408b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
94121efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
942b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9431da177e4SLinus Torvalds {
9441da177e4SLinus Torvalds 	struct rt6_info *rt;
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 	/*
9471da177e4SLinus Torvalds 	 *	Clone the route.
9481da177e4SLinus Torvalds 	 */
9491da177e4SLinus Torvalds 
950d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
95183a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9521da177e4SLinus Torvalds 
953ad706862SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0);
95483a09abdSMartin KaFai Lau 
95583a09abdSMartin KaFai Lau 	if (!rt)
95683a09abdSMartin KaFai Lau 		return NULL;
95783a09abdSMartin KaFai Lau 
95883a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9598b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
96083a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
96183a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
96283a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
96383a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9648b9df265SMartin KaFai Lau 
9658b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
966bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
96721efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
96858c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9691da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9701da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9714e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9721da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9731da177e4SLinus Torvalds 		}
9741da177e4SLinus Torvalds #endif
97595a9a5baSYOSHIFUJI Hideaki 	}
97695a9a5baSYOSHIFUJI Hideaki 
977299d9939SYOSHIFUJI Hideaki 	return rt;
978299d9939SYOSHIFUJI Hideaki }
979299d9939SYOSHIFUJI Hideaki 
980d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
981d52d3997SMartin KaFai Lau {
982d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
983d52d3997SMartin KaFai Lau 
984d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
985ad706862SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags);
986d52d3997SMartin KaFai Lau 
987d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
988d52d3997SMartin KaFai Lau 		return NULL;
989d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
990d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
991d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
992d52d3997SMartin KaFai Lau 	return pcpu_rt;
993d52d3997SMartin KaFai Lau }
994d52d3997SMartin KaFai Lau 
995d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
996d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
997d52d3997SMartin KaFai Lau {
998a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, **p;
999d52d3997SMartin KaFai Lau 
1000d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
1001d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
1002d52d3997SMartin KaFai Lau 
1003a73e4195SMartin KaFai Lau 	if (pcpu_rt) {
1004a73e4195SMartin KaFai Lau 		dst_hold(&pcpu_rt->dst);
1005a73e4195SMartin KaFai Lau 		rt6_dst_from_metrics_check(pcpu_rt);
1006a73e4195SMartin KaFai Lau 	}
1007a73e4195SMartin KaFai Lau 	return pcpu_rt;
1008a73e4195SMartin KaFai Lau }
1009a73e4195SMartin KaFai Lau 
1010a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
1011a73e4195SMartin KaFai Lau {
10129c7370a1SMartin KaFai Lau 	struct fib6_table *table = rt->rt6i_table;
1013a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
1014d52d3997SMartin KaFai Lau 
1015d52d3997SMartin KaFai Lau 	pcpu_rt = ip6_rt_pcpu_alloc(rt);
1016d52d3997SMartin KaFai Lau 	if (!pcpu_rt) {
1017d52d3997SMartin KaFai Lau 		struct net *net = dev_net(rt->dst.dev);
1018d52d3997SMartin KaFai Lau 
10199c7370a1SMartin KaFai Lau 		dst_hold(&net->ipv6.ip6_null_entry->dst);
10209c7370a1SMartin KaFai Lau 		return net->ipv6.ip6_null_entry;
1021d52d3997SMartin KaFai Lau 	}
1022d52d3997SMartin KaFai Lau 
10239c7370a1SMartin KaFai Lau 	read_lock_bh(&table->tb6_lock);
10249c7370a1SMartin KaFai Lau 	if (rt->rt6i_pcpu) {
1025a73e4195SMartin KaFai Lau 		p = this_cpu_ptr(rt->rt6i_pcpu);
1026d52d3997SMartin KaFai Lau 		prev = cmpxchg(p, NULL, pcpu_rt);
1027d52d3997SMartin KaFai Lau 		if (prev) {
1028d52d3997SMartin KaFai Lau 			/* If someone did it before us, return prev instead */
1029d52d3997SMartin KaFai Lau 			dst_destroy(&pcpu_rt->dst);
1030d52d3997SMartin KaFai Lau 			pcpu_rt = prev;
1031d52d3997SMartin KaFai Lau 		}
10329c7370a1SMartin KaFai Lau 	} else {
10339c7370a1SMartin KaFai Lau 		/* rt has been removed from the fib6 tree
10349c7370a1SMartin KaFai Lau 		 * before we have a chance to acquire the read_lock.
10359c7370a1SMartin KaFai Lau 		 * In this case, don't brother to create a pcpu rt
10369c7370a1SMartin KaFai Lau 		 * since rt is going away anyway.  The next
10379c7370a1SMartin KaFai Lau 		 * dst_check() will trigger a re-lookup.
10389c7370a1SMartin KaFai Lau 		 */
10399c7370a1SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
10409c7370a1SMartin KaFai Lau 		pcpu_rt = rt;
10419c7370a1SMartin KaFai Lau 	}
1042d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1043d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
10449c7370a1SMartin KaFai Lau 	read_unlock_bh(&table->tb6_lock);
1045d52d3997SMartin KaFai Lau 	return pcpu_rt;
1046d52d3997SMartin KaFai Lau }
1047d52d3997SMartin KaFai Lau 
10489ff74384SDavid Ahern struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
10499ff74384SDavid Ahern 			       int oif, struct flowi6 *fl6, int flags)
10501da177e4SLinus Torvalds {
1051367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
105245e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1053c71099acSThomas Graf 	int strict = 0;
10541da177e4SLinus Torvalds 
105577d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1056d5d32e4bSDavid Ahern 	strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1057367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1058367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10591da177e4SLinus Torvalds 
1060c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10611da177e4SLinus Torvalds 
10624c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1063367efcb9SMartin KaFai Lau 	saved_fn = fn;
10641da177e4SLinus Torvalds 
1065ca254490SDavid Ahern 	if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1066ca254490SDavid Ahern 		oif = 0;
1067ca254490SDavid Ahern 
1068a3c00e46SMartin KaFai Lau redo_rt6_select:
1069367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
107052bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1071367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1072a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1073a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1074a3c00e46SMartin KaFai Lau 		if (fn)
1075a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1076367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1077367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1078367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1079367efcb9SMartin KaFai Lau 			fn = saved_fn;
1080367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1081367efcb9SMartin KaFai Lau 		}
1082a3c00e46SMartin KaFai Lau 	}
1083a3c00e46SMartin KaFai Lau 
1084d52d3997SMartin KaFai Lau 
1085d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10863da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1087c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10881da177e4SLinus Torvalds 
1089d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1090b811580dSDavid Ahern 
1091b811580dSDavid Ahern 		trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
1092d52d3997SMartin KaFai Lau 		return rt;
10933da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10943da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10953da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10963da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10973da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10983da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
10993da59bd9SMartin KaFai Lau 		 */
1100c71099acSThomas Graf 
11013da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
11023da59bd9SMartin KaFai Lau 
1103d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1104d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1105d52d3997SMartin KaFai Lau 
11063da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
11073da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
11083da59bd9SMartin KaFai Lau 
11093da59bd9SMartin KaFai Lau 		if (uncached_rt)
11108d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
11113da59bd9SMartin KaFai Lau 		else
11123da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1113d52d3997SMartin KaFai Lau 
11143da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
1115b811580dSDavid Ahern 
1116b811580dSDavid Ahern 		trace_fib6_table_lookup(net, uncached_rt, table->tb6_id, fl6);
11173da59bd9SMartin KaFai Lau 		return uncached_rt;
11183da59bd9SMartin KaFai Lau 
1119d52d3997SMartin KaFai Lau 	} else {
1120d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1121d52d3997SMartin KaFai Lau 
1122d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1123d52d3997SMartin KaFai Lau 
1124d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1125d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1126d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1127d52d3997SMartin KaFai Lau 
11289c7370a1SMartin KaFai Lau 		if (pcpu_rt) {
1129d52d3997SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11309c7370a1SMartin KaFai Lau 		} else {
11319c7370a1SMartin KaFai Lau 			/* We have to do the read_unlock first
11329c7370a1SMartin KaFai Lau 			 * because rt6_make_pcpu_route() may trigger
11339c7370a1SMartin KaFai Lau 			 * ip6_dst_gc() which will take the write_lock.
11349c7370a1SMartin KaFai Lau 			 */
11359c7370a1SMartin KaFai Lau 			dst_hold(&rt->dst);
11369c7370a1SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11379c7370a1SMartin KaFai Lau 			pcpu_rt = rt6_make_pcpu_route(rt);
11389c7370a1SMartin KaFai Lau 			dst_release(&rt->dst);
11399c7370a1SMartin KaFai Lau 		}
1140d52d3997SMartin KaFai Lau 
1141b811580dSDavid Ahern 		trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6);
1142d52d3997SMartin KaFai Lau 		return pcpu_rt;
11439c7370a1SMartin KaFai Lau 
1144d52d3997SMartin KaFai Lau 	}
1145c71099acSThomas Graf }
11469ff74384SDavid Ahern EXPORT_SYMBOL_GPL(ip6_pol_route);
1147c71099acSThomas Graf 
11488ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11494c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11504acad72dSPavel Emelyanov {
11514c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11524acad72dSPavel Emelyanov }
11534acad72dSPavel Emelyanov 
1154d409b847SMahesh Bandewar struct dst_entry *ip6_route_input_lookup(struct net *net,
115572331bc0SShmulik Ladkani 					 struct net_device *dev,
115672331bc0SShmulik Ladkani 					 struct flowi6 *fl6, int flags)
115772331bc0SShmulik Ladkani {
115872331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
115972331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
116072331bc0SShmulik Ladkani 
116172331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
116272331bc0SShmulik Ladkani }
1163d409b847SMahesh Bandewar EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
116472331bc0SShmulik Ladkani 
1165c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1166c71099acSThomas Graf {
1167b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1168c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1169adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1170904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
11714c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
1172e0d56fddSDavid Ahern 		.flowi6_iif = skb->dev->ifindex,
11734c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11744c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11756502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11764c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11774c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1178c71099acSThomas Graf 	};
1179adaa70bbSThomas Graf 
1180904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
118146fa062aSJiri Benc 	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
1182904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
118306e9d040SJiri Benc 	skb_dst_drop(skb);
118472331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1185c71099acSThomas Graf }
1186c71099acSThomas Graf 
11878ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11884c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1189c71099acSThomas Graf {
11904c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1191c71099acSThomas Graf }
1192c71099acSThomas Graf 
11936f21c96aSPaolo Abeni struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
11946f21c96aSPaolo Abeni 					 struct flowi6 *fl6, int flags)
1195c71099acSThomas Graf {
1196d46a9d67SDavid Ahern 	bool any_src;
1197c71099acSThomas Graf 
11984c1feac5SDavid Ahern 	if (rt6_need_strict(&fl6->daddr)) {
11994c1feac5SDavid Ahern 		struct dst_entry *dst;
12004c1feac5SDavid Ahern 
12014c1feac5SDavid Ahern 		dst = l3mdev_link_scope_lookup(net, fl6);
1202ca254490SDavid Ahern 		if (dst)
1203ca254490SDavid Ahern 			return dst;
12044c1feac5SDavid Ahern 	}
1205ca254490SDavid Ahern 
12061fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
12074dc27d1cSDavid McCullough 
1208d46a9d67SDavid Ahern 	any_src = ipv6_addr_any(&fl6->saddr);
1209741a11d9SDavid Ahern 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
1210d46a9d67SDavid Ahern 	    (fl6->flowi6_oif && any_src))
121177d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1212c71099acSThomas Graf 
1213d46a9d67SDavid Ahern 	if (!any_src)
1214adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
12150c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
12160c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1217adaa70bbSThomas Graf 
12184c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
12191da177e4SLinus Torvalds }
12206f21c96aSPaolo Abeni EXPORT_SYMBOL_GPL(ip6_route_output_flags);
12211da177e4SLinus Torvalds 
12222774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
122314e50e57SDavid S. Miller {
12245c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
122514e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
122614e50e57SDavid S. Miller 
1227f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
122814e50e57SDavid S. Miller 	if (rt) {
12290a1f5962SMartin KaFai Lau 		rt6_info_init(rt);
12300a1f5962SMartin KaFai Lau 
1231d8d1f30bSChangli Gao 		new = &rt->dst;
123214e50e57SDavid S. Miller 		new->__use = 1;
1233352e512cSHerbert Xu 		new->input = dst_discard;
1234ede2059dSEric W. Biederman 		new->output = dst_discard_out;
123514e50e57SDavid S. Miller 
1236defb3519SDavid S. Miller 		dst_copy_metrics(new, &ort->dst);
123714e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
123814e50e57SDavid S. Miller 		if (rt->rt6i_idev)
123914e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
124014e50e57SDavid S. Miller 
12414e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
12420a1f5962SMartin KaFai Lau 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
124314e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
124414e50e57SDavid S. Miller 
124514e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
124614e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
124714e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
124814e50e57SDavid S. Miller #endif
124914e50e57SDavid S. Miller 
125014e50e57SDavid S. Miller 		dst_free(new);
125114e50e57SDavid S. Miller 	}
125214e50e57SDavid S. Miller 
125369ead7afSDavid S. Miller 	dst_release(dst_orig);
125469ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
125514e50e57SDavid S. Miller }
125614e50e57SDavid S. Miller 
12571da177e4SLinus Torvalds /*
12581da177e4SLinus Torvalds  *	Destination cache support functions
12591da177e4SLinus Torvalds  */
12601da177e4SLinus Torvalds 
12614b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12624b32b5adSMartin KaFai Lau {
12634b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12644b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12654b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12664b32b5adSMartin KaFai Lau }
12674b32b5adSMartin KaFai Lau 
12683da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12693da59bd9SMartin KaFai Lau {
12703da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12713da59bd9SMartin KaFai Lau 		return NULL;
12723da59bd9SMartin KaFai Lau 
12733da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12743da59bd9SMartin KaFai Lau 		return NULL;
12753da59bd9SMartin KaFai Lau 
12763da59bd9SMartin KaFai Lau 	return &rt->dst;
12773da59bd9SMartin KaFai Lau }
12783da59bd9SMartin KaFai Lau 
12793da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12803da59bd9SMartin KaFai Lau {
12815973fb1eSMartin KaFai Lau 	if (!__rt6_check_expired(rt) &&
12825973fb1eSMartin KaFai Lau 	    rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12833da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12843da59bd9SMartin KaFai Lau 		return &rt->dst;
12853da59bd9SMartin KaFai Lau 	else
12863da59bd9SMartin KaFai Lau 		return NULL;
12873da59bd9SMartin KaFai Lau }
12883da59bd9SMartin KaFai Lau 
12891da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12901da177e4SLinus Torvalds {
12911da177e4SLinus Torvalds 	struct rt6_info *rt;
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12941da177e4SLinus Torvalds 
12956f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12966f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12976f3118b5SNicolas Dichtel 	 * into this function always.
12986f3118b5SNicolas Dichtel 	 */
1299e3bc10bdSHannes Frederic Sowa 
13004b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
13014b32b5adSMartin KaFai Lau 
130202bcf4e0SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU ||
130302bcf4e0SMartin KaFai Lau 	    (unlikely(dst->flags & DST_NOCACHE) && rt->dst.from))
13043da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
13053da59bd9SMartin KaFai Lau 	else
13063da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
13071da177e4SLinus Torvalds }
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
13101da177e4SLinus Torvalds {
13111da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
13121da177e4SLinus Torvalds 
13131da177e4SLinus Torvalds 	if (rt) {
131454c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
131554c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1316e0a1ad73SThomas Graf 				ip6_del_rt(rt);
131754c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
13181da177e4SLinus Torvalds 			}
131954c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
132054c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
132154c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
132254c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
132354c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
132454c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
13251da177e4SLinus Torvalds }
13261da177e4SLinus Torvalds 
13271da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
13281da177e4SLinus Torvalds {
13291da177e4SLinus Torvalds 	struct rt6_info *rt;
13301da177e4SLinus Torvalds 
13313ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
13321da177e4SLinus Torvalds 
1333adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
13341da177e4SLinus Torvalds 	if (rt) {
13351eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
13361eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
13378e3d5be7SMartin KaFai Lau 			ip6_del_rt(rt);
13381eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
13391da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
13401da177e4SLinus Torvalds 		}
13411da177e4SLinus Torvalds 	}
13421eb4f758SHannes Frederic Sowa }
13431da177e4SLinus Torvalds 
134445e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
134545e4fd26SMartin KaFai Lau {
134645e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
134745e4fd26SMartin KaFai Lau 
134845e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
134945e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
135045e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
135145e4fd26SMartin KaFai Lau }
135245e4fd26SMartin KaFai Lau 
13530d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
13540d3f6d29SMartin KaFai Lau {
13550d3f6d29SMartin KaFai Lau 	return !(rt->rt6i_flags & RTF_CACHE) &&
13560d3f6d29SMartin KaFai Lau 		(rt->rt6i_flags & RTF_PCPU || rt->rt6i_node);
13570d3f6d29SMartin KaFai Lau }
13580d3f6d29SMartin KaFai Lau 
135945e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
136045e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13611da177e4SLinus Torvalds {
13621da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13631da177e4SLinus Torvalds 
136445e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
136545e4fd26SMartin KaFai Lau 		return;
136645e4fd26SMartin KaFai Lau 
136719bda36cSXin Long 	if (dst_metric_locked(dst, RTAX_MTU))
136819bda36cSXin Long 		return;
136919bda36cSXin Long 
137081aded24SDavid S. Miller 	dst_confirm(dst);
137145e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
137245e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
137345e4fd26SMartin KaFai Lau 		return;
137481aded24SDavid S. Miller 
13750d3f6d29SMartin KaFai Lau 	if (!rt6_cache_allowed_for_pmtu(rt6)) {
137645e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
137745e4fd26SMartin KaFai Lau 	} else {
137845e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
137945e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13809d289715SHagen Paul Pfeifer 
138145e4fd26SMartin KaFai Lau 		if (iph) {
138245e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
138345e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
138445e4fd26SMartin KaFai Lau 		} else if (sk) {
138545e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
138645e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
138745e4fd26SMartin KaFai Lau 		} else {
138845e4fd26SMartin KaFai Lau 			return;
13891da177e4SLinus Torvalds 		}
139045e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
139145e4fd26SMartin KaFai Lau 		if (nrt6) {
139245e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
139345e4fd26SMartin KaFai Lau 
139445e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
139545e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
139645e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
139745e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
139845e4fd26SMartin KaFai Lau 			 */
139945e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
140045e4fd26SMartin KaFai Lau 		}
140145e4fd26SMartin KaFai Lau 	}
140245e4fd26SMartin KaFai Lau }
140345e4fd26SMartin KaFai Lau 
140445e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
140545e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
140645e4fd26SMartin KaFai Lau {
140745e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
14081da177e4SLinus Torvalds }
14091da177e4SLinus Torvalds 
141042ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1411e2d118a1SLorenzo Colitti 		     int oif, u32 mark, kuid_t uid)
141281aded24SDavid S. Miller {
141381aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
141481aded24SDavid S. Miller 	struct dst_entry *dst;
141581aded24SDavid S. Miller 	struct flowi6 fl6;
141681aded24SDavid S. Miller 
141781aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
141881aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
14191b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
142081aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
142181aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
14226502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
1423e2d118a1SLorenzo Colitti 	fl6.flowi6_uid = uid;
142481aded24SDavid S. Miller 
142581aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
142681aded24SDavid S. Miller 	if (!dst->error)
142745e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
142881aded24SDavid S. Miller 	dst_release(dst);
142981aded24SDavid S. Miller }
143081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
143181aded24SDavid S. Miller 
143281aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
143381aded24SDavid S. Miller {
143433c162a9SMartin KaFai Lau 	struct dst_entry *dst;
143533c162a9SMartin KaFai Lau 
143681aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
1437e2d118a1SLorenzo Colitti 			sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid);
143833c162a9SMartin KaFai Lau 
143933c162a9SMartin KaFai Lau 	dst = __sk_dst_get(sk);
144033c162a9SMartin KaFai Lau 	if (!dst || !dst->obsolete ||
144133c162a9SMartin KaFai Lau 	    dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
144233c162a9SMartin KaFai Lau 		return;
144333c162a9SMartin KaFai Lau 
144433c162a9SMartin KaFai Lau 	bh_lock_sock(sk);
144533c162a9SMartin KaFai Lau 	if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
144633c162a9SMartin KaFai Lau 		ip6_datagram_dst_update(sk, false);
144733c162a9SMartin KaFai Lau 	bh_unlock_sock(sk);
144881aded24SDavid S. Miller }
144981aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
145081aded24SDavid S. Miller 
1451b55b76b2SDuan Jiong /* Handle redirects */
1452b55b76b2SDuan Jiong struct ip6rd_flowi {
1453b55b76b2SDuan Jiong 	struct flowi6 fl6;
1454b55b76b2SDuan Jiong 	struct in6_addr gateway;
1455b55b76b2SDuan Jiong };
1456b55b76b2SDuan Jiong 
1457b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1458b55b76b2SDuan Jiong 					     struct fib6_table *table,
1459b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1460b55b76b2SDuan Jiong 					     int flags)
1461b55b76b2SDuan Jiong {
1462b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1463b55b76b2SDuan Jiong 	struct rt6_info *rt;
1464b55b76b2SDuan Jiong 	struct fib6_node *fn;
1465b55b76b2SDuan Jiong 
1466b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
146767c408cfSAlexander Alemayhu 	 * check if the redirect has come from appropriate router.
1468b55b76b2SDuan Jiong 	 *
1469b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1470b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1471b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1472b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1473b55b76b2SDuan Jiong 	 * routes.
1474b55b76b2SDuan Jiong 	 */
1475b55b76b2SDuan Jiong 
1476b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1477b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1478b55b76b2SDuan Jiong restart:
1479b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1480b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1481b55b76b2SDuan Jiong 			continue;
1482b55b76b2SDuan Jiong 		if (rt->dst.error)
1483b55b76b2SDuan Jiong 			break;
1484b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1485b55b76b2SDuan Jiong 			continue;
1486b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1487b55b76b2SDuan Jiong 			continue;
1488b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1489b55b76b2SDuan Jiong 			continue;
1490b55b76b2SDuan Jiong 		break;
1491b55b76b2SDuan Jiong 	}
1492b55b76b2SDuan Jiong 
1493b55b76b2SDuan Jiong 	if (!rt)
1494b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1495b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1496b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1497b0a1ba59SMartin KaFai Lau 		goto out;
1498b0a1ba59SMartin KaFai Lau 	}
1499b0a1ba59SMartin KaFai Lau 
1500b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1501a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1502a3c00e46SMartin KaFai Lau 		if (fn)
1503a3c00e46SMartin KaFai Lau 			goto restart;
1504b55b76b2SDuan Jiong 	}
1505a3c00e46SMartin KaFai Lau 
1506b0a1ba59SMartin KaFai Lau out:
1507b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1508b55b76b2SDuan Jiong 
1509b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1510b55b76b2SDuan Jiong 
1511b811580dSDavid Ahern 	trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
1512b55b76b2SDuan Jiong 	return rt;
1513b55b76b2SDuan Jiong };
1514b55b76b2SDuan Jiong 
1515b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1516b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1517b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1518b55b76b2SDuan Jiong {
1519b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1520b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1521b55b76b2SDuan Jiong 
1522b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1523b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1524b55b76b2SDuan Jiong 
1525b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1526b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1527b55b76b2SDuan Jiong }
1528b55b76b2SDuan Jiong 
1529e2d118a1SLorenzo Colitti void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
1530e2d118a1SLorenzo Colitti 		  kuid_t uid)
15313a5ad2eeSDavid S. Miller {
15323a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
15333a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
15343a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
15353a5ad2eeSDavid S. Miller 
15363a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1537e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
15383a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
15393a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
15403a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
15413a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
15426502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
1543e2d118a1SLorenzo Colitti 	fl6.flowi6_uid = uid;
15443a5ad2eeSDavid S. Miller 
1545b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
15466700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
15473a5ad2eeSDavid S. Miller 	dst_release(dst);
15483a5ad2eeSDavid S. Miller }
15493a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
15503a5ad2eeSDavid S. Miller 
1551c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1552c92a59ecSDuan Jiong 			    u32 mark)
1553c92a59ecSDuan Jiong {
1554c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1555c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1556c92a59ecSDuan Jiong 	struct dst_entry *dst;
1557c92a59ecSDuan Jiong 	struct flowi6 fl6;
1558c92a59ecSDuan Jiong 
1559c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1560e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1561c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1562c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1563c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1564c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1565e2d118a1SLorenzo Colitti 	fl6.flowi6_uid = sock_net_uid(net, NULL);
1566c92a59ecSDuan Jiong 
1567b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1568c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1569c92a59ecSDuan Jiong 	dst_release(dst);
1570c92a59ecSDuan Jiong }
1571c92a59ecSDuan Jiong 
15723a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
15733a5ad2eeSDavid S. Miller {
1574e2d118a1SLorenzo Colitti 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
1575e2d118a1SLorenzo Colitti 		     sk->sk_uid);
15763a5ad2eeSDavid S. Miller }
15773a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15783a5ad2eeSDavid S. Miller 
15790dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15801da177e4SLinus Torvalds {
15810dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15820dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15830dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15840dbaee3bSDavid S. Miller 
15851da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15861da177e4SLinus Torvalds 
15875578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15885578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15891da177e4SLinus Torvalds 
15901da177e4SLinus Torvalds 	/*
15911da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15921da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15931da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15941da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15951da177e4SLinus Torvalds 	 */
15961da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15971da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15981da177e4SLinus Torvalds 	return mtu;
15991da177e4SLinus Torvalds }
16001da177e4SLinus Torvalds 
1601ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1602d33e4553SDavid S. Miller {
16034b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
16044b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1605d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1606618f9bc7SSteffen Klassert 
1607618f9bc7SSteffen Klassert 	if (mtu)
160830f78d8eSEric Dumazet 		goto out;
1609618f9bc7SSteffen Klassert 
16104b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
16114b32b5adSMartin KaFai Lau 	if (mtu)
16124b32b5adSMartin KaFai Lau 		goto out;
16134b32b5adSMartin KaFai Lau 
1614618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1615d33e4553SDavid S. Miller 
1616d33e4553SDavid S. Miller 	rcu_read_lock();
1617d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1618d33e4553SDavid S. Miller 	if (idev)
1619d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1620d33e4553SDavid S. Miller 	rcu_read_unlock();
1621d33e4553SDavid S. Miller 
162230f78d8eSEric Dumazet out:
162314972cbdSRoopa Prabhu 	mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
162414972cbdSRoopa Prabhu 
162514972cbdSRoopa Prabhu 	return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
1626d33e4553SDavid S. Miller }
1627d33e4553SDavid S. Miller 
16283b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
16293b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
16305d0bbeebSThomas Graf 
16313b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
163287a11578SDavid S. Miller 				  struct flowi6 *fl6)
16331da177e4SLinus Torvalds {
163487a11578SDavid S. Miller 	struct dst_entry *dst;
16351da177e4SLinus Torvalds 	struct rt6_info *rt;
16361da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1637c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
16381da177e4SLinus Torvalds 
163938308473SDavid S. Miller 	if (unlikely(!idev))
1640122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
16411da177e4SLinus Torvalds 
1642ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, dev, 0);
164338308473SDavid S. Miller 	if (unlikely(!rt)) {
16441da177e4SLinus Torvalds 		in6_dev_put(idev);
164587a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
16461da177e4SLinus Torvalds 		goto out;
16471da177e4SLinus Torvalds 	}
16481da177e4SLinus Torvalds 
16498e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
16508e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1651d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1652550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
165387a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
16548e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
16558e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
165614edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
16571da177e4SLinus Torvalds 
16583b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1659d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1660d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
16613b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16621da177e4SLinus Torvalds 
16635578689aSDaniel Lezcano 	fib6_force_start_gc(net);
16641da177e4SLinus Torvalds 
166587a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
166687a11578SDavid S. Miller 
16671da177e4SLinus Torvalds out:
166887a11578SDavid S. Miller 	return dst;
16691da177e4SLinus Torvalds }
16701da177e4SLinus Torvalds 
16713d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
16721da177e4SLinus Torvalds {
1673e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
16743d0f24a7SStephen Hemminger 	int more = 0;
16751da177e4SLinus Torvalds 
16763b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16773b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16785d0bbeebSThomas Graf 
16791da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16801da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16811da177e4SLinus Torvalds 			*pprev = dst->next;
16821da177e4SLinus Torvalds 			dst_free(dst);
16831da177e4SLinus Torvalds 		} else {
16841da177e4SLinus Torvalds 			pprev = &dst->next;
16853d0f24a7SStephen Hemminger 			++more;
16861da177e4SLinus Torvalds 		}
16871da177e4SLinus Torvalds 	}
16881da177e4SLinus Torvalds 
16893b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16905d0bbeebSThomas Graf 
16913d0f24a7SStephen Hemminger 	return more;
16921da177e4SLinus Torvalds }
16931da177e4SLinus Torvalds 
16941e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16951e493d19SDavid S. Miller 			    void *arg)
16961e493d19SDavid S. Miller {
16971e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16981e493d19SDavid S. Miller 
16991e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
17001e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
17011e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
17021e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
17031e493d19SDavid S. Miller 		if (func(rt, arg)) {
17041e493d19SDavid S. Miller 			*pprev = dst->next;
17051e493d19SDavid S. Miller 			dst_free(dst);
17061e493d19SDavid S. Miller 		} else {
17071e493d19SDavid S. Miller 			pprev = &dst->next;
17081e493d19SDavid S. Miller 		}
17091e493d19SDavid S. Miller 	}
17101e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
17111e493d19SDavid S. Miller }
17121e493d19SDavid S. Miller 
1713569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
17141da177e4SLinus Torvalds {
171586393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
17167019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
17177019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
17187019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
17197019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
17207019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1721fc66f95cSEric Dumazet 	int entries;
17221da177e4SLinus Torvalds 
1723fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
172449a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1725fc66f95cSEric Dumazet 	    entries <= rt_max_size)
17261da177e4SLinus Torvalds 		goto out;
17271da177e4SLinus Torvalds 
17286891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
172914956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1730fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1731fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
17327019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
17331da177e4SLinus Torvalds out:
17347019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1735fc66f95cSEric Dumazet 	return entries > rt_max_size;
17361da177e4SLinus Torvalds }
17371da177e4SLinus Torvalds 
1738e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1739e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1740e715b6d3SFlorian Westphal {
1741c3a8d947SDaniel Borkmann 	bool ecn_ca = false;
1742e715b6d3SFlorian Westphal 	struct nlattr *nla;
1743e715b6d3SFlorian Westphal 	int remaining;
1744e715b6d3SFlorian Westphal 	u32 *mp;
1745e715b6d3SFlorian Westphal 
174663159f29SIan Morris 	if (!cfg->fc_mx)
1747e715b6d3SFlorian Westphal 		return 0;
1748e715b6d3SFlorian Westphal 
1749e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1750e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1751e715b6d3SFlorian Westphal 		return -ENOMEM;
1752e715b6d3SFlorian Westphal 
1753e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1754e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1755ea697639SDaniel Borkmann 		u32 val;
1756ea697639SDaniel Borkmann 
17571bb14807SDaniel Borkmann 		if (!type)
17581bb14807SDaniel Borkmann 			continue;
1759e715b6d3SFlorian Westphal 		if (unlikely(type > RTAX_MAX))
1760e715b6d3SFlorian Westphal 			goto err;
17611bb14807SDaniel Borkmann 
1762ea697639SDaniel Borkmann 		if (type == RTAX_CC_ALGO) {
1763ea697639SDaniel Borkmann 			char tmp[TCP_CA_NAME_MAX];
1764e715b6d3SFlorian Westphal 
1765ea697639SDaniel Borkmann 			nla_strlcpy(tmp, nla, sizeof(tmp));
1766c3a8d947SDaniel Borkmann 			val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
1767ea697639SDaniel Borkmann 			if (val == TCP_CA_UNSPEC)
1768ea697639SDaniel Borkmann 				goto err;
1769ea697639SDaniel Borkmann 		} else {
1770ea697639SDaniel Borkmann 			val = nla_get_u32(nla);
1771ea697639SDaniel Borkmann 		}
1772626abd59SPaolo Abeni 		if (type == RTAX_HOPLIMIT && val > 255)
1773626abd59SPaolo Abeni 			val = 255;
1774b8d3e416SDaniel Borkmann 		if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
1775b8d3e416SDaniel Borkmann 			goto err;
1776ea697639SDaniel Borkmann 
1777ea697639SDaniel Borkmann 		mp[type - 1] = val;
1778e715b6d3SFlorian Westphal 		__set_bit(type - 1, mxc->mx_valid);
1779e715b6d3SFlorian Westphal 	}
1780e715b6d3SFlorian Westphal 
1781c3a8d947SDaniel Borkmann 	if (ecn_ca) {
1782c3a8d947SDaniel Borkmann 		__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
1783c3a8d947SDaniel Borkmann 		mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
1784c3a8d947SDaniel Borkmann 	}
1785e715b6d3SFlorian Westphal 
1786c3a8d947SDaniel Borkmann 	mxc->mx = mp;
1787e715b6d3SFlorian Westphal 	return 0;
1788e715b6d3SFlorian Westphal  err:
1789e715b6d3SFlorian Westphal 	kfree(mp);
1790e715b6d3SFlorian Westphal 	return -EINVAL;
1791e715b6d3SFlorian Westphal }
17921da177e4SLinus Torvalds 
17938c14586fSDavid Ahern static struct rt6_info *ip6_nh_lookup_table(struct net *net,
17948c14586fSDavid Ahern 					    struct fib6_config *cfg,
17958c14586fSDavid Ahern 					    const struct in6_addr *gw_addr)
17968c14586fSDavid Ahern {
17978c14586fSDavid Ahern 	struct flowi6 fl6 = {
17988c14586fSDavid Ahern 		.flowi6_oif = cfg->fc_ifindex,
17998c14586fSDavid Ahern 		.daddr = *gw_addr,
18008c14586fSDavid Ahern 		.saddr = cfg->fc_prefsrc,
18018c14586fSDavid Ahern 	};
18028c14586fSDavid Ahern 	struct fib6_table *table;
18038c14586fSDavid Ahern 	struct rt6_info *rt;
1804d5d32e4bSDavid Ahern 	int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_IGNORE_LINKSTATE;
18058c14586fSDavid Ahern 
18068c14586fSDavid Ahern 	table = fib6_get_table(net, cfg->fc_table);
18078c14586fSDavid Ahern 	if (!table)
18088c14586fSDavid Ahern 		return NULL;
18098c14586fSDavid Ahern 
18108c14586fSDavid Ahern 	if (!ipv6_addr_any(&cfg->fc_prefsrc))
18118c14586fSDavid Ahern 		flags |= RT6_LOOKUP_F_HAS_SADDR;
18128c14586fSDavid Ahern 
18138c14586fSDavid Ahern 	rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags);
18148c14586fSDavid Ahern 
18158c14586fSDavid Ahern 	/* if table lookup failed, fall back to full lookup */
18168c14586fSDavid Ahern 	if (rt == net->ipv6.ip6_null_entry) {
18178c14586fSDavid Ahern 		ip6_rt_put(rt);
18188c14586fSDavid Ahern 		rt = NULL;
18198c14586fSDavid Ahern 	}
18208c14586fSDavid Ahern 
18218c14586fSDavid Ahern 	return rt;
18228c14586fSDavid Ahern }
18238c14586fSDavid Ahern 
18248c5b83f0SRoopa Prabhu static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
18251da177e4SLinus Torvalds {
18265578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
18271da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
18281da177e4SLinus Torvalds 	struct net_device *dev = NULL;
18291da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1830c71099acSThomas Graf 	struct fib6_table *table;
18311da177e4SLinus Torvalds 	int addr_type;
18328c5b83f0SRoopa Prabhu 	int err = -EINVAL;
18331da177e4SLinus Torvalds 
183486872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
18358c5b83f0SRoopa Prabhu 		goto out;
18361da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
183786872cb5SThomas Graf 	if (cfg->fc_src_len)
18388c5b83f0SRoopa Prabhu 		goto out;
18391da177e4SLinus Torvalds #endif
184086872cb5SThomas Graf 	if (cfg->fc_ifindex) {
18411da177e4SLinus Torvalds 		err = -ENODEV;
18425578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
18431da177e4SLinus Torvalds 		if (!dev)
18441da177e4SLinus Torvalds 			goto out;
18451da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
18461da177e4SLinus Torvalds 		if (!idev)
18471da177e4SLinus Torvalds 			goto out;
18481da177e4SLinus Torvalds 	}
18491da177e4SLinus Torvalds 
185086872cb5SThomas Graf 	if (cfg->fc_metric == 0)
185186872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
18521da177e4SLinus Torvalds 
1853c71099acSThomas Graf 	err = -ENOBUFS;
185438308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1855d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1856d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
185738308473SDavid S. Miller 		if (!table) {
1858f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1859d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1860d71314b4SMatti Vaittinen 		}
1861d71314b4SMatti Vaittinen 	} else {
1862d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1863d71314b4SMatti Vaittinen 	}
186438308473SDavid S. Miller 
186538308473SDavid S. Miller 	if (!table)
1866c71099acSThomas Graf 		goto out;
1867c71099acSThomas Graf 
1868ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, NULL,
1869ad706862SMartin KaFai Lau 			   (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT);
18701da177e4SLinus Torvalds 
187138308473SDavid S. Miller 	if (!rt) {
18721da177e4SLinus Torvalds 		err = -ENOMEM;
18731da177e4SLinus Torvalds 		goto out;
18741da177e4SLinus Torvalds 	}
18751da177e4SLinus Torvalds 
18761716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
18771716a961SGao feng 		rt6_set_expires(rt, jiffies +
18781716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
18791716a961SGao feng 	else
18801716a961SGao feng 		rt6_clean_expires(rt);
18811da177e4SLinus Torvalds 
188286872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
188386872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
188486872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
188586872cb5SThomas Graf 
188686872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
18871da177e4SLinus Torvalds 
18881da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1889d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1890ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1891ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
18921da177e4SLinus Torvalds 	else
1893d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
18941da177e4SLinus Torvalds 
1895d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
18961da177e4SLinus Torvalds 
189719e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
189819e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
189919e42e45SRoopa Prabhu 
1900*30357d7dSDavid Ahern 		err = lwtunnel_build_state(cfg->fc_encap_type,
1901127eb7cdSTom Herbert 					   cfg->fc_encap, AF_INET6, cfg,
1902127eb7cdSTom Herbert 					   &lwtstate);
190319e42e45SRoopa Prabhu 		if (err)
190419e42e45SRoopa Prabhu 			goto out;
190561adedf3SJiri Benc 		rt->dst.lwtstate = lwtstate_get(lwtstate);
190661adedf3SJiri Benc 		if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
190761adedf3SJiri Benc 			rt->dst.lwtstate->orig_output = rt->dst.output;
190861adedf3SJiri Benc 			rt->dst.output = lwtunnel_output;
190919e42e45SRoopa Prabhu 		}
191061adedf3SJiri Benc 		if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
191161adedf3SJiri Benc 			rt->dst.lwtstate->orig_input = rt->dst.input;
191261adedf3SJiri Benc 			rt->dst.input = lwtunnel_input;
191325368623STom Herbert 		}
191425368623STom Herbert 	}
191519e42e45SRoopa Prabhu 
191686872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
191786872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1918afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
191911d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
19201da177e4SLinus Torvalds 
19211da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
192286872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
192386872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
19241da177e4SLinus Torvalds #endif
19251da177e4SLinus Torvalds 
192686872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
19271da177e4SLinus Torvalds 
19281da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
19291da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
19301da177e4SLinus Torvalds 	 */
193186872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
193238308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
193338308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
193438308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
19351da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
19365578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
19371da177e4SLinus Torvalds 			if (dev) {
19381da177e4SLinus Torvalds 				dev_put(dev);
19391da177e4SLinus Torvalds 				in6_dev_put(idev);
19401da177e4SLinus Torvalds 			}
19415578689aSDaniel Lezcano 			dev = net->loopback_dev;
19421da177e4SLinus Torvalds 			dev_hold(dev);
19431da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
19441da177e4SLinus Torvalds 			if (!idev) {
19451da177e4SLinus Torvalds 				err = -ENODEV;
19461da177e4SLinus Torvalds 				goto out;
19471da177e4SLinus Torvalds 			}
19481da177e4SLinus Torvalds 		}
19491da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1950ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1951ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1952ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1953ede2059dSEric W. Biederman 			rt->dst.output = dst_discard_out;
19547150aedeSKamala R 			rt->dst.input = dst_discard;
1955ef2c7d7bSNicolas Dichtel 			break;
1956ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1957ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
19587150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
19597150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1960ef2c7d7bSNicolas Dichtel 			break;
1961b4949ab2SNicolas Dichtel 		case RTN_THROW:
19620315e382SNikola Forró 		case RTN_UNREACHABLE:
1963ef2c7d7bSNicolas Dichtel 		default:
19647150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
19650315e382SNikola Forró 					: (cfg->fc_type == RTN_UNREACHABLE)
19660315e382SNikola Forró 					? -EHOSTUNREACH : -ENETUNREACH;
19677150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
19687150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1969ef2c7d7bSNicolas Dichtel 			break;
1970ef2c7d7bSNicolas Dichtel 		}
19711da177e4SLinus Torvalds 		goto install_route;
19721da177e4SLinus Torvalds 	}
19731da177e4SLinus Torvalds 
197486872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1975b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
19761da177e4SLinus Torvalds 		int gwa_type;
19771da177e4SLinus Torvalds 
197886872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1979330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
198048ed7b26SFlorian Westphal 
198148ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
198248ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
198348ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
198448ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
198548ed7b26SFlorian Westphal 		 */
198648ed7b26SFlorian Westphal 		err = -EINVAL;
1987330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1988330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1989330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
199048ed7b26SFlorian Westphal 			goto out;
199148ed7b26SFlorian Westphal 
19924e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
19931da177e4SLinus Torvalds 
19941da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
19958c14586fSDavid Ahern 			struct rt6_info *grt = NULL;
19961da177e4SLinus Torvalds 
19971da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
19981da177e4SLinus Torvalds 			   addresses as nexthop address.
19991da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
20001da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
20011da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
20021da177e4SLinus Torvalds 			   some exceptions. --ANK
200396d5822cSErik Nordmark 			   We allow IPv4-mapped nexthops to support RFC4798-type
200496d5822cSErik Nordmark 			   addressing
20051da177e4SLinus Torvalds 			 */
200696d5822cSErik Nordmark 			if (!(gwa_type & (IPV6_ADDR_UNICAST |
200796d5822cSErik Nordmark 					  IPV6_ADDR_MAPPED)))
20081da177e4SLinus Torvalds 				goto out;
20091da177e4SLinus Torvalds 
2010a435a07fSVincent Bernat 			if (cfg->fc_table) {
20118c14586fSDavid Ahern 				grt = ip6_nh_lookup_table(net, cfg, gw_addr);
20128c14586fSDavid Ahern 
2013a435a07fSVincent Bernat 				if (grt) {
2014a435a07fSVincent Bernat 					if (grt->rt6i_flags & RTF_GATEWAY ||
2015a435a07fSVincent Bernat 					    (dev && dev != grt->dst.dev)) {
2016a435a07fSVincent Bernat 						ip6_rt_put(grt);
2017a435a07fSVincent Bernat 						grt = NULL;
2018a435a07fSVincent Bernat 					}
2019a435a07fSVincent Bernat 				}
2020a435a07fSVincent Bernat 			}
2021a435a07fSVincent Bernat 
20228c14586fSDavid Ahern 			if (!grt)
20238c14586fSDavid Ahern 				grt = rt6_lookup(net, gw_addr, NULL,
20248c14586fSDavid Ahern 						 cfg->fc_ifindex, 1);
20251da177e4SLinus Torvalds 
20261da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
202738308473SDavid S. Miller 			if (!grt)
20281da177e4SLinus Torvalds 				goto out;
20291da177e4SLinus Torvalds 			if (dev) {
2030d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
203194e187c0SAmerigo Wang 					ip6_rt_put(grt);
20321da177e4SLinus Torvalds 					goto out;
20331da177e4SLinus Torvalds 				}
20341da177e4SLinus Torvalds 			} else {
2035d1918542SDavid S. Miller 				dev = grt->dst.dev;
20361da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
20371da177e4SLinus Torvalds 				dev_hold(dev);
20381da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
20391da177e4SLinus Torvalds 			}
20401da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
20411da177e4SLinus Torvalds 				err = 0;
204294e187c0SAmerigo Wang 			ip6_rt_put(grt);
20431da177e4SLinus Torvalds 
20441da177e4SLinus Torvalds 			if (err)
20451da177e4SLinus Torvalds 				goto out;
20461da177e4SLinus Torvalds 		}
20471da177e4SLinus Torvalds 		err = -EINVAL;
204838308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
20491da177e4SLinus Torvalds 			goto out;
20501da177e4SLinus Torvalds 	}
20511da177e4SLinus Torvalds 
20521da177e4SLinus Torvalds 	err = -ENODEV;
205338308473SDavid S. Miller 	if (!dev)
20541da177e4SLinus Torvalds 		goto out;
20551da177e4SLinus Torvalds 
2056c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
2057c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
2058c3968a85SDaniel Walter 			err = -EINVAL;
2059c3968a85SDaniel Walter 			goto out;
2060c3968a85SDaniel Walter 		}
20614e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
2062c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
2063c3968a85SDaniel Walter 	} else
2064c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2065c3968a85SDaniel Walter 
206686872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
20671da177e4SLinus Torvalds 
20681da177e4SLinus Torvalds install_route:
2069d8d1f30bSChangli Gao 	rt->dst.dev = dev;
20701da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
2071c71099acSThomas Graf 	rt->rt6i_table = table;
207263152fc0SDaniel Lezcano 
2073c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
207463152fc0SDaniel Lezcano 
20758c5b83f0SRoopa Prabhu 	return rt;
20761da177e4SLinus Torvalds out:
20771da177e4SLinus Torvalds 	if (dev)
20781da177e4SLinus Torvalds 		dev_put(dev);
20791da177e4SLinus Torvalds 	if (idev)
20801da177e4SLinus Torvalds 		in6_dev_put(idev);
20811da177e4SLinus Torvalds 	if (rt)
2082d8d1f30bSChangli Gao 		dst_free(&rt->dst);
20836b9ea5a6SRoopa Prabhu 
20848c5b83f0SRoopa Prabhu 	return ERR_PTR(err);
20856b9ea5a6SRoopa Prabhu }
20866b9ea5a6SRoopa Prabhu 
20876b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg)
20886b9ea5a6SRoopa Prabhu {
20896b9ea5a6SRoopa Prabhu 	struct mx6_config mxc = { .mx = NULL, };
20908c5b83f0SRoopa Prabhu 	struct rt6_info *rt;
20916b9ea5a6SRoopa Prabhu 	int err;
20926b9ea5a6SRoopa Prabhu 
20938c5b83f0SRoopa Prabhu 	rt = ip6_route_info_create(cfg);
20948c5b83f0SRoopa Prabhu 	if (IS_ERR(rt)) {
20958c5b83f0SRoopa Prabhu 		err = PTR_ERR(rt);
20968c5b83f0SRoopa Prabhu 		rt = NULL;
20976b9ea5a6SRoopa Prabhu 		goto out;
20988c5b83f0SRoopa Prabhu 	}
20996b9ea5a6SRoopa Prabhu 
21006b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&mxc, cfg);
21016b9ea5a6SRoopa Prabhu 	if (err)
21026b9ea5a6SRoopa Prabhu 		goto out;
21036b9ea5a6SRoopa Prabhu 
21046b9ea5a6SRoopa Prabhu 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
21056b9ea5a6SRoopa Prabhu 
21066b9ea5a6SRoopa Prabhu 	kfree(mxc.mx);
21076b9ea5a6SRoopa Prabhu 
21086b9ea5a6SRoopa Prabhu 	return err;
21096b9ea5a6SRoopa Prabhu out:
21106b9ea5a6SRoopa Prabhu 	if (rt)
21116b9ea5a6SRoopa Prabhu 		dst_free(&rt->dst);
21126b9ea5a6SRoopa Prabhu 
21131da177e4SLinus Torvalds 	return err;
21141da177e4SLinus Torvalds }
21151da177e4SLinus Torvalds 
211686872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
21171da177e4SLinus Torvalds {
21181da177e4SLinus Torvalds 	int err;
2119c71099acSThomas Graf 	struct fib6_table *table;
2120d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
21211da177e4SLinus Torvalds 
21228e3d5be7SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry ||
21238e3d5be7SMartin KaFai Lau 	    rt->dst.flags & DST_NOCACHE) {
21246825a26cSGao feng 		err = -ENOENT;
21256825a26cSGao feng 		goto out;
21266825a26cSGao feng 	}
21276c813a72SPatrick McHardy 
2128c71099acSThomas Graf 	table = rt->rt6i_table;
2129c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
213086872cb5SThomas Graf 	err = fib6_del(rt, info);
2131c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
21321da177e4SLinus Torvalds 
21336825a26cSGao feng out:
213494e187c0SAmerigo Wang 	ip6_rt_put(rt);
21351da177e4SLinus Torvalds 	return err;
21361da177e4SLinus Torvalds }
21371da177e4SLinus Torvalds 
2138e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
2139e0a1ad73SThomas Graf {
21404d1169c1SDenis V. Lunev 	struct nl_info info = {
2141d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
21424d1169c1SDenis V. Lunev 	};
2143528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
2144e0a1ad73SThomas Graf }
2145e0a1ad73SThomas Graf 
214686872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
21471da177e4SLinus Torvalds {
2148c71099acSThomas Graf 	struct fib6_table *table;
21491da177e4SLinus Torvalds 	struct fib6_node *fn;
21501da177e4SLinus Torvalds 	struct rt6_info *rt;
21511da177e4SLinus Torvalds 	int err = -ESRCH;
21521da177e4SLinus Torvalds 
21535578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
215438308473SDavid S. Miller 	if (!table)
2155c71099acSThomas Graf 		return err;
21561da177e4SLinus Torvalds 
2157c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2158c71099acSThomas Graf 
2159c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
216086872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
216186872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
21621da177e4SLinus Torvalds 
21631da177e4SLinus Torvalds 	if (fn) {
2164d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
21651f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
21661f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
21671f56a01fSMartin KaFai Lau 				continue;
216886872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2169d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2170d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
21711da177e4SLinus Torvalds 				continue;
217286872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
217386872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
21741da177e4SLinus Torvalds 				continue;
217586872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
21761da177e4SLinus Torvalds 				continue;
2177c2ed1880SMantas M 			if (cfg->fc_protocol && cfg->fc_protocol != rt->rt6i_protocol)
2178c2ed1880SMantas M 				continue;
2179d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2180c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
21811da177e4SLinus Torvalds 
218286872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
21831da177e4SLinus Torvalds 		}
21841da177e4SLinus Torvalds 	}
2185c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
21861da177e4SLinus Torvalds 
21871da177e4SLinus Torvalds 	return err;
21881da177e4SLinus Torvalds }
21891da177e4SLinus Torvalds 
21906700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2191a6279458SYOSHIFUJI Hideaki {
2192a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2193e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2194e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2195e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2196e8599ff4SDavid S. Miller 	struct neighbour *neigh;
219771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
21986e157b6aSDavid S. Miller 	int optlen, on_link;
21996e157b6aSDavid S. Miller 	u8 *lladdr;
2200e8599ff4SDavid S. Miller 
220129a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
220271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2203e8599ff4SDavid S. Miller 
2204e8599ff4SDavid S. Miller 	if (optlen < 0) {
22056e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2206e8599ff4SDavid S. Miller 		return;
2207e8599ff4SDavid S. Miller 	}
2208e8599ff4SDavid S. Miller 
220971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2210e8599ff4SDavid S. Miller 
221171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
22126e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2213e8599ff4SDavid S. Miller 		return;
2214e8599ff4SDavid S. Miller 	}
2215e8599ff4SDavid S. Miller 
22166e157b6aSDavid S. Miller 	on_link = 0;
221771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2218e8599ff4SDavid S. Miller 		on_link = 1;
221971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2220e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
22216e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2222e8599ff4SDavid S. Miller 		return;
2223e8599ff4SDavid S. Miller 	}
2224e8599ff4SDavid S. Miller 
2225e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2226e8599ff4SDavid S. Miller 	if (!in6_dev)
2227e8599ff4SDavid S. Miller 		return;
2228e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2229e8599ff4SDavid S. Miller 		return;
2230e8599ff4SDavid S. Miller 
2231e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2232e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2233e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2234e8599ff4SDavid S. Miller 	 */
2235e8599ff4SDavid S. Miller 
2236f997c55cSAlexander Aring 	if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
2237e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2238e8599ff4SDavid S. Miller 		return;
2239e8599ff4SDavid S. Miller 	}
22406e157b6aSDavid S. Miller 
22416e157b6aSDavid S. Miller 	lladdr = NULL;
2242e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2243e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2244e8599ff4SDavid S. Miller 					     skb->dev);
2245e8599ff4SDavid S. Miller 		if (!lladdr) {
2246e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2247e8599ff4SDavid S. Miller 			return;
2248e8599ff4SDavid S. Miller 		}
2249e8599ff4SDavid S. Miller 	}
2250e8599ff4SDavid S. Miller 
22516e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
2252ec13ad1dSMatthias Schiffer 	if (rt->rt6i_flags & RTF_REJECT) {
22536e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
22546e157b6aSDavid S. Miller 		return;
22556e157b6aSDavid S. Miller 	}
22566e157b6aSDavid S. Miller 
22576e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
22586e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
22596e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
22606e157b6aSDavid S. Miller 	 */
22616e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
22626e157b6aSDavid S. Miller 
226371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2264e8599ff4SDavid S. Miller 	if (!neigh)
2265e8599ff4SDavid S. Miller 		return;
2266e8599ff4SDavid S. Miller 
22671da177e4SLinus Torvalds 	/*
22681da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
22691da177e4SLinus Torvalds 	 */
22701da177e4SLinus Torvalds 
2271f997c55cSAlexander Aring 	ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
22721da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
22731da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
22741da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
2275f997c55cSAlexander Aring 				     NEIGH_UPDATE_F_ISROUTER)),
2276f997c55cSAlexander Aring 		     NDISC_REDIRECT, &ndopts);
22771da177e4SLinus Torvalds 
227883a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
227938308473SDavid S. Miller 	if (!nrt)
22801da177e4SLinus Torvalds 		goto out;
22811da177e4SLinus Torvalds 
22821da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
22831da177e4SLinus Torvalds 	if (on_link)
22841da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
22851da177e4SLinus Torvalds 
22864e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
22871da177e4SLinus Torvalds 
228840e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
22891da177e4SLinus Torvalds 		goto out;
22901da177e4SLinus Torvalds 
2291d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2292d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
229371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
229460592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
22958d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
22968d71740cSTom Tucker 
22971da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
22986e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2299e0a1ad73SThomas Graf 		ip6_del_rt(rt);
23001da177e4SLinus Torvalds 	}
23011da177e4SLinus Torvalds 
23021da177e4SLinus Torvalds out:
2303e8599ff4SDavid S. Miller 	neigh_release(neigh);
23046e157b6aSDavid S. Miller }
23056e157b6aSDavid S. Miller 
23061da177e4SLinus Torvalds /*
23071da177e4SLinus Torvalds  *	Misc support functions
23081da177e4SLinus Torvalds  */
23091da177e4SLinus Torvalds 
23104b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
23114b32b5adSMartin KaFai Lau {
23124b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
23134b32b5adSMartin KaFai Lau 
23144b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
23154b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
23164b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
23174b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
23184b32b5adSMartin KaFai Lau }
23194b32b5adSMartin KaFai Lau 
232083a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
23211da177e4SLinus Torvalds {
2322d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2323d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
232483a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2325d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
23261da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
23271da177e4SLinus Torvalds 	if (rt->rt6i_idev)
23281da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2329d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
23304e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
23311716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
23321716a961SGao feng 	rt6_set_from(rt, ort);
233383a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
23341da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
233583a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
23361da177e4SLinus Torvalds #endif
233783a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2338c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
233961adedf3SJiri Benc 	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
23401da177e4SLinus Torvalds }
23411da177e4SLinus Torvalds 
234270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2343efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2344b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2345830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
2346830218c1SDavid Ahern 					   struct net_device *dev)
234770ceb4f5SYOSHIFUJI Hideaki {
2348830218c1SDavid Ahern 	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
2349830218c1SDavid Ahern 	int ifindex = dev->ifindex;
235070ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
235170ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2352c71099acSThomas Graf 	struct fib6_table *table;
235370ceb4f5SYOSHIFUJI Hideaki 
2354830218c1SDavid Ahern 	table = fib6_get_table(net, tb_id);
235538308473SDavid S. Miller 	if (!table)
2356c71099acSThomas Graf 		return NULL;
2357c71099acSThomas Graf 
23585744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2359c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
236070ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
236170ceb4f5SYOSHIFUJI Hideaki 		goto out;
236270ceb4f5SYOSHIFUJI Hideaki 
2363d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2364d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
236570ceb4f5SYOSHIFUJI Hideaki 			continue;
236670ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
236770ceb4f5SYOSHIFUJI Hideaki 			continue;
236870ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
236970ceb4f5SYOSHIFUJI Hideaki 			continue;
2370d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
237170ceb4f5SYOSHIFUJI Hideaki 		break;
237270ceb4f5SYOSHIFUJI Hideaki 	}
237370ceb4f5SYOSHIFUJI Hideaki out:
23745744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
237570ceb4f5SYOSHIFUJI Hideaki 	return rt;
237670ceb4f5SYOSHIFUJI Hideaki }
237770ceb4f5SYOSHIFUJI Hideaki 
2378efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2379b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2380830218c1SDavid Ahern 					   const struct in6_addr *gwaddr,
2381830218c1SDavid Ahern 					   struct net_device *dev,
238295c96174SEric Dumazet 					   unsigned int pref)
238370ceb4f5SYOSHIFUJI Hideaki {
238486872cb5SThomas Graf 	struct fib6_config cfg = {
2385238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
2386830218c1SDavid Ahern 		.fc_ifindex	= dev->ifindex,
238786872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
238886872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
238986872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
239015e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2391efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2392efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
239386872cb5SThomas Graf 	};
239470ceb4f5SYOSHIFUJI Hideaki 
2395830218c1SDavid Ahern 	cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
23964e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
23974e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
239886872cb5SThomas Graf 
2399e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2400e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
240186872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
240270ceb4f5SYOSHIFUJI Hideaki 
240386872cb5SThomas Graf 	ip6_route_add(&cfg);
240470ceb4f5SYOSHIFUJI Hideaki 
2405830218c1SDavid Ahern 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
240670ceb4f5SYOSHIFUJI Hideaki }
240770ceb4f5SYOSHIFUJI Hideaki #endif
240870ceb4f5SYOSHIFUJI Hideaki 
2409b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
24101da177e4SLinus Torvalds {
2411830218c1SDavid Ahern 	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
24121da177e4SLinus Torvalds 	struct rt6_info *rt;
2413c71099acSThomas Graf 	struct fib6_table *table;
24141da177e4SLinus Torvalds 
2415830218c1SDavid Ahern 	table = fib6_get_table(dev_net(dev), tb_id);
241638308473SDavid S. Miller 	if (!table)
2417c71099acSThomas Graf 		return NULL;
24181da177e4SLinus Torvalds 
24195744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2420d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2421d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2422045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
24231da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
24241da177e4SLinus Torvalds 			break;
24251da177e4SLinus Torvalds 	}
24261da177e4SLinus Torvalds 	if (rt)
2427d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
24285744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
24291da177e4SLinus Torvalds 	return rt;
24301da177e4SLinus Torvalds }
24311da177e4SLinus Torvalds 
2432b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2433ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2434ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
24351da177e4SLinus Torvalds {
243686872cb5SThomas Graf 	struct fib6_config cfg = {
2437ca254490SDavid Ahern 		.fc_table	= l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
2438238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
243986872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
244086872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
244186872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
244215e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
24435578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2444c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
244586872cb5SThomas Graf 	};
24461da177e4SLinus Torvalds 
24474e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
24481da177e4SLinus Torvalds 
2449830218c1SDavid Ahern 	if (!ip6_route_add(&cfg)) {
2450830218c1SDavid Ahern 		struct fib6_table *table;
2451830218c1SDavid Ahern 
2452830218c1SDavid Ahern 		table = fib6_get_table(dev_net(dev), cfg.fc_table);
2453830218c1SDavid Ahern 		if (table)
2454830218c1SDavid Ahern 			table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
2455830218c1SDavid Ahern 	}
24561da177e4SLinus Torvalds 
24571da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
24581da177e4SLinus Torvalds }
24591da177e4SLinus Torvalds 
2460830218c1SDavid Ahern static void __rt6_purge_dflt_routers(struct fib6_table *table)
24611da177e4SLinus Torvalds {
24621da177e4SLinus Torvalds 	struct rt6_info *rt;
24631da177e4SLinus Torvalds 
24641da177e4SLinus Torvalds restart:
2465c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2466d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
24673e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
24683e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2469d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2470c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2471e0a1ad73SThomas Graf 			ip6_del_rt(rt);
24721da177e4SLinus Torvalds 			goto restart;
24731da177e4SLinus Torvalds 		}
24741da177e4SLinus Torvalds 	}
2475c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
2476830218c1SDavid Ahern 
2477830218c1SDavid Ahern 	table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
2478830218c1SDavid Ahern }
2479830218c1SDavid Ahern 
2480830218c1SDavid Ahern void rt6_purge_dflt_routers(struct net *net)
2481830218c1SDavid Ahern {
2482830218c1SDavid Ahern 	struct fib6_table *table;
2483830218c1SDavid Ahern 	struct hlist_head *head;
2484830218c1SDavid Ahern 	unsigned int h;
2485830218c1SDavid Ahern 
2486830218c1SDavid Ahern 	rcu_read_lock();
2487830218c1SDavid Ahern 
2488830218c1SDavid Ahern 	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
2489830218c1SDavid Ahern 		head = &net->ipv6.fib_table_hash[h];
2490830218c1SDavid Ahern 		hlist_for_each_entry_rcu(table, head, tb6_hlist) {
2491830218c1SDavid Ahern 			if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
2492830218c1SDavid Ahern 				__rt6_purge_dflt_routers(table);
2493830218c1SDavid Ahern 		}
2494830218c1SDavid Ahern 	}
2495830218c1SDavid Ahern 
2496830218c1SDavid Ahern 	rcu_read_unlock();
24971da177e4SLinus Torvalds }
24981da177e4SLinus Torvalds 
24995578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
25005578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
250186872cb5SThomas Graf 				 struct fib6_config *cfg)
250286872cb5SThomas Graf {
250386872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
250486872cb5SThomas Graf 
2505ca254490SDavid Ahern 	cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
2506ca254490SDavid Ahern 			 : RT6_TABLE_MAIN;
250786872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
250886872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
250986872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
251086872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
251186872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
251286872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
251386872cb5SThomas Graf 
25145578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2515f1243c2dSBenjamin Thery 
25164e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
25174e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
25184e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
251986872cb5SThomas Graf }
252086872cb5SThomas Graf 
25215578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
25221da177e4SLinus Torvalds {
252386872cb5SThomas Graf 	struct fib6_config cfg;
25241da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
25251da177e4SLinus Torvalds 	int err;
25261da177e4SLinus Torvalds 
25271da177e4SLinus Torvalds 	switch (cmd) {
25281da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
25291da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2530af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
25311da177e4SLinus Torvalds 			return -EPERM;
25321da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
25331da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
25341da177e4SLinus Torvalds 		if (err)
25351da177e4SLinus Torvalds 			return -EFAULT;
25361da177e4SLinus Torvalds 
25375578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
253886872cb5SThomas Graf 
25391da177e4SLinus Torvalds 		rtnl_lock();
25401da177e4SLinus Torvalds 		switch (cmd) {
25411da177e4SLinus Torvalds 		case SIOCADDRT:
254286872cb5SThomas Graf 			err = ip6_route_add(&cfg);
25431da177e4SLinus Torvalds 			break;
25441da177e4SLinus Torvalds 		case SIOCDELRT:
254586872cb5SThomas Graf 			err = ip6_route_del(&cfg);
25461da177e4SLinus Torvalds 			break;
25471da177e4SLinus Torvalds 		default:
25481da177e4SLinus Torvalds 			err = -EINVAL;
25491da177e4SLinus Torvalds 		}
25501da177e4SLinus Torvalds 		rtnl_unlock();
25511da177e4SLinus Torvalds 
25521da177e4SLinus Torvalds 		return err;
25533ff50b79SStephen Hemminger 	}
25541da177e4SLinus Torvalds 
25551da177e4SLinus Torvalds 	return -EINVAL;
25561da177e4SLinus Torvalds }
25571da177e4SLinus Torvalds 
25581da177e4SLinus Torvalds /*
25591da177e4SLinus Torvalds  *	Drop the packet on the floor
25601da177e4SLinus Torvalds  */
25611da177e4SLinus Torvalds 
2562d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
25631da177e4SLinus Torvalds {
2564612f09e8SYOSHIFUJI Hideaki 	int type;
2565adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2566612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2567612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
25680660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
256945bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
25703bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
25713bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2572612f09e8SYOSHIFUJI Hideaki 			break;
2573612f09e8SYOSHIFUJI Hideaki 		}
2574612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2575612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
25763bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
25773bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2578612f09e8SYOSHIFUJI Hideaki 		break;
2579612f09e8SYOSHIFUJI Hideaki 	}
25803ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
25811da177e4SLinus Torvalds 	kfree_skb(skb);
25821da177e4SLinus Torvalds 	return 0;
25831da177e4SLinus Torvalds }
25841da177e4SLinus Torvalds 
25859ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
25869ce8ade0SThomas Graf {
2587612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
25889ce8ade0SThomas Graf }
25899ce8ade0SThomas Graf 
2590ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
25911da177e4SLinus Torvalds {
2592adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2593612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
25941da177e4SLinus Torvalds }
25951da177e4SLinus Torvalds 
25969ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
25979ce8ade0SThomas Graf {
2598612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
25999ce8ade0SThomas Graf }
26009ce8ade0SThomas Graf 
2601ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
26029ce8ade0SThomas Graf {
2603adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2604612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
26059ce8ade0SThomas Graf }
26069ce8ade0SThomas Graf 
26071da177e4SLinus Torvalds /*
26081da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
26091da177e4SLinus Torvalds  */
26101da177e4SLinus Torvalds 
26111da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
26121da177e4SLinus Torvalds 				    const struct in6_addr *addr,
26138f031519SDavid S. Miller 				    bool anycast)
26141da177e4SLinus Torvalds {
2615ca254490SDavid Ahern 	u32 tb_id;
2616c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
26175f02ce24SDavid Ahern 	struct net_device *dev = net->loopback_dev;
26185f02ce24SDavid Ahern 	struct rt6_info *rt;
26195f02ce24SDavid Ahern 
26205f02ce24SDavid Ahern 	/* use L3 Master device as loopback for host routes if device
26215f02ce24SDavid Ahern 	 * is enslaved and address is not link local or multicast
26225f02ce24SDavid Ahern 	 */
26235f02ce24SDavid Ahern 	if (!rt6_need_strict(addr))
26245f02ce24SDavid Ahern 		dev = l3mdev_master_dev_rcu(idev->dev) ? : dev;
26255f02ce24SDavid Ahern 
26265f02ce24SDavid Ahern 	rt = ip6_dst_alloc(net, dev, DST_NOCOUNT);
2627a3300ef4SHannes Frederic Sowa 	if (!rt)
26281da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
26291da177e4SLinus Torvalds 
26301da177e4SLinus Torvalds 	in6_dev_hold(idev);
26311da177e4SLinus Torvalds 
263211d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2633d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2634d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
26351da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
26361da177e4SLinus Torvalds 
26371da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
263858c4fb86SYOSHIFUJI Hideaki 	if (anycast)
263958c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
264058c4fb86SYOSHIFUJI Hideaki 	else
26411da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
26421da177e4SLinus Torvalds 
2643550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
26444e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
26451da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
2646ca254490SDavid Ahern 	tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
2647ca254490SDavid Ahern 	rt->rt6i_table = fib6_get_table(net, tb_id);
26488e3d5be7SMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
26491da177e4SLinus Torvalds 
2650d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
26511da177e4SLinus Torvalds 
26521da177e4SLinus Torvalds 	return rt;
26531da177e4SLinus Torvalds }
26541da177e4SLinus Torvalds 
2655c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2656c3968a85SDaniel Walter struct arg_dev_net_ip {
2657c3968a85SDaniel Walter 	struct net_device *dev;
2658c3968a85SDaniel Walter 	struct net *net;
2659c3968a85SDaniel Walter 	struct in6_addr *addr;
2660c3968a85SDaniel Walter };
2661c3968a85SDaniel Walter 
2662c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2663c3968a85SDaniel Walter {
2664c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2665c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2666c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2667c3968a85SDaniel Walter 
2668d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2669c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2670c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2671c3968a85SDaniel Walter 		/* remove prefsrc entry */
2672c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2673c3968a85SDaniel Walter 	}
2674c3968a85SDaniel Walter 	return 0;
2675c3968a85SDaniel Walter }
2676c3968a85SDaniel Walter 
2677c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2678c3968a85SDaniel Walter {
2679c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2680c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2681c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2682c3968a85SDaniel Walter 		.net = net,
2683c3968a85SDaniel Walter 		.addr = &ifp->addr,
2684c3968a85SDaniel Walter 	};
26850c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2686c3968a85SDaniel Walter }
2687c3968a85SDaniel Walter 
2688be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2689be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2690be7a010dSDuan Jiong 
2691be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2692be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2693be7a010dSDuan Jiong {
2694be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2695be7a010dSDuan Jiong 
2696be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2697be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2698be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2699be7a010dSDuan Jiong 		return -1;
2700be7a010dSDuan Jiong 	}
2701be7a010dSDuan Jiong 	return 0;
2702be7a010dSDuan Jiong }
2703be7a010dSDuan Jiong 
2704be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2705be7a010dSDuan Jiong {
2706be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2707be7a010dSDuan Jiong }
2708be7a010dSDuan Jiong 
27098ed67789SDaniel Lezcano struct arg_dev_net {
27108ed67789SDaniel Lezcano 	struct net_device *dev;
27118ed67789SDaniel Lezcano 	struct net *net;
27128ed67789SDaniel Lezcano };
27138ed67789SDaniel Lezcano 
2714a1a22c12SDavid Ahern /* called with write lock held for table with rt */
27151da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
27161da177e4SLinus Torvalds {
2717bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2718bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
27198ed67789SDaniel Lezcano 
2720d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2721a1a22c12SDavid Ahern 	    rt != adn->net->ipv6.ip6_null_entry &&
2722a1a22c12SDavid Ahern 	    (rt->rt6i_nsiblings == 0 ||
2723a1a22c12SDavid Ahern 	     !rt->rt6i_idev->cnf.ignore_routes_with_linkdown))
27241da177e4SLinus Torvalds 		return -1;
2725c159d30cSDavid S. Miller 
27261da177e4SLinus Torvalds 	return 0;
27271da177e4SLinus Torvalds }
27281da177e4SLinus Torvalds 
2729f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
27301da177e4SLinus Torvalds {
27318ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
27328ed67789SDaniel Lezcano 		.dev = dev,
27338ed67789SDaniel Lezcano 		.net = net,
27348ed67789SDaniel Lezcano 	};
27358ed67789SDaniel Lezcano 
27360c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
27371e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
2738e332bc67SEric W. Biederman 	if (dev)
27398d0b94afSMartin KaFai Lau 		rt6_uncached_list_flush_dev(net, dev);
27401da177e4SLinus Torvalds }
27411da177e4SLinus Torvalds 
274295c96174SEric Dumazet struct rt6_mtu_change_arg {
27431da177e4SLinus Torvalds 	struct net_device *dev;
274495c96174SEric Dumazet 	unsigned int mtu;
27451da177e4SLinus Torvalds };
27461da177e4SLinus Torvalds 
27471da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
27481da177e4SLinus Torvalds {
27491da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
27501da177e4SLinus Torvalds 	struct inet6_dev *idev;
27511da177e4SLinus Torvalds 
27521da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
27531da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
27541da177e4SLinus Torvalds 	   We still use this lock to block changes
27551da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
27561da177e4SLinus Torvalds 	*/
27571da177e4SLinus Torvalds 
27581da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
275938308473SDavid S. Miller 	if (!idev)
27601da177e4SLinus Torvalds 		return 0;
27611da177e4SLinus Torvalds 
27621da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
27631da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
27641da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
27651da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
27661da177e4SLinus Torvalds 	 */
27671da177e4SLinus Torvalds 	/*
27681da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
27691da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
27701da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
27711da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
27721da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
27731da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
277467c408cfSAlexander Alemayhu 	   PMTU discovery.
27751da177e4SLinus Torvalds 	 */
2776d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
2777fb56be83SMaciej Żenczykowski 	    dst_metric_raw(&rt->dst, RTAX_MTU) &&
27784b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
27794b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
27804b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
27814b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
27824b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
27834b32b5adSMartin KaFai Lau 			 * been updated.
27844b32b5adSMartin KaFai Lau 			 */
27854b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
27864b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
27874b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2788d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
27894b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2790defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2791566cfd8fSSimon Arlott 		}
27924b32b5adSMartin KaFai Lau 	}
27931da177e4SLinus Torvalds 	return 0;
27941da177e4SLinus Torvalds }
27951da177e4SLinus Torvalds 
279695c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
27971da177e4SLinus Torvalds {
2798c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2799c71099acSThomas Graf 		.dev = dev,
2800c71099acSThomas Graf 		.mtu = mtu,
2801c71099acSThomas Graf 	};
28021da177e4SLinus Torvalds 
28030c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
28041da177e4SLinus Torvalds }
28051da177e4SLinus Torvalds 
2806ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
28075176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
280886872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2809ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
281086872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
281186872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
281251ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2813c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
281419e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
281519e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
281632bc201eSXin Long 	[RTA_EXPIRES]		= { .type = NLA_U32 },
2817622ec2c9SLorenzo Colitti 	[RTA_UID]		= { .type = NLA_U32 },
281886872cb5SThomas Graf };
281986872cb5SThomas Graf 
282086872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
282186872cb5SThomas Graf 			      struct fib6_config *cfg)
28221da177e4SLinus Torvalds {
282386872cb5SThomas Graf 	struct rtmsg *rtm;
282486872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2825c78ba6d6SLubomir Rintel 	unsigned int pref;
282686872cb5SThomas Graf 	int err;
28271da177e4SLinus Torvalds 
282886872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
282986872cb5SThomas Graf 	if (err < 0)
283086872cb5SThomas Graf 		goto errout;
28311da177e4SLinus Torvalds 
283286872cb5SThomas Graf 	err = -EINVAL;
283386872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
283486872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
283586872cb5SThomas Graf 
283686872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
283786872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
283886872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
283986872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
284086872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2841ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
284286872cb5SThomas Graf 
2843ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2844ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2845b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2846b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
284786872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
284886872cb5SThomas Graf 
2849ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2850ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2851ab79ad14SMaciej Żenczykowski 
28521f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
28531f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
28541f56a01fSMartin KaFai Lau 
285515e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
285686872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
28573b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
285886872cb5SThomas Graf 
285986872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
286067b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
286186872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
28621da177e4SLinus Torvalds 	}
286386872cb5SThomas Graf 
286486872cb5SThomas Graf 	if (tb[RTA_DST]) {
286586872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
286686872cb5SThomas Graf 
286786872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
286886872cb5SThomas Graf 			goto errout;
286986872cb5SThomas Graf 
287086872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
28711da177e4SLinus Torvalds 	}
287286872cb5SThomas Graf 
287386872cb5SThomas Graf 	if (tb[RTA_SRC]) {
287486872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
287586872cb5SThomas Graf 
287686872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
287786872cb5SThomas Graf 			goto errout;
287886872cb5SThomas Graf 
287986872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
28801da177e4SLinus Torvalds 	}
288186872cb5SThomas Graf 
2882c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
288367b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2884c3968a85SDaniel Walter 
288586872cb5SThomas Graf 	if (tb[RTA_OIF])
288686872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
288786872cb5SThomas Graf 
288886872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
288986872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
289086872cb5SThomas Graf 
289186872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
289286872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
289386872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
28941da177e4SLinus Torvalds 	}
289586872cb5SThomas Graf 
289686872cb5SThomas Graf 	if (tb[RTA_TABLE])
289786872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
289886872cb5SThomas Graf 
289951ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
290051ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
290151ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
29029ed59592SDavid Ahern 
29039ed59592SDavid Ahern 		err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
29049ed59592SDavid Ahern 						     cfg->fc_mp_len);
29059ed59592SDavid Ahern 		if (err < 0)
29069ed59592SDavid Ahern 			goto errout;
290751ebd318SNicolas Dichtel 	}
290851ebd318SNicolas Dichtel 
2909c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2910c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2911c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2912c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2913c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2914c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2915c78ba6d6SLubomir Rintel 	}
2916c78ba6d6SLubomir Rintel 
291719e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
291819e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
291919e42e45SRoopa Prabhu 
29209ed59592SDavid Ahern 	if (tb[RTA_ENCAP_TYPE]) {
292119e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
292219e42e45SRoopa Prabhu 
29239ed59592SDavid Ahern 		err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
29249ed59592SDavid Ahern 		if (err < 0)
29259ed59592SDavid Ahern 			goto errout;
29269ed59592SDavid Ahern 	}
29279ed59592SDavid Ahern 
292832bc201eSXin Long 	if (tb[RTA_EXPIRES]) {
292932bc201eSXin Long 		unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
293032bc201eSXin Long 
293132bc201eSXin Long 		if (addrconf_finite_timeout(timeout)) {
293232bc201eSXin Long 			cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
293332bc201eSXin Long 			cfg->fc_flags |= RTF_EXPIRES;
293432bc201eSXin Long 		}
293532bc201eSXin Long 	}
293632bc201eSXin Long 
293786872cb5SThomas Graf 	err = 0;
293886872cb5SThomas Graf errout:
293986872cb5SThomas Graf 	return err;
29401da177e4SLinus Torvalds }
29411da177e4SLinus Torvalds 
29426b9ea5a6SRoopa Prabhu struct rt6_nh {
29436b9ea5a6SRoopa Prabhu 	struct rt6_info *rt6_info;
29446b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
29456b9ea5a6SRoopa Prabhu 	struct mx6_config mxc;
29466b9ea5a6SRoopa Prabhu 	struct list_head next;
29476b9ea5a6SRoopa Prabhu };
29486b9ea5a6SRoopa Prabhu 
29496b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
29506b9ea5a6SRoopa Prabhu {
29516b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
29526b9ea5a6SRoopa Prabhu 
29536b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
29546b9ea5a6SRoopa Prabhu 		pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n",
29556b9ea5a6SRoopa Prabhu 		        &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway,
29566b9ea5a6SRoopa Prabhu 		        nh->r_cfg.fc_ifindex);
29576b9ea5a6SRoopa Prabhu 	}
29586b9ea5a6SRoopa Prabhu }
29596b9ea5a6SRoopa Prabhu 
29606b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list,
29616b9ea5a6SRoopa Prabhu 				 struct rt6_info *rt, struct fib6_config *r_cfg)
29626b9ea5a6SRoopa Prabhu {
29636b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
29646b9ea5a6SRoopa Prabhu 	struct rt6_info *rtnh;
29656b9ea5a6SRoopa Prabhu 	int err = -EEXIST;
29666b9ea5a6SRoopa Prabhu 
29676b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
29686b9ea5a6SRoopa Prabhu 		/* check if rt6_info already exists */
29696b9ea5a6SRoopa Prabhu 		rtnh = nh->rt6_info;
29706b9ea5a6SRoopa Prabhu 
29716b9ea5a6SRoopa Prabhu 		if (rtnh->dst.dev == rt->dst.dev &&
29726b9ea5a6SRoopa Prabhu 		    rtnh->rt6i_idev == rt->rt6i_idev &&
29736b9ea5a6SRoopa Prabhu 		    ipv6_addr_equal(&rtnh->rt6i_gateway,
29746b9ea5a6SRoopa Prabhu 				    &rt->rt6i_gateway))
29756b9ea5a6SRoopa Prabhu 			return err;
29766b9ea5a6SRoopa Prabhu 	}
29776b9ea5a6SRoopa Prabhu 
29786b9ea5a6SRoopa Prabhu 	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
29796b9ea5a6SRoopa Prabhu 	if (!nh)
29806b9ea5a6SRoopa Prabhu 		return -ENOMEM;
29816b9ea5a6SRoopa Prabhu 	nh->rt6_info = rt;
29826b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&nh->mxc, r_cfg);
29836b9ea5a6SRoopa Prabhu 	if (err) {
29846b9ea5a6SRoopa Prabhu 		kfree(nh);
29856b9ea5a6SRoopa Prabhu 		return err;
29866b9ea5a6SRoopa Prabhu 	}
29876b9ea5a6SRoopa Prabhu 	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
29886b9ea5a6SRoopa Prabhu 	list_add_tail(&nh->next, rt6_nh_list);
29896b9ea5a6SRoopa Prabhu 
29906b9ea5a6SRoopa Prabhu 	return 0;
29916b9ea5a6SRoopa Prabhu }
29926b9ea5a6SRoopa Prabhu 
29936b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg)
299451ebd318SNicolas Dichtel {
299551ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
299651ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
29976b9ea5a6SRoopa Prabhu 	struct rt6_info *rt;
29986b9ea5a6SRoopa Prabhu 	struct rt6_nh *err_nh;
29996b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh, *nh_safe;
300051ebd318SNicolas Dichtel 	int remaining;
300151ebd318SNicolas Dichtel 	int attrlen;
30026b9ea5a6SRoopa Prabhu 	int err = 1;
30036b9ea5a6SRoopa Prabhu 	int nhn = 0;
30046b9ea5a6SRoopa Prabhu 	int replace = (cfg->fc_nlinfo.nlh &&
30056b9ea5a6SRoopa Prabhu 		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
30066b9ea5a6SRoopa Prabhu 	LIST_HEAD(rt6_nh_list);
300751ebd318SNicolas Dichtel 
300835f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
300951ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
301051ebd318SNicolas Dichtel 
30116b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
30126b9ea5a6SRoopa Prabhu 	 * rt6_info structs per nexthop
30136b9ea5a6SRoopa Prabhu 	 */
301451ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
301551ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
301651ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
301751ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
301851ebd318SNicolas Dichtel 
301951ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
302051ebd318SNicolas Dichtel 		if (attrlen > 0) {
302151ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
302251ebd318SNicolas Dichtel 
302351ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
302451ebd318SNicolas Dichtel 			if (nla) {
302567b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
302651ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
302751ebd318SNicolas Dichtel 			}
302819e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
302919e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
303019e42e45SRoopa Prabhu 			if (nla)
303119e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
303251ebd318SNicolas Dichtel 		}
30336b9ea5a6SRoopa Prabhu 
30348c5b83f0SRoopa Prabhu 		rt = ip6_route_info_create(&r_cfg);
30358c5b83f0SRoopa Prabhu 		if (IS_ERR(rt)) {
30368c5b83f0SRoopa Prabhu 			err = PTR_ERR(rt);
30378c5b83f0SRoopa Prabhu 			rt = NULL;
30386b9ea5a6SRoopa Prabhu 			goto cleanup;
30398c5b83f0SRoopa Prabhu 		}
30406b9ea5a6SRoopa Prabhu 
30416b9ea5a6SRoopa Prabhu 		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
304251ebd318SNicolas Dichtel 		if (err) {
30436b9ea5a6SRoopa Prabhu 			dst_free(&rt->dst);
30446b9ea5a6SRoopa Prabhu 			goto cleanup;
304551ebd318SNicolas Dichtel 		}
30466b9ea5a6SRoopa Prabhu 
30476b9ea5a6SRoopa Prabhu 		rtnh = rtnh_next(rtnh, &remaining);
304851ebd318SNicolas Dichtel 	}
30496b9ea5a6SRoopa Prabhu 
30506b9ea5a6SRoopa Prabhu 	err_nh = NULL;
30516b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
30526b9ea5a6SRoopa Prabhu 		err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc);
30536b9ea5a6SRoopa Prabhu 		/* nh->rt6_info is used or freed at this point, reset to NULL*/
30546b9ea5a6SRoopa Prabhu 		nh->rt6_info = NULL;
30556b9ea5a6SRoopa Prabhu 		if (err) {
30566b9ea5a6SRoopa Prabhu 			if (replace && nhn)
30576b9ea5a6SRoopa Prabhu 				ip6_print_replace_route_err(&rt6_nh_list);
30586b9ea5a6SRoopa Prabhu 			err_nh = nh;
30596b9ea5a6SRoopa Prabhu 			goto add_errout;
30606b9ea5a6SRoopa Prabhu 		}
30616b9ea5a6SRoopa Prabhu 
30621a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
306327596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
306427596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
306527596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
306627596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
306727596472SMichal Kubeček 		 * be added to it.
30681a72418bSNicolas Dichtel 		 */
306927596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
307027596472SMichal Kubeček 						     NLM_F_REPLACE);
30716b9ea5a6SRoopa Prabhu 		nhn++;
30726b9ea5a6SRoopa Prabhu 	}
30736b9ea5a6SRoopa Prabhu 
30746b9ea5a6SRoopa Prabhu 	goto cleanup;
30756b9ea5a6SRoopa Prabhu 
30766b9ea5a6SRoopa Prabhu add_errout:
30776b9ea5a6SRoopa Prabhu 	/* Delete routes that were already added */
30786b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
30796b9ea5a6SRoopa Prabhu 		if (err_nh == nh)
30806b9ea5a6SRoopa Prabhu 			break;
30816b9ea5a6SRoopa Prabhu 		ip6_route_del(&nh->r_cfg);
30826b9ea5a6SRoopa Prabhu 	}
30836b9ea5a6SRoopa Prabhu 
30846b9ea5a6SRoopa Prabhu cleanup:
30856b9ea5a6SRoopa Prabhu 	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
30866b9ea5a6SRoopa Prabhu 		if (nh->rt6_info)
30876b9ea5a6SRoopa Prabhu 			dst_free(&nh->rt6_info->dst);
30886b9ea5a6SRoopa Prabhu 		kfree(nh->mxc.mx);
30896b9ea5a6SRoopa Prabhu 		list_del(&nh->next);
30906b9ea5a6SRoopa Prabhu 		kfree(nh);
30916b9ea5a6SRoopa Prabhu 	}
30926b9ea5a6SRoopa Prabhu 
30936b9ea5a6SRoopa Prabhu 	return err;
30946b9ea5a6SRoopa Prabhu }
30956b9ea5a6SRoopa Prabhu 
30966b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg)
30976b9ea5a6SRoopa Prabhu {
30986b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
30996b9ea5a6SRoopa Prabhu 	struct rtnexthop *rtnh;
31006b9ea5a6SRoopa Prabhu 	int remaining;
31016b9ea5a6SRoopa Prabhu 	int attrlen;
31026b9ea5a6SRoopa Prabhu 	int err = 1, last_err = 0;
31036b9ea5a6SRoopa Prabhu 
31046b9ea5a6SRoopa Prabhu 	remaining = cfg->fc_mp_len;
31056b9ea5a6SRoopa Prabhu 	rtnh = (struct rtnexthop *)cfg->fc_mp;
31066b9ea5a6SRoopa Prabhu 
31076b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry */
31086b9ea5a6SRoopa Prabhu 	while (rtnh_ok(rtnh, remaining)) {
31096b9ea5a6SRoopa Prabhu 		memcpy(&r_cfg, cfg, sizeof(*cfg));
31106b9ea5a6SRoopa Prabhu 		if (rtnh->rtnh_ifindex)
31116b9ea5a6SRoopa Prabhu 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
31126b9ea5a6SRoopa Prabhu 
31136b9ea5a6SRoopa Prabhu 		attrlen = rtnh_attrlen(rtnh);
31146b9ea5a6SRoopa Prabhu 		if (attrlen > 0) {
31156b9ea5a6SRoopa Prabhu 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
31166b9ea5a6SRoopa Prabhu 
31176b9ea5a6SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
31186b9ea5a6SRoopa Prabhu 			if (nla) {
31196b9ea5a6SRoopa Prabhu 				nla_memcpy(&r_cfg.fc_gateway, nla, 16);
31206b9ea5a6SRoopa Prabhu 				r_cfg.fc_flags |= RTF_GATEWAY;
31216b9ea5a6SRoopa Prabhu 			}
31226b9ea5a6SRoopa Prabhu 		}
31236b9ea5a6SRoopa Prabhu 		err = ip6_route_del(&r_cfg);
31246b9ea5a6SRoopa Prabhu 		if (err)
31256b9ea5a6SRoopa Prabhu 			last_err = err;
31266b9ea5a6SRoopa Prabhu 
312751ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
312851ebd318SNicolas Dichtel 	}
312951ebd318SNicolas Dichtel 
313051ebd318SNicolas Dichtel 	return last_err;
313151ebd318SNicolas Dichtel }
313251ebd318SNicolas Dichtel 
3133661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
31341da177e4SLinus Torvalds {
313586872cb5SThomas Graf 	struct fib6_config cfg;
313686872cb5SThomas Graf 	int err;
31371da177e4SLinus Torvalds 
313886872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
313986872cb5SThomas Graf 	if (err < 0)
314086872cb5SThomas Graf 		return err;
314186872cb5SThomas Graf 
314251ebd318SNicolas Dichtel 	if (cfg.fc_mp)
31436b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_del(&cfg);
314451ebd318SNicolas Dichtel 	else
314586872cb5SThomas Graf 		return ip6_route_del(&cfg);
31461da177e4SLinus Torvalds }
31471da177e4SLinus Torvalds 
3148661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
31491da177e4SLinus Torvalds {
315086872cb5SThomas Graf 	struct fib6_config cfg;
315186872cb5SThomas Graf 	int err;
31521da177e4SLinus Torvalds 
315386872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
315486872cb5SThomas Graf 	if (err < 0)
315586872cb5SThomas Graf 		return err;
315686872cb5SThomas Graf 
315751ebd318SNicolas Dichtel 	if (cfg.fc_mp)
31586b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_add(&cfg);
315951ebd318SNicolas Dichtel 	else
316086872cb5SThomas Graf 		return ip6_route_add(&cfg);
31611da177e4SLinus Torvalds }
31621da177e4SLinus Torvalds 
316319e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
3164339bf98fSThomas Graf {
3165339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
3166339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
3167339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
3168339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
3169339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
3170339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
3171339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
3172339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
3173339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
31746a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
3175ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
3176c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
317719e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
317861adedf3SJiri Benc 	       + lwtunnel_get_encap_size(rt->dst.lwtstate);
3179339bf98fSThomas Graf }
3180339bf98fSThomas Graf 
3181191cd582SBrian Haley static int rt6_fill_node(struct net *net,
3182191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
31830d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
318415e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
3185f8cfe2ceSDavid Ahern 			 unsigned int flags)
31861da177e4SLinus Torvalds {
31874b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
31881da177e4SLinus Torvalds 	struct rtmsg *rtm;
31891da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
3190e3703b3dSThomas Graf 	long expires;
31919e762a4aSPatrick McHardy 	u32 table;
31921da177e4SLinus Torvalds 
319315e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
319438308473SDavid S. Miller 	if (!nlh)
319526932566SPatrick McHardy 		return -EMSGSIZE;
31962d7202bfSThomas Graf 
31972d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
31981da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
31991da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
32001da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
32011da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
3202c71099acSThomas Graf 	if (rt->rt6i_table)
32039e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
3204c71099acSThomas Graf 	else
32059e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
32069e762a4aSPatrick McHardy 	rtm->rtm_table = table;
3207c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
3208c78679e8SDavid S. Miller 		goto nla_put_failure;
3209ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
3210ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
3211ef2c7d7bSNicolas Dichtel 		case -EINVAL:
3212ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
3213ef2c7d7bSNicolas Dichtel 			break;
3214ef2c7d7bSNicolas Dichtel 		case -EACCES:
3215ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
3216ef2c7d7bSNicolas Dichtel 			break;
3217b4949ab2SNicolas Dichtel 		case -EAGAIN:
3218b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
3219b4949ab2SNicolas Dichtel 			break;
3220ef2c7d7bSNicolas Dichtel 		default:
32211da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
3222ef2c7d7bSNicolas Dichtel 			break;
3223ef2c7d7bSNicolas Dichtel 		}
3224ef2c7d7bSNicolas Dichtel 	}
3225ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
3226ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
3227d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
32281da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
32291da177e4SLinus Torvalds 	else
32301da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
32311da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
3232a1a22c12SDavid Ahern 	if (!netif_running(rt->dst.dev) || !netif_carrier_ok(rt->dst.dev)) {
3233cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
323435103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
323535103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
323635103d11SAndy Gospodarek 	}
32371da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
32381da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
32391da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
32401da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
3241f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
3242f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
32431da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
3244f0396f60SDenis Ovsienko 		else
3245f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
3246f0396f60SDenis Ovsienko 	}
32471da177e4SLinus Torvalds 
32481da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
32491da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
32501da177e4SLinus Torvalds 
32511da177e4SLinus Torvalds 	if (dst) {
3252930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
3253c78679e8SDavid S. Miller 			goto nla_put_failure;
32541da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
32551da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
3256930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
3257c78679e8SDavid S. Miller 			goto nla_put_failure;
32581da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
32591da177e4SLinus Torvalds 	if (src) {
3260930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
3261c78679e8SDavid S. Miller 			goto nla_put_failure;
32621da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
3263c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
3264930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
3265c78679e8SDavid S. Miller 		goto nla_put_failure;
32661da177e4SLinus Torvalds #endif
32677bc570c8SYOSHIFUJI Hideaki 	if (iif) {
32687bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
32697bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
3270fd61c6baSDavid Ahern 			int err = ip6mr_get_route(net, skb, rtm, portid);
32712cf75070SNikolay Aleksandrov 
32727bc570c8SYOSHIFUJI Hideaki 			if (err == 0)
32737bc570c8SYOSHIFUJI Hideaki 				return 0;
3274fd61c6baSDavid Ahern 			if (err < 0)
32757bc570c8SYOSHIFUJI Hideaki 				goto nla_put_failure;
32767bc570c8SYOSHIFUJI Hideaki 		} else
32777bc570c8SYOSHIFUJI Hideaki #endif
3278c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
3279c78679e8SDavid S. Miller 				goto nla_put_failure;
32807bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
32811da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
3282c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
3283930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3284c78679e8SDavid S. Miller 			goto nla_put_failure;
3285c3968a85SDaniel Walter 	}
3286c3968a85SDaniel Walter 
3287c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
3288c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
32894e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
3290930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3291c78679e8SDavid S. Miller 			goto nla_put_failure;
32921da177e4SLinus Torvalds 	}
32932d7202bfSThomas Graf 
32944b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
32954b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
32964b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
32974b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
32982d7202bfSThomas Graf 		goto nla_put_failure;
32992d7202bfSThomas Graf 
3300dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
3301930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
330294f826b8SEric Dumazet 			goto nla_put_failure;
330394f826b8SEric Dumazet 	}
33042d7202bfSThomas Graf 
3305c78679e8SDavid S. Miller 	if (rt->dst.dev &&
3306c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3307c78679e8SDavid S. Miller 		goto nla_put_failure;
3308c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
3309c78679e8SDavid S. Miller 		goto nla_put_failure;
33108253947eSLi Wei 
33118253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
331269cdf8f9SYOSHIFUJI Hideaki 
331387a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
3314e3703b3dSThomas Graf 		goto nla_put_failure;
33151da177e4SLinus Torvalds 
3316c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
3317c78ba6d6SLubomir Rintel 		goto nla_put_failure;
3318c78ba6d6SLubomir Rintel 
3319ea7a8085SDavid Ahern 	if (lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
3320ea7a8085SDavid Ahern 		goto nla_put_failure;
332119e42e45SRoopa Prabhu 
3322053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
3323053c095aSJohannes Berg 	return 0;
33242d7202bfSThomas Graf 
33252d7202bfSThomas Graf nla_put_failure:
332626932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
332726932566SPatrick McHardy 	return -EMSGSIZE;
33281da177e4SLinus Torvalds }
33291da177e4SLinus Torvalds 
33301b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
33311da177e4SLinus Torvalds {
33321da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
33331f17e2f2SDavid Ahern 	struct net *net = arg->net;
33341f17e2f2SDavid Ahern 
33351f17e2f2SDavid Ahern 	if (rt == net->ipv6.ip6_null_entry)
33361f17e2f2SDavid Ahern 		return 0;
33371da177e4SLinus Torvalds 
33382d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
33392d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
3340f8cfe2ceSDavid Ahern 
3341f8cfe2ceSDavid Ahern 		/* user wants prefix routes only */
3342f8cfe2ceSDavid Ahern 		if (rtm->rtm_flags & RTM_F_PREFIX &&
3343f8cfe2ceSDavid Ahern 		    !(rt->rt6i_flags & RTF_PREFIX_RT)) {
3344f8cfe2ceSDavid Ahern 			/* success since this is not a prefix route */
3345f8cfe2ceSDavid Ahern 			return 1;
3346f8cfe2ceSDavid Ahern 		}
3347f8cfe2ceSDavid Ahern 	}
33481da177e4SLinus Torvalds 
33491f17e2f2SDavid Ahern 	return rt6_fill_node(net,
3350191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
335115e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
3352f8cfe2ceSDavid Ahern 		     NLM_F_MULTI);
33531da177e4SLinus Torvalds }
33541da177e4SLinus Torvalds 
3355661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
33561da177e4SLinus Torvalds {
33573b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3358ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
33591da177e4SLinus Torvalds 	struct rt6_info *rt;
3360ab364a6fSThomas Graf 	struct sk_buff *skb;
3361ab364a6fSThomas Graf 	struct rtmsg *rtm;
33624c9483b2SDavid S. Miller 	struct flowi6 fl6;
336372331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3364ab364a6fSThomas Graf 
3365ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3366ab364a6fSThomas Graf 	if (err < 0)
3367ab364a6fSThomas Graf 		goto errout;
3368ab364a6fSThomas Graf 
3369ab364a6fSThomas Graf 	err = -EINVAL;
33704c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
337138b7097bSHannes Frederic Sowa 	rtm = nlmsg_data(nlh);
337238b7097bSHannes Frederic Sowa 	fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
3373ab364a6fSThomas Graf 
3374ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3375ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3376ab364a6fSThomas Graf 			goto errout;
3377ab364a6fSThomas Graf 
33784e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3379ab364a6fSThomas Graf 	}
3380ab364a6fSThomas Graf 
3381ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3382ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3383ab364a6fSThomas Graf 			goto errout;
3384ab364a6fSThomas Graf 
33854e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3386ab364a6fSThomas Graf 	}
3387ab364a6fSThomas Graf 
3388ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3389ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3390ab364a6fSThomas Graf 
3391ab364a6fSThomas Graf 	if (tb[RTA_OIF])
339272331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3393ab364a6fSThomas Graf 
33942e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
33952e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
33962e47b291SLorenzo Colitti 
3397622ec2c9SLorenzo Colitti 	if (tb[RTA_UID])
3398622ec2c9SLorenzo Colitti 		fl6.flowi6_uid = make_kuid(current_user_ns(),
3399622ec2c9SLorenzo Colitti 					   nla_get_u32(tb[RTA_UID]));
3400622ec2c9SLorenzo Colitti 	else
3401622ec2c9SLorenzo Colitti 		fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
3402622ec2c9SLorenzo Colitti 
3403ab364a6fSThomas Graf 	if (iif) {
3404ab364a6fSThomas Graf 		struct net_device *dev;
340572331bc0SShmulik Ladkani 		int flags = 0;
340672331bc0SShmulik Ladkani 
34075578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3408ab364a6fSThomas Graf 		if (!dev) {
3409ab364a6fSThomas Graf 			err = -ENODEV;
3410ab364a6fSThomas Graf 			goto errout;
3411ab364a6fSThomas Graf 		}
341272331bc0SShmulik Ladkani 
341372331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
341472331bc0SShmulik Ladkani 
341572331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
341672331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
341772331bc0SShmulik Ladkani 
341872331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
341972331bc0SShmulik Ladkani 							       flags);
342072331bc0SShmulik Ladkani 	} else {
342172331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
342272331bc0SShmulik Ladkani 
342372331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3424ab364a6fSThomas Graf 	}
34251da177e4SLinus Torvalds 
34261da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
342738308473SDavid S. Miller 	if (!skb) {
342894e187c0SAmerigo Wang 		ip6_rt_put(rt);
3429ab364a6fSThomas Graf 		err = -ENOBUFS;
3430ab364a6fSThomas Graf 		goto errout;
3431ab364a6fSThomas Graf 	}
34321da177e4SLinus Torvalds 
3433d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
34341da177e4SLinus Torvalds 
34354c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
343615e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
3437f8cfe2ceSDavid Ahern 			    nlh->nlmsg_seq, 0);
34381da177e4SLinus Torvalds 	if (err < 0) {
3439ab364a6fSThomas Graf 		kfree_skb(skb);
3440ab364a6fSThomas Graf 		goto errout;
34411da177e4SLinus Torvalds 	}
34421da177e4SLinus Torvalds 
344315e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3444ab364a6fSThomas Graf errout:
34451da177e4SLinus Torvalds 	return err;
34461da177e4SLinus Torvalds }
34471da177e4SLinus Torvalds 
344837a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
344937a1d361SRoopa Prabhu 		     unsigned int nlm_flags)
34501da177e4SLinus Torvalds {
34511da177e4SLinus Torvalds 	struct sk_buff *skb;
34525578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3453528c4cebSDenis V. Lunev 	u32 seq;
3454528c4cebSDenis V. Lunev 	int err;
34550d51aa80SJamal Hadi Salim 
3456528c4cebSDenis V. Lunev 	err = -ENOBUFS;
345738308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
345886872cb5SThomas Graf 
345919e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
346038308473SDavid S. Miller 	if (!skb)
346121713ebcSThomas Graf 		goto errout;
34621da177e4SLinus Torvalds 
3463191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
3464f8cfe2ceSDavid Ahern 				event, info->portid, seq, nlm_flags);
346526932566SPatrick McHardy 	if (err < 0) {
346626932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
346726932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
346826932566SPatrick McHardy 		kfree_skb(skb);
346926932566SPatrick McHardy 		goto errout;
347026932566SPatrick McHardy 	}
347115e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
34725578689aSDaniel Lezcano 		    info->nlh, gfp_any());
34731ce85fe4SPablo Neira Ayuso 	return;
347421713ebcSThomas Graf errout:
347521713ebcSThomas Graf 	if (err < 0)
34765578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
34771da177e4SLinus Torvalds }
34781da177e4SLinus Torvalds 
34798ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3480351638e7SJiri Pirko 				unsigned long event, void *ptr)
34818ed67789SDaniel Lezcano {
3482351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3483c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
34848ed67789SDaniel Lezcano 
34858ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3486d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
34878ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
34888ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3489d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
34908ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3491d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
34928ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
34938ed67789SDaniel Lezcano #endif
34948ed67789SDaniel Lezcano 	}
34958ed67789SDaniel Lezcano 
34968ed67789SDaniel Lezcano 	return NOTIFY_OK;
34978ed67789SDaniel Lezcano }
34988ed67789SDaniel Lezcano 
34991da177e4SLinus Torvalds /*
35001da177e4SLinus Torvalds  *	/proc
35011da177e4SLinus Torvalds  */
35021da177e4SLinus Torvalds 
35031da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
35041da177e4SLinus Torvalds 
350533120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
350633120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
350733120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
350833120b30SAlexey Dobriyan 	.read		= seq_read,
350933120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
35108d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
351133120b30SAlexey Dobriyan };
351233120b30SAlexey Dobriyan 
35131da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
35141da177e4SLinus Torvalds {
351569ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
35161da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
351769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
351869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
351969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
352069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
352169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3522fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
352369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
35241da177e4SLinus Torvalds 
35251da177e4SLinus Torvalds 	return 0;
35261da177e4SLinus Torvalds }
35271da177e4SLinus Torvalds 
35281da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
35291da177e4SLinus Torvalds {
3530de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
353169ddb805SDaniel Lezcano }
353269ddb805SDaniel Lezcano 
35339a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
35341da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
35351da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
35361da177e4SLinus Torvalds 	.read	 = seq_read,
35371da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3538b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
35391da177e4SLinus Torvalds };
35401da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
35411da177e4SLinus Torvalds 
35421da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
35431da177e4SLinus Torvalds 
35441da177e4SLinus Torvalds static
3545fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
35461da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
35471da177e4SLinus Torvalds {
3548c486da34SLucian Adrian Grijincu 	struct net *net;
3549c486da34SLucian Adrian Grijincu 	int delay;
3550c486da34SLucian Adrian Grijincu 	if (!write)
3551c486da34SLucian Adrian Grijincu 		return -EINVAL;
3552c486da34SLucian Adrian Grijincu 
3553c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3554c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
35558d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
35562ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
35571da177e4SLinus Torvalds 	return 0;
35581da177e4SLinus Torvalds }
35591da177e4SLinus Torvalds 
3560fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
35611da177e4SLinus Torvalds 	{
35621da177e4SLinus Torvalds 		.procname	=	"flush",
35634990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
35641da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
356589c8b3a1SDave Jones 		.mode		=	0200,
35666d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
35671da177e4SLinus Torvalds 	},
35681da177e4SLinus Torvalds 	{
35691da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
35709a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
35711da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35721da177e4SLinus Torvalds 		.mode		=	0644,
35736d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
35741da177e4SLinus Torvalds 	},
35751da177e4SLinus Torvalds 	{
35761da177e4SLinus Torvalds 		.procname	=	"max_size",
35774990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
35781da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35791da177e4SLinus Torvalds 		.mode		=	0644,
35806d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
35811da177e4SLinus Torvalds 	},
35821da177e4SLinus Torvalds 	{
35831da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
35844990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
35851da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35861da177e4SLinus Torvalds 		.mode		=	0644,
35876d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
35881da177e4SLinus Torvalds 	},
35891da177e4SLinus Torvalds 	{
35901da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
35914990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
35921da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35931da177e4SLinus Torvalds 		.mode		=	0644,
35946d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
35951da177e4SLinus Torvalds 	},
35961da177e4SLinus Torvalds 	{
35971da177e4SLinus Torvalds 		.procname	=	"gc_interval",
35984990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
35991da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
36001da177e4SLinus Torvalds 		.mode		=	0644,
36016d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
36021da177e4SLinus Torvalds 	},
36031da177e4SLinus Torvalds 	{
36041da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
36054990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
36061da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
36071da177e4SLinus Torvalds 		.mode		=	0644,
3608f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
36091da177e4SLinus Torvalds 	},
36101da177e4SLinus Torvalds 	{
36111da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
36124990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
36131da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
36141da177e4SLinus Torvalds 		.mode		=	0644,
36156d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
36161da177e4SLinus Torvalds 	},
36171da177e4SLinus Torvalds 	{
36181da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
36194990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
36201da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
36211da177e4SLinus Torvalds 		.mode		=	0644,
3622f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
36231da177e4SLinus Torvalds 	},
36241da177e4SLinus Torvalds 	{
36251da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
36264990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
36271da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
36281da177e4SLinus Torvalds 		.mode		=	0644,
36296d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
36301da177e4SLinus Torvalds 	},
3631f8572d8fSEric W. Biederman 	{ }
36321da177e4SLinus Torvalds };
36331da177e4SLinus Torvalds 
36342c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3635760f2d01SDaniel Lezcano {
3636760f2d01SDaniel Lezcano 	struct ctl_table *table;
3637760f2d01SDaniel Lezcano 
3638760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3639760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3640760f2d01SDaniel Lezcano 			GFP_KERNEL);
36415ee09105SYOSHIFUJI Hideaki 
36425ee09105SYOSHIFUJI Hideaki 	if (table) {
36435ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3644c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
364586393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
36465ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
36475ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
36485ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
36495ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
36505ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
36515ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
36525ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
36539c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3654464dc801SEric W. Biederman 
3655464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3656464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3657464dc801SEric W. Biederman 			table[0].procname = NULL;
36585ee09105SYOSHIFUJI Hideaki 	}
36595ee09105SYOSHIFUJI Hideaki 
3660760f2d01SDaniel Lezcano 	return table;
3661760f2d01SDaniel Lezcano }
36621da177e4SLinus Torvalds #endif
36631da177e4SLinus Torvalds 
36642c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3665cdb18761SDaniel Lezcano {
3666633d424bSPavel Emelyanov 	int ret = -ENOMEM;
36678ed67789SDaniel Lezcano 
366886393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
366986393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3670f2fc6a54SBenjamin Thery 
3671fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3672fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3673fc66f95cSEric Dumazet 
36748ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
36758ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
36768ed67789SDaniel Lezcano 					   GFP_KERNEL);
36778ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3678fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3679d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
36808ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3681d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
368262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
368362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
36848ed67789SDaniel Lezcano 
36858ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
36868ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
36878ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
36888ed67789SDaniel Lezcano 					       GFP_KERNEL);
368968fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
369068fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3691d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
36928ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3693d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
369462fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
369562fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
36968ed67789SDaniel Lezcano 
36978ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
36988ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
36998ed67789SDaniel Lezcano 					       GFP_KERNEL);
370068fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
370168fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3702d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
37038ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3704d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
370562fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
370662fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
37078ed67789SDaniel Lezcano #endif
37088ed67789SDaniel Lezcano 
3709b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3710b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3711b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3712b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3713b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3714b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3715b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3716b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3717b339a47cSPeter Zijlstra 
37186891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
37196891a346SBenjamin Thery 
37208ed67789SDaniel Lezcano 	ret = 0;
37218ed67789SDaniel Lezcano out:
37228ed67789SDaniel Lezcano 	return ret;
3723f2fc6a54SBenjamin Thery 
372468fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
372568fffc67SPeter Zijlstra out_ip6_prohibit_entry:
372668fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
372768fffc67SPeter Zijlstra out_ip6_null_entry:
372868fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
372968fffc67SPeter Zijlstra #endif
3730fc66f95cSEric Dumazet out_ip6_dst_entries:
3731fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3732f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3733f2fc6a54SBenjamin Thery 	goto out;
3734cdb18761SDaniel Lezcano }
3735cdb18761SDaniel Lezcano 
37362c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3737cdb18761SDaniel Lezcano {
37388ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
37398ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
37408ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
37418ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
37428ed67789SDaniel Lezcano #endif
374341bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3744cdb18761SDaniel Lezcano }
3745cdb18761SDaniel Lezcano 
3746d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3747d189634eSThomas Graf {
3748d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3749d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3750d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3751d189634eSThomas Graf #endif
3752d189634eSThomas Graf 	return 0;
3753d189634eSThomas Graf }
3754d189634eSThomas Graf 
3755d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3756d189634eSThomas Graf {
3757d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3758ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3759ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3760d189634eSThomas Graf #endif
3761d189634eSThomas Graf }
3762d189634eSThomas Graf 
3763cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3764cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3765cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3766cdb18761SDaniel Lezcano };
3767cdb18761SDaniel Lezcano 
3768c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3769c3426b47SDavid S. Miller {
3770c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3771c3426b47SDavid S. Miller 
3772c3426b47SDavid S. Miller 	if (!bp)
3773c3426b47SDavid S. Miller 		return -ENOMEM;
3774c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3775c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3776c3426b47SDavid S. Miller 	return 0;
3777c3426b47SDavid S. Miller }
3778c3426b47SDavid S. Miller 
3779c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3780c3426b47SDavid S. Miller {
3781c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3782c3426b47SDavid S. Miller 
3783c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
378456a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3785c3426b47SDavid S. Miller 	kfree(bp);
3786c3426b47SDavid S. Miller }
3787c3426b47SDavid S. Miller 
37882b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3789c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3790c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3791c3426b47SDavid S. Miller };
3792c3426b47SDavid S. Miller 
3793d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3794d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3795d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3796d189634eSThomas Graf };
3797d189634eSThomas Graf 
37988ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
37998ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
38008ed67789SDaniel Lezcano 	.priority = 0,
38018ed67789SDaniel Lezcano };
38028ed67789SDaniel Lezcano 
3803433d49c3SDaniel Lezcano int __init ip6_route_init(void)
38041da177e4SLinus Torvalds {
3805433d49c3SDaniel Lezcano 	int ret;
38068d0b94afSMartin KaFai Lau 	int cpu;
3807433d49c3SDaniel Lezcano 
38089a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
38099a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
38109a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
38119a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
38129a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3813c19a28e1SFernando Carrijo 		goto out;
381414e50e57SDavid S. Miller 
3815fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
38168ed67789SDaniel Lezcano 	if (ret)
3817bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3818bdb3289fSDaniel Lezcano 
3819c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3820c3426b47SDavid S. Miller 	if (ret)
3821e8803b6cSDavid S. Miller 		goto out_dst_entries;
38222a0c451aSThomas Graf 
38237e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
38247e52b33bSDavid S. Miller 	if (ret)
38257e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3826c3426b47SDavid S. Miller 
38275dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
38285dc121e9SArnaud Ebalard 
38298ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
38308ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
38318ed67789SDaniel Lezcano 	 * manually for init_net */
3832d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
38338ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3834bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3835d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
38368ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3837d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
38388ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3839bdb3289fSDaniel Lezcano   #endif
3840e8803b6cSDavid S. Miller 	ret = fib6_init();
3841433d49c3SDaniel Lezcano 	if (ret)
38428ed67789SDaniel Lezcano 		goto out_register_subsys;
3843433d49c3SDaniel Lezcano 
3844433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3845433d49c3SDaniel Lezcano 	if (ret)
3846e8803b6cSDavid S. Miller 		goto out_fib6_init;
3847c35b7e72SDaniel Lezcano 
3848433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3849433d49c3SDaniel Lezcano 	if (ret)
3850433d49c3SDaniel Lezcano 		goto xfrm6_init;
38517e5449c2SDaniel Lezcano 
3852d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3853d189634eSThomas Graf 	if (ret)
3854d189634eSThomas Graf 		goto fib6_rules_init;
3855d189634eSThomas Graf 
3856433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3857c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3858c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3859c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3860d189634eSThomas Graf 		goto out_register_late_subsys;
3861433d49c3SDaniel Lezcano 
38628ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3863cdb18761SDaniel Lezcano 	if (ret)
3864d189634eSThomas Graf 		goto out_register_late_subsys;
38658ed67789SDaniel Lezcano 
38668d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
38678d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
38688d0b94afSMartin KaFai Lau 
38698d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
38708d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
38718d0b94afSMartin KaFai Lau 	}
38728d0b94afSMartin KaFai Lau 
3873433d49c3SDaniel Lezcano out:
3874433d49c3SDaniel Lezcano 	return ret;
3875433d49c3SDaniel Lezcano 
3876d189634eSThomas Graf out_register_late_subsys:
3877d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3878433d49c3SDaniel Lezcano fib6_rules_init:
3879433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3880433d49c3SDaniel Lezcano xfrm6_init:
3881433d49c3SDaniel Lezcano 	xfrm6_fini();
38822a0c451aSThomas Graf out_fib6_init:
38832a0c451aSThomas Graf 	fib6_gc_cleanup();
38848ed67789SDaniel Lezcano out_register_subsys:
38858ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
38867e52b33bSDavid S. Miller out_register_inetpeer:
38877e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3888fc66f95cSEric Dumazet out_dst_entries:
3889fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3890433d49c3SDaniel Lezcano out_kmem_cache:
3891f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3892433d49c3SDaniel Lezcano 	goto out;
38931da177e4SLinus Torvalds }
38941da177e4SLinus Torvalds 
38951da177e4SLinus Torvalds void ip6_route_cleanup(void)
38961da177e4SLinus Torvalds {
38978ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3898d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3899101367c2SThomas Graf 	fib6_rules_cleanup();
39001da177e4SLinus Torvalds 	xfrm6_fini();
39011da177e4SLinus Torvalds 	fib6_gc_cleanup();
3902c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
39038ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
390441bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3905f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
39061da177e4SLinus Torvalds }
3907