xref: /openbmc/linux/net/ipv6/route.c (revision 2536862311d2276454ddef9dc36d6551a4b400fd)
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>
571da177e4SLinus Torvalds #include <net/xfrm.h>
588d71740cSTom Tucker #include <net/netevent.h>
5921713ebcSThomas Graf #include <net/netlink.h>
6051ebd318SNicolas Dichtel #include <net/nexthop.h>
6119e42e45SRoopa Prabhu #include <net/lwtunnel.h>
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds #include <asm/uaccess.h>
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
661da177e4SLinus Torvalds #include <linux/sysctl.h>
671da177e4SLinus Torvalds #endif
681da177e4SLinus Torvalds 
69afc154e9SHannes Frederic Sowa enum rt6_nud_state {
707e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
717e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
727e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
73afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
74afc154e9SHannes Frederic Sowa };
75afc154e9SHannes Frederic Sowa 
7683a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort);
771da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
780dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
79ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
801da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
811da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
821da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
831da177e4SLinus Torvalds 				       struct net_device *dev, int how);
84569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
87aad88724SEric Dumazet static int		ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb);
887150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
89aad88724SEric Dumazet static int		ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb);
901da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
916700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
926700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
936700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
946700c270SDavid S. Miller 					struct sk_buff *skb);
954b32b5adSMartin KaFai Lau static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
9652bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
971da177e4SLinus Torvalds 
9870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
99efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
100b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
101b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
10295c96174SEric Dumazet 					   unsigned int pref);
103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
104b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
105b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
10670ceb4f5SYOSHIFUJI Hideaki #endif
10770ceb4f5SYOSHIFUJI Hideaki 
1088d0b94afSMartin KaFai Lau struct uncached_list {
1098d0b94afSMartin KaFai Lau 	spinlock_t		lock;
1108d0b94afSMartin KaFai Lau 	struct list_head	head;
1118d0b94afSMartin KaFai Lau };
1128d0b94afSMartin KaFai Lau 
1138d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1148d0b94afSMartin KaFai Lau 
1158d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt)
1168d0b94afSMartin KaFai Lau {
1178d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1188d0b94afSMartin KaFai Lau 
1198d0b94afSMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
1208d0b94afSMartin KaFai Lau 	rt->rt6i_uncached_list = ul;
1218d0b94afSMartin KaFai Lau 
1228d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
1238d0b94afSMartin KaFai Lau 	list_add_tail(&rt->rt6i_uncached, &ul->head);
1248d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
1258d0b94afSMartin KaFai Lau }
1268d0b94afSMartin KaFai Lau 
1278d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt)
1288d0b94afSMartin KaFai Lau {
1298d0b94afSMartin KaFai Lau 	if (!list_empty(&rt->rt6i_uncached)) {
1308d0b94afSMartin KaFai Lau 		struct uncached_list *ul = rt->rt6i_uncached_list;
1318d0b94afSMartin KaFai Lau 
1328d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1338d0b94afSMartin KaFai Lau 		list_del(&rt->rt6i_uncached);
1348d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1358d0b94afSMartin KaFai Lau 	}
1368d0b94afSMartin KaFai Lau }
1378d0b94afSMartin KaFai Lau 
1388d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
1398d0b94afSMartin KaFai Lau {
1408d0b94afSMartin KaFai Lau 	struct net_device *loopback_dev = net->loopback_dev;
1418d0b94afSMartin KaFai Lau 	int cpu;
1428d0b94afSMartin KaFai Lau 
1438d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
1448d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
1458d0b94afSMartin KaFai Lau 		struct rt6_info *rt;
1468d0b94afSMartin KaFai Lau 
1478d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1488d0b94afSMartin KaFai Lau 		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
1498d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
1508d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
1518d0b94afSMartin KaFai Lau 
1528d0b94afSMartin KaFai Lau 			if (rt_idev && (rt_idev->dev == dev || !dev) &&
1538d0b94afSMartin KaFai Lau 			    rt_idev->dev != loopback_dev) {
1548d0b94afSMartin KaFai Lau 				rt->rt6i_idev = in6_dev_get(loopback_dev);
1558d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
1568d0b94afSMartin KaFai Lau 			}
1578d0b94afSMartin KaFai Lau 
1588d0b94afSMartin KaFai Lau 			if (rt_dev && (rt_dev == dev || !dev) &&
1598d0b94afSMartin KaFai Lau 			    rt_dev != loopback_dev) {
1608d0b94afSMartin KaFai Lau 				rt->dst.dev = loopback_dev;
1618d0b94afSMartin KaFai Lau 				dev_hold(rt->dst.dev);
1628d0b94afSMartin KaFai Lau 				dev_put(rt_dev);
1638d0b94afSMartin KaFai Lau 			}
1648d0b94afSMartin KaFai Lau 		}
1658d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1668d0b94afSMartin KaFai Lau 	}
1678d0b94afSMartin KaFai Lau }
1688d0b94afSMartin KaFai Lau 
169d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
170d52d3997SMartin KaFai Lau {
171d52d3997SMartin KaFai Lau 	return dst_metrics_write_ptr(rt->dst.from);
172d52d3997SMartin KaFai Lau }
173d52d3997SMartin KaFai Lau 
17406582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
17506582540SDavid S. Miller {
17606582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
17706582540SDavid S. Miller 
178d52d3997SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU)
179d52d3997SMartin KaFai Lau 		return rt6_pcpu_cow_metrics(rt);
180d52d3997SMartin KaFai Lau 	else if (rt->rt6i_flags & RTF_CACHE)
1814b32b5adSMartin KaFai Lau 		return NULL;
1824b32b5adSMartin KaFai Lau 	else
1833b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
18406582540SDavid S. Miller }
18506582540SDavid S. Miller 
186f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
187f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
188f894cbf8SDavid S. Miller 					     const void *daddr)
18939232973SDavid S. Miller {
19039232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
19139232973SDavid S. Miller 
192a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
19339232973SDavid S. Miller 		return (const void *) p;
194f894cbf8SDavid S. Miller 	else if (skb)
195f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
19639232973SDavid S. Miller 	return daddr;
19739232973SDavid S. Miller }
19839232973SDavid S. Miller 
199f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
200f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
201f894cbf8SDavid S. Miller 					  const void *daddr)
202d3aaeb38SDavid S. Miller {
20339232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
20439232973SDavid S. Miller 	struct neighbour *n;
20539232973SDavid S. Miller 
206f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
2078e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
208f83c7790SDavid S. Miller 	if (n)
209f83c7790SDavid S. Miller 		return n;
210f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
211f83c7790SDavid S. Miller }
212f83c7790SDavid S. Miller 
2139a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2141da177e4SLinus Torvalds 	.family			=	AF_INET6,
2151da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2161da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2171da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2180dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
219ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
22006582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
2211da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2221da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2231da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2241da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2251da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2266e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2271ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
228d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
2291da177e4SLinus Torvalds };
2301da177e4SLinus Torvalds 
231ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
232ec831ea7SRoland Dreier {
233618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
234618f9bc7SSteffen Klassert 
235618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
236ec831ea7SRoland Dreier }
237ec831ea7SRoland Dreier 
2386700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2396700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
24014e50e57SDavid S. Miller {
24114e50e57SDavid S. Miller }
24214e50e57SDavid S. Miller 
2436700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2446700c270SDavid S. Miller 				      struct sk_buff *skb)
245b587ee3bSDavid S. Miller {
246b587ee3bSDavid S. Miller }
247b587ee3bSDavid S. Miller 
2480972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
2490972ddb2SHeld Bernhard 					 unsigned long old)
2500972ddb2SHeld Bernhard {
2510972ddb2SHeld Bernhard 	return NULL;
2520972ddb2SHeld Bernhard }
2530972ddb2SHeld Bernhard 
25414e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
25514e50e57SDavid S. Miller 	.family			=	AF_INET6,
25614e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
25714e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
258ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
259214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
26014e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
261b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2620972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
263d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
26414e50e57SDavid S. Miller };
26514e50e57SDavid S. Miller 
26662fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
26714edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
26862fa8a84SDavid S. Miller };
26962fa8a84SDavid S. Miller 
270fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2711da177e4SLinus Torvalds 	.dst = {
2721da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2731da177e4SLinus Torvalds 		.__use		= 1,
2742c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2751da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2761da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2771da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2781da177e4SLinus Torvalds 	},
2791da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2804f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2811da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2821da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2831da177e4SLinus Torvalds };
2841da177e4SLinus Torvalds 
285101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
286101367c2SThomas Graf 
287fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
288101367c2SThomas Graf 	.dst = {
289101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
290101367c2SThomas Graf 		.__use		= 1,
2912c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
292101367c2SThomas Graf 		.error		= -EACCES,
2939ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2949ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
295101367c2SThomas Graf 	},
296101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2974f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
298101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
299101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
300101367c2SThomas Graf };
301101367c2SThomas Graf 
302fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
303101367c2SThomas Graf 	.dst = {
304101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
305101367c2SThomas Graf 		.__use		= 1,
3062c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
307101367c2SThomas Graf 		.error		= -EINVAL,
308352e512cSHerbert Xu 		.input		= dst_discard,
309aad88724SEric Dumazet 		.output		= dst_discard_sk,
310101367c2SThomas Graf 	},
311101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3124f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
313101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
314101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
315101367c2SThomas Graf };
316101367c2SThomas Graf 
317101367c2SThomas Graf #endif
318101367c2SThomas Graf 
3191da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
320d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net,
321957c665fSDavid S. Miller 					struct net_device *dev,
3228b96d22dSDavid S. Miller 					int flags,
3238b96d22dSDavid S. Miller 					struct fib6_table *table)
3241da177e4SLinus Torvalds {
32597bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3266f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
327cf911662SDavid S. Miller 
32897bab73fSDavid S. Miller 	if (rt) {
3298104891bSSteffen Klassert 		struct dst_entry *dst = &rt->dst;
3308104891bSSteffen Klassert 
3318104891bSSteffen Klassert 		memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
33251ebd318SNicolas Dichtel 		INIT_LIST_HEAD(&rt->rt6i_siblings);
3338d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&rt->rt6i_uncached);
33497bab73fSDavid S. Miller 	}
335cf911662SDavid S. Miller 	return rt;
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
338d52d3997SMartin KaFai Lau static struct rt6_info *ip6_dst_alloc(struct net *net,
339d52d3997SMartin KaFai Lau 				      struct net_device *dev,
340d52d3997SMartin KaFai Lau 				      int flags,
341d52d3997SMartin KaFai Lau 				      struct fib6_table *table)
342d52d3997SMartin KaFai Lau {
343d52d3997SMartin KaFai Lau 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags, table);
344d52d3997SMartin KaFai Lau 
345d52d3997SMartin KaFai Lau 	if (rt) {
346d52d3997SMartin KaFai Lau 		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
347d52d3997SMartin KaFai Lau 		if (rt->rt6i_pcpu) {
348d52d3997SMartin KaFai Lau 			int cpu;
349d52d3997SMartin KaFai Lau 
350d52d3997SMartin KaFai Lau 			for_each_possible_cpu(cpu) {
351d52d3997SMartin KaFai Lau 				struct rt6_info **p;
352d52d3997SMartin KaFai Lau 
353d52d3997SMartin KaFai Lau 				p = per_cpu_ptr(rt->rt6i_pcpu, cpu);
354d52d3997SMartin KaFai Lau 				/* no one shares rt */
355d52d3997SMartin KaFai Lau 				*p =  NULL;
356d52d3997SMartin KaFai Lau 			}
357d52d3997SMartin KaFai Lau 		} else {
358d52d3997SMartin KaFai Lau 			dst_destroy((struct dst_entry *)rt);
359d52d3997SMartin KaFai Lau 			return NULL;
360d52d3997SMartin KaFai Lau 		}
361d52d3997SMartin KaFai Lau 	}
362d52d3997SMartin KaFai Lau 
363d52d3997SMartin KaFai Lau 	return rt;
364d52d3997SMartin KaFai Lau }
365d52d3997SMartin KaFai Lau 
3661da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
369ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
3708d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3711da177e4SLinus Torvalds 
3728e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
373d52d3997SMartin KaFai Lau 	free_percpu(rt->rt6i_pcpu);
3748d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
3758d0b94afSMartin KaFai Lau 
3768d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
37738308473SDavid S. Miller 	if (idev) {
3781da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3791da177e4SLinus Torvalds 		in6_dev_put(idev);
3801da177e4SLinus Torvalds 	}
3811716a961SGao feng 
382ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
383ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
384b3419363SDavid S. Miller }
385b3419363SDavid S. Miller 
3861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3871da177e4SLinus Torvalds 			   int how)
3881da177e4SLinus Torvalds {
3891da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3901da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3915a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
392c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3931da177e4SLinus Torvalds 
39497cac082SDavid S. Miller 	if (dev != loopback_dev) {
39597cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3965a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3975a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
39838308473SDavid S. Miller 			if (loopback_idev) {
3991da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
4001da177e4SLinus Torvalds 				in6_dev_put(idev);
4011da177e4SLinus Torvalds 			}
4021da177e4SLinus Torvalds 		}
40397cac082SDavid S. Miller 	}
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds 
406a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4071da177e4SLinus Torvalds {
4081716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4091716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
410a50feda5SEric Dumazet 			return true;
4111716a961SGao feng 	} else if (rt->dst.from) {
4123fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
4131716a961SGao feng 	}
414a50feda5SEric Dumazet 	return false;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
41751ebd318SNicolas Dichtel /* Multipath route selection:
41851ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
41951ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
42051ebd318SNicolas Dichtel  */
42151ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
42251ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
42351ebd318SNicolas Dichtel {
42451ebd318SNicolas Dichtel 	unsigned int val = fl6->flowi6_proto;
42551ebd318SNicolas Dichtel 
426c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->daddr);
427c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->saddr);
42851ebd318SNicolas Dichtel 
42951ebd318SNicolas Dichtel 	/* Work only if this not encapsulated */
43051ebd318SNicolas Dichtel 	switch (fl6->flowi6_proto) {
43151ebd318SNicolas Dichtel 	case IPPROTO_UDP:
43251ebd318SNicolas Dichtel 	case IPPROTO_TCP:
43351ebd318SNicolas Dichtel 	case IPPROTO_SCTP:
434b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_sport;
435b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_dport;
43651ebd318SNicolas Dichtel 		break;
43751ebd318SNicolas Dichtel 
43851ebd318SNicolas Dichtel 	case IPPROTO_ICMPV6:
439b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_type;
440b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_code;
44151ebd318SNicolas Dichtel 		break;
44251ebd318SNicolas Dichtel 	}
44351ebd318SNicolas Dichtel 	/* RFC6438 recommands to use flowlabel */
444b3ce5ae1SNicolas Dichtel 	val ^= (__force u32)fl6->flowlabel;
44551ebd318SNicolas Dichtel 
44651ebd318SNicolas Dichtel 	/* Perhaps, we need to tune, this function? */
44751ebd318SNicolas Dichtel 	val = val ^ (val >> 7) ^ (val >> 12);
44851ebd318SNicolas Dichtel 	return val % candidate_count;
44951ebd318SNicolas Dichtel }
45051ebd318SNicolas Dichtel 
45151ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
45252bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
45352bd4c0cSNicolas Dichtel 					     int strict)
45451ebd318SNicolas Dichtel {
45551ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
45651ebd318SNicolas Dichtel 	int route_choosen;
45751ebd318SNicolas Dichtel 
45851ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
45951ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
46051ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
46151ebd318SNicolas Dichtel 	 */
46251ebd318SNicolas Dichtel 	if (route_choosen)
46351ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
46451ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
46551ebd318SNicolas Dichtel 			route_choosen--;
46651ebd318SNicolas Dichtel 			if (route_choosen == 0) {
46752bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
46852bd4c0cSNicolas Dichtel 					break;
46951ebd318SNicolas Dichtel 				match = sibling;
47051ebd318SNicolas Dichtel 				break;
47151ebd318SNicolas Dichtel 			}
47251ebd318SNicolas Dichtel 		}
47351ebd318SNicolas Dichtel 	return match;
47451ebd318SNicolas Dichtel }
47551ebd318SNicolas Dichtel 
4761da177e4SLinus Torvalds /*
477c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4781da177e4SLinus Torvalds  */
4791da177e4SLinus Torvalds 
4808ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4818ed67789SDaniel Lezcano 						    struct rt6_info *rt,
482b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4831da177e4SLinus Torvalds 						    int oif,
484d420895eSYOSHIFUJI Hideaki 						    int flags)
4851da177e4SLinus Torvalds {
4861da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4871da177e4SLinus Torvalds 	struct rt6_info *sprt;
4881da177e4SLinus Torvalds 
489dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
490dd3abc4eSYOSHIFUJI Hideaki 		goto out;
491dd3abc4eSYOSHIFUJI Hideaki 
492d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
493d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
494dd3abc4eSYOSHIFUJI Hideaki 
495dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4961da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4971da177e4SLinus Torvalds 				return sprt;
4981da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
49938308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
5001da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
501d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
5021da177e4SLinus Torvalds 						continue;
5031da177e4SLinus Torvalds 					if (local && (!oif ||
5041da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
5051da177e4SLinus Torvalds 						continue;
5061da177e4SLinus Torvalds 				}
5071da177e4SLinus Torvalds 				local = sprt;
5081da177e4SLinus Torvalds 			}
509dd3abc4eSYOSHIFUJI Hideaki 		} else {
510dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
511dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
512dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
513dd3abc4eSYOSHIFUJI Hideaki 		}
5141da177e4SLinus Torvalds 	}
5151da177e4SLinus Torvalds 
516dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
5171da177e4SLinus Torvalds 		if (local)
5181da177e4SLinus Torvalds 			return local;
5191da177e4SLinus Torvalds 
520d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
5218ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
5221da177e4SLinus Torvalds 	}
523dd3abc4eSYOSHIFUJI Hideaki out:
5241da177e4SLinus Torvalds 	return rt;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
52727097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
528c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
529c2f17e82SHannes Frederic Sowa 	struct work_struct work;
530c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
531c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
532c2f17e82SHannes Frederic Sowa };
533c2f17e82SHannes Frederic Sowa 
534c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
535c2f17e82SHannes Frederic Sowa {
536c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
537c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
538c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
539c2f17e82SHannes Frederic Sowa 
540c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
541c2f17e82SHannes Frederic Sowa 	ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
542c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
543662f5533SMichael Büsch 	kfree(work);
544c2f17e82SHannes Frederic Sowa }
545c2f17e82SHannes Frederic Sowa 
54627097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
54727097255SYOSHIFUJI Hideaki {
548990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
549f2c31e32SEric Dumazet 	struct neighbour *neigh;
55027097255SYOSHIFUJI Hideaki 	/*
55127097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
55227097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
55327097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
55427097255SYOSHIFUJI Hideaki 	 *
55527097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
55627097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
55727097255SYOSHIFUJI Hideaki 	 */
5582152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
559fdd6681dSAmerigo Wang 		return;
5602152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5612152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5622152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5638d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5648d6c31bfSMartin KaFai Lau 			goto out;
5658d6c31bfSMartin KaFai Lau 
566990edb42SMartin KaFai Lau 		work = NULL;
5672152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
568990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
569990edb42SMartin KaFai Lau 		    time_after(jiffies,
570990edb42SMartin KaFai Lau 			       neigh->updated +
571990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
572c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
573990edb42SMartin KaFai Lau 			if (work)
5747e980569SJiri Benc 				__neigh_set_probe_once(neigh);
575990edb42SMartin KaFai Lau 		}
576c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
577990edb42SMartin KaFai Lau 	} else {
578990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
579990edb42SMartin KaFai Lau 	}
580c2f17e82SHannes Frederic Sowa 
581c2f17e82SHannes Frederic Sowa 	if (work) {
582c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
583c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
584c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
585c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
586c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
587c2f17e82SHannes Frederic Sowa 	}
588990edb42SMartin KaFai Lau 
5898d6c31bfSMartin KaFai Lau out:
5902152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
591f2c31e32SEric Dumazet }
59227097255SYOSHIFUJI Hideaki #else
59327097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
59427097255SYOSHIFUJI Hideaki {
59527097255SYOSHIFUJI Hideaki }
59627097255SYOSHIFUJI Hideaki #endif
59727097255SYOSHIFUJI Hideaki 
5981da177e4SLinus Torvalds /*
599554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
6001da177e4SLinus Torvalds  */
601b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
6021da177e4SLinus Torvalds {
603d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
604161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
605554cfb7eSYOSHIFUJI Hideaki 		return 2;
606161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
607161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
608161980f4SDavid S. Miller 		return 1;
609554cfb7eSYOSHIFUJI Hideaki 	return 0;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds 
612afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
6131da177e4SLinus Torvalds {
614f2c31e32SEric Dumazet 	struct neighbour *neigh;
615afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
616f2c31e32SEric Dumazet 
6174d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
6184d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
619afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
620145a3621SYOSHIFUJI Hideaki / 吉藤英明 
621145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
622145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
623145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
624145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
625554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
626afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
627398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
628a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
629afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6307e980569SJiri Benc 		else
6317e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
632398bcbebSYOSHIFUJI Hideaki #endif
633145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
634afc154e9SHannes Frederic Sowa 	} else {
635afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6367e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
637a5a81f0bSPaul Marks 	}
638145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
639145a3621SYOSHIFUJI Hideaki / 吉藤英明 
640a5a81f0bSPaul Marks 	return ret;
6411da177e4SLinus Torvalds }
6421da177e4SLinus Torvalds 
643554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
644554cfb7eSYOSHIFUJI Hideaki 			   int strict)
645554cfb7eSYOSHIFUJI Hideaki {
646a5a81f0bSPaul Marks 	int m;
6474d0c5911SYOSHIFUJI Hideaki 
6484d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
64977d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
650afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
651ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
652ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
653ebacaaa0SYOSHIFUJI Hideaki #endif
654afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
655afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
656afc154e9SHannes Frederic Sowa 		if (n < 0)
657afc154e9SHannes Frederic Sowa 			return n;
658afc154e9SHannes Frederic Sowa 	}
659554cfb7eSYOSHIFUJI Hideaki 	return m;
660554cfb7eSYOSHIFUJI Hideaki }
661554cfb7eSYOSHIFUJI Hideaki 
662f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
663afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
664afc154e9SHannes Frederic Sowa 				   bool *do_rr)
665554cfb7eSYOSHIFUJI Hideaki {
666554cfb7eSYOSHIFUJI Hideaki 	int m;
667afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
66835103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
66935103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
67035103d11SAndy Gospodarek 
67135103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
67235103d11SAndy Gospodarek 	    idev->cnf.ignore_routes_with_linkdown)
67335103d11SAndy Gospodarek 		goto out;
674554cfb7eSYOSHIFUJI Hideaki 
675554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
676f11e6659SDavid S. Miller 		goto out;
677554cfb7eSYOSHIFUJI Hideaki 
678554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6797e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
680afc154e9SHannes Frederic Sowa 		match_do_rr = true;
681afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6827e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
683f11e6659SDavid S. Miller 		goto out;
6841da177e4SLinus Torvalds 	}
685f11e6659SDavid S. Miller 
686afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
687afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
688afc154e9SHannes Frederic Sowa 
6897e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
690afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
691afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
692afc154e9SHannes Frederic Sowa 		*mpri = m;
693afc154e9SHannes Frederic Sowa 		match = rt;
694afc154e9SHannes Frederic Sowa 	}
695f11e6659SDavid S. Miller out:
696f11e6659SDavid S. Miller 	return match;
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds 
699f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
700f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
701afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
702afc154e9SHannes Frederic Sowa 				     bool *do_rr)
703f11e6659SDavid S. Miller {
7049fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
705f11e6659SDavid S. Miller 	int mpri = -1;
706f11e6659SDavid S. Miller 
707f11e6659SDavid S. Miller 	match = NULL;
7089fbdcfafSSteffen Klassert 	cont = NULL;
7099fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
7109fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7119fbdcfafSSteffen Klassert 			cont = rt;
7129fbdcfafSSteffen Klassert 			break;
7139fbdcfafSSteffen Klassert 		}
7149fbdcfafSSteffen Klassert 
715afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7169fbdcfafSSteffen Klassert 	}
7179fbdcfafSSteffen Klassert 
7189fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
7199fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7209fbdcfafSSteffen Klassert 			cont = rt;
7219fbdcfafSSteffen Klassert 			break;
7229fbdcfafSSteffen Klassert 		}
7239fbdcfafSSteffen Klassert 
7249fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7259fbdcfafSSteffen Klassert 	}
7269fbdcfafSSteffen Klassert 
7279fbdcfafSSteffen Klassert 	if (match || !cont)
7289fbdcfafSSteffen Klassert 		return match;
7299fbdcfafSSteffen Klassert 
7309fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
731afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
732f11e6659SDavid S. Miller 
733f11e6659SDavid S. Miller 	return match;
734f11e6659SDavid S. Miller }
735f11e6659SDavid S. Miller 
736f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
737f11e6659SDavid S. Miller {
738f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7398ed67789SDaniel Lezcano 	struct net *net;
740afc154e9SHannes Frederic Sowa 	bool do_rr = false;
741f11e6659SDavid S. Miller 
742f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
743f11e6659SDavid S. Miller 	if (!rt0)
744f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
745f11e6659SDavid S. Miller 
746afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
747afc154e9SHannes Frederic Sowa 			     &do_rr);
748f11e6659SDavid S. Miller 
749afc154e9SHannes Frederic Sowa 	if (do_rr) {
750d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
751f11e6659SDavid S. Miller 
752554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
753f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
754f11e6659SDavid S. Miller 			next = fn->leaf;
755f11e6659SDavid S. Miller 
756f11e6659SDavid S. Miller 		if (next != rt0)
757f11e6659SDavid S. Miller 			fn->rr_ptr = next;
758554cfb7eSYOSHIFUJI Hideaki 	}
759554cfb7eSYOSHIFUJI Hideaki 
760d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
761a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds 
7648b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7658b9df265SMartin KaFai Lau {
7668b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7678b9df265SMartin KaFai Lau }
7688b9df265SMartin KaFai Lau 
76970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
77070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
771b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
77270ceb4f5SYOSHIFUJI Hideaki {
773c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
77470ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
77570ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
77670ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7774bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
77870ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
77970ceb4f5SYOSHIFUJI Hideaki 
78070ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
78170ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78270ceb4f5SYOSHIFUJI Hideaki 	}
78370ceb4f5SYOSHIFUJI Hideaki 
78470ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
78570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
78670ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78770ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
78870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
79070ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
79170ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
79270ceb4f5SYOSHIFUJI Hideaki 		}
79370ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
79470ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
79570ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
79670ceb4f5SYOSHIFUJI Hideaki 		}
79770ceb4f5SYOSHIFUJI Hideaki 	}
79870ceb4f5SYOSHIFUJI Hideaki 
79970ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
80070ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
8013933fc95SJens Rosenboom 		return -EINVAL;
80270ceb4f5SYOSHIFUJI Hideaki 
8034bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
80470ceb4f5SYOSHIFUJI Hideaki 
80570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
80670ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
80770ceb4f5SYOSHIFUJI Hideaki 	else {
80870ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
80970ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
81070ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
81170ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
81270ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
81370ceb4f5SYOSHIFUJI Hideaki 	}
81470ceb4f5SYOSHIFUJI Hideaki 
815f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
816f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
817f104a567SDuan Jiong 	else
818f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
819f104a567SDuan Jiong 					gwaddr, dev->ifindex);
82070ceb4f5SYOSHIFUJI Hideaki 
82170ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
822e0a1ad73SThomas Graf 		ip6_del_rt(rt);
82370ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
82470ceb4f5SYOSHIFUJI Hideaki 	}
82570ceb4f5SYOSHIFUJI Hideaki 
82670ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
827efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
82870ceb4f5SYOSHIFUJI Hideaki 					pref);
82970ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
83070ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
83170ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
83270ceb4f5SYOSHIFUJI Hideaki 
83370ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8341716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8351716a961SGao feng 			rt6_clean_expires(rt);
8361716a961SGao feng 		else
8371716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8381716a961SGao feng 
83994e187c0SAmerigo Wang 		ip6_rt_put(rt);
84070ceb4f5SYOSHIFUJI Hideaki 	}
84170ceb4f5SYOSHIFUJI Hideaki 	return 0;
84270ceb4f5SYOSHIFUJI Hideaki }
84370ceb4f5SYOSHIFUJI Hideaki #endif
84470ceb4f5SYOSHIFUJI Hideaki 
845a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
846a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
847a3c00e46SMartin KaFai Lau {
848a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
849a3c00e46SMartin KaFai Lau 	while (1) {
850a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
851a3c00e46SMartin KaFai Lau 			return NULL;
852a3c00e46SMartin KaFai Lau 		pn = fn->parent;
853a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
854a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
855a3c00e46SMartin KaFai Lau 		else
856a3c00e46SMartin KaFai Lau 			fn = pn;
857a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
858a3c00e46SMartin KaFai Lau 			return fn;
859a3c00e46SMartin KaFai Lau 	}
860a3c00e46SMartin KaFai Lau }
861c71099acSThomas Graf 
8628ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8638ed67789SDaniel Lezcano 					     struct fib6_table *table,
8644c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8651da177e4SLinus Torvalds {
8661da177e4SLinus Torvalds 	struct fib6_node *fn;
8671da177e4SLinus Torvalds 	struct rt6_info *rt;
8681da177e4SLinus Torvalds 
869c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8704c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
871c71099acSThomas Graf restart:
872c71099acSThomas Graf 	rt = fn->leaf;
8734c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
87451ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
87552bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
876a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
877a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
878a3c00e46SMartin KaFai Lau 		if (fn)
879a3c00e46SMartin KaFai Lau 			goto restart;
880a3c00e46SMartin KaFai Lau 	}
881d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
882c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8831da177e4SLinus Torvalds 	return rt;
884c71099acSThomas Graf 
885c71099acSThomas Graf }
886c71099acSThomas Graf 
887ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
888ea6e574eSFlorian Westphal 				    int flags)
889ea6e574eSFlorian Westphal {
890ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
891ea6e574eSFlorian Westphal }
892ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
893ea6e574eSFlorian Westphal 
8949acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8959acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
896c71099acSThomas Graf {
8974c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8984c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8994c9483b2SDavid S. Miller 		.daddr = *daddr,
900c71099acSThomas Graf 	};
901c71099acSThomas Graf 	struct dst_entry *dst;
90277d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
903c71099acSThomas Graf 
904adaa70bbSThomas Graf 	if (saddr) {
9054c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
906adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
907adaa70bbSThomas Graf 	}
908adaa70bbSThomas Graf 
9094c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
910c71099acSThomas Graf 	if (dst->error == 0)
911c71099acSThomas Graf 		return (struct rt6_info *) dst;
912c71099acSThomas Graf 
913c71099acSThomas Graf 	dst_release(dst);
914c71099acSThomas Graf 
9151da177e4SLinus Torvalds 	return NULL;
9161da177e4SLinus Torvalds }
9177159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
9187159039aSYOSHIFUJI Hideaki 
919c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
9201da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
9211da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
9221da177e4SLinus Torvalds    be destroyed.
9231da177e4SLinus Torvalds  */
9241da177e4SLinus Torvalds 
925e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
926e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9271da177e4SLinus Torvalds {
9281da177e4SLinus Torvalds 	int err;
929c71099acSThomas Graf 	struct fib6_table *table;
9301da177e4SLinus Torvalds 
931c71099acSThomas Graf 	table = rt->rt6i_table;
932c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
933e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
934c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	return err;
9371da177e4SLinus Torvalds }
9381da177e4SLinus Torvalds 
93940e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
94040e22e8fSThomas Graf {
941e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
942e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
943e715b6d3SFlorian Westphal 
944e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
94540e22e8fSThomas Graf }
94640e22e8fSThomas Graf 
9478b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
94821efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
949b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9501da177e4SLinus Torvalds {
9511da177e4SLinus Torvalds 	struct rt6_info *rt;
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	/*
9541da177e4SLinus Torvalds 	 *	Clone the route.
9551da177e4SLinus Torvalds 	 */
9561da177e4SLinus Torvalds 
957d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
95883a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9591da177e4SLinus Torvalds 
960d52d3997SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev,
96183a09abdSMartin KaFai Lau 			     0, ort->rt6i_table);
96283a09abdSMartin KaFai Lau 
96383a09abdSMartin KaFai Lau 	if (!rt)
96483a09abdSMartin KaFai Lau 		return NULL;
96583a09abdSMartin KaFai Lau 
96683a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9678b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
96883a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
96983a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
97083a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
97183a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9728b9df265SMartin KaFai Lau 
9738b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
974bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
97521efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
97658c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9771da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9781da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9794e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9801da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9811da177e4SLinus Torvalds 		}
9821da177e4SLinus Torvalds #endif
98395a9a5baSYOSHIFUJI Hideaki 	}
98495a9a5baSYOSHIFUJI Hideaki 
985299d9939SYOSHIFUJI Hideaki 	return rt;
986299d9939SYOSHIFUJI Hideaki }
987299d9939SYOSHIFUJI Hideaki 
988d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
989d52d3997SMartin KaFai Lau {
990d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
991d52d3997SMartin KaFai Lau 
992d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
993d52d3997SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags,
994d52d3997SMartin KaFai Lau 				  rt->rt6i_table);
995d52d3997SMartin KaFai Lau 
996d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
997d52d3997SMartin KaFai Lau 		return NULL;
998d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
999d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
1000d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
1001d52d3997SMartin KaFai Lau 	return pcpu_rt;
1002d52d3997SMartin KaFai Lau }
1003d52d3997SMartin KaFai Lau 
1004d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
1005d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
1006d52d3997SMartin KaFai Lau {
1007d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
1008d52d3997SMartin KaFai Lau 
1009d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
1010d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
1011d52d3997SMartin KaFai Lau 
1012d52d3997SMartin KaFai Lau 	if (pcpu_rt)
1013d52d3997SMartin KaFai Lau 		goto done;
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 
1019d52d3997SMartin KaFai Lau 		pcpu_rt = net->ipv6.ip6_null_entry;
1020d52d3997SMartin KaFai Lau 		goto done;
1021d52d3997SMartin KaFai Lau 	}
1022d52d3997SMartin KaFai Lau 
1023d52d3997SMartin KaFai Lau 	prev = cmpxchg(p, NULL, pcpu_rt);
1024d52d3997SMartin KaFai Lau 	if (prev) {
1025d52d3997SMartin KaFai Lau 		/* If someone did it before us, return prev instead */
1026d52d3997SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
1027d52d3997SMartin KaFai Lau 		pcpu_rt = prev;
1028d52d3997SMartin KaFai Lau 	}
1029d52d3997SMartin KaFai Lau 
1030d52d3997SMartin KaFai Lau done:
1031d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1032d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
1033d52d3997SMartin KaFai Lau 	return pcpu_rt;
1034d52d3997SMartin KaFai Lau }
1035d52d3997SMartin KaFai Lau 
10368ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
10374c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
10381da177e4SLinus Torvalds {
1039367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
104045e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1041c71099acSThomas Graf 	int strict = 0;
10421da177e4SLinus Torvalds 
104377d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1044367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1045367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10461da177e4SLinus Torvalds 
1047c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10481da177e4SLinus Torvalds 
10494c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1050367efcb9SMartin KaFai Lau 	saved_fn = fn;
10511da177e4SLinus Torvalds 
1052a3c00e46SMartin KaFai Lau redo_rt6_select:
1053367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
105452bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1055367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1056a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1057a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1058a3c00e46SMartin KaFai Lau 		if (fn)
1059a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1060367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1061367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1062367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1063367efcb9SMartin KaFai Lau 			fn = saved_fn;
1064367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1065367efcb9SMartin KaFai Lau 		}
1066a3c00e46SMartin KaFai Lau 	}
1067a3c00e46SMartin KaFai Lau 
1068d52d3997SMartin KaFai Lau 
1069d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10703da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1071c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10721da177e4SLinus Torvalds 
1073d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1074d52d3997SMartin KaFai Lau 		return rt;
10753da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10763da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10773da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10783da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10793da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10803da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
10813da59bd9SMartin KaFai Lau 		 */
1082c71099acSThomas Graf 
10833da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
10843da59bd9SMartin KaFai Lau 
1085d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1086d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1087d52d3997SMartin KaFai Lau 
10883da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
10893da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
10903da59bd9SMartin KaFai Lau 
10913da59bd9SMartin KaFai Lau 		if (uncached_rt)
10928d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
10933da59bd9SMartin KaFai Lau 		else
10943da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1095d52d3997SMartin KaFai Lau 
10963da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
10973da59bd9SMartin KaFai Lau 		return uncached_rt;
10983da59bd9SMartin KaFai Lau 
1099d52d3997SMartin KaFai Lau 	} else {
1100d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1101d52d3997SMartin KaFai Lau 
1102d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1103d52d3997SMartin KaFai Lau 
1104d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1105d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1106d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1107d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1108d52d3997SMartin KaFai Lau 
1109d52d3997SMartin KaFai Lau 		return pcpu_rt;
1110d52d3997SMartin KaFai Lau 	}
1111c71099acSThomas Graf }
1112c71099acSThomas Graf 
11138ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11144c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11154acad72dSPavel Emelyanov {
11164c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11174acad72dSPavel Emelyanov }
11184acad72dSPavel Emelyanov 
111972331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
112072331bc0SShmulik Ladkani 						struct net_device *dev,
112172331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
112272331bc0SShmulik Ladkani {
112372331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
112472331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
112572331bc0SShmulik Ladkani 
112672331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
112772331bc0SShmulik Ladkani }
112872331bc0SShmulik Ladkani 
1129c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1130c71099acSThomas Graf {
1131b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1132c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1133adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
11344c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
11354c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
11364c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11374c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11386502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11394c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11404c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1141c71099acSThomas Graf 	};
1142adaa70bbSThomas Graf 
114372331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1144c71099acSThomas Graf }
1145c71099acSThomas Graf 
11468ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11474c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1148c71099acSThomas Graf {
11494c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1150c71099acSThomas Graf }
1151c71099acSThomas Graf 
11529c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
11534c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1154c71099acSThomas Graf {
1155c71099acSThomas Graf 	int flags = 0;
1156c71099acSThomas Graf 
11571fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
11584dc27d1cSDavid McCullough 
11594c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
116077d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1161c71099acSThomas Graf 
11624c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
1163adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
11640c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
11650c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1166adaa70bbSThomas Graf 
11674c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
11681da177e4SLinus Torvalds }
11697159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
11701da177e4SLinus Torvalds 
11712774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
117214e50e57SDavid S. Miller {
11735c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
117414e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
117514e50e57SDavid S. Miller 
1176f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
117714e50e57SDavid S. Miller 	if (rt) {
1178d8d1f30bSChangli Gao 		new = &rt->dst;
117914e50e57SDavid S. Miller 
11808104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
11818104891bSSteffen Klassert 
118214e50e57SDavid S. Miller 		new->__use = 1;
1183352e512cSHerbert Xu 		new->input = dst_discard;
1184aad88724SEric Dumazet 		new->output = dst_discard_sk;
118514e50e57SDavid S. Miller 
118621efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
118721efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
118821efcfa0SEric Dumazet 		else
1189defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
119014e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
119114e50e57SDavid S. Miller 		if (rt->rt6i_idev)
119214e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
119314e50e57SDavid S. Miller 
11944e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
11951716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
119614e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
119714e50e57SDavid S. Miller 
119814e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
119914e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
120014e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
120114e50e57SDavid S. Miller #endif
120214e50e57SDavid S. Miller 
120314e50e57SDavid S. Miller 		dst_free(new);
120414e50e57SDavid S. Miller 	}
120514e50e57SDavid S. Miller 
120669ead7afSDavid S. Miller 	dst_release(dst_orig);
120769ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
120814e50e57SDavid S. Miller }
120914e50e57SDavid S. Miller 
12101da177e4SLinus Torvalds /*
12111da177e4SLinus Torvalds  *	Destination cache support functions
12121da177e4SLinus Torvalds  */
12131da177e4SLinus Torvalds 
12144b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12154b32b5adSMartin KaFai Lau {
12164b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12174b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12184b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12194b32b5adSMartin KaFai Lau }
12204b32b5adSMartin KaFai Lau 
12213da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12223da59bd9SMartin KaFai Lau {
12233da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12243da59bd9SMartin KaFai Lau 		return NULL;
12253da59bd9SMartin KaFai Lau 
12263da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12273da59bd9SMartin KaFai Lau 		return NULL;
12283da59bd9SMartin KaFai Lau 
12293da59bd9SMartin KaFai Lau 	return &rt->dst;
12303da59bd9SMartin KaFai Lau }
12313da59bd9SMartin KaFai Lau 
12323da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12333da59bd9SMartin KaFai Lau {
12343da59bd9SMartin KaFai Lau 	if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12353da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12363da59bd9SMartin KaFai Lau 		return &rt->dst;
12373da59bd9SMartin KaFai Lau 	else
12383da59bd9SMartin KaFai Lau 		return NULL;
12393da59bd9SMartin KaFai Lau }
12403da59bd9SMartin KaFai Lau 
12411da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12421da177e4SLinus Torvalds {
12431da177e4SLinus Torvalds 	struct rt6_info *rt;
12441da177e4SLinus Torvalds 
12451da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12461da177e4SLinus Torvalds 
12476f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12486f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12496f3118b5SNicolas Dichtel 	 * into this function always.
12506f3118b5SNicolas Dichtel 	 */
1251e3bc10bdSHannes Frederic Sowa 
12524b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
12534b32b5adSMartin KaFai Lau 
1254d52d3997SMartin KaFai Lau 	if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE))
12553da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
12563da59bd9SMartin KaFai Lau 	else
12573da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
12581da177e4SLinus Torvalds }
12591da177e4SLinus Torvalds 
12601da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
12611da177e4SLinus Torvalds {
12621da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
12631da177e4SLinus Torvalds 
12641da177e4SLinus Torvalds 	if (rt) {
126554c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
126654c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1267e0a1ad73SThomas Graf 				ip6_del_rt(rt);
126854c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
12691da177e4SLinus Torvalds 			}
127054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
127154c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
127254c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
127354c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
127454c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
127554c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
12761da177e4SLinus Torvalds }
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
12791da177e4SLinus Torvalds {
12801da177e4SLinus Torvalds 	struct rt6_info *rt;
12811da177e4SLinus Torvalds 
12823ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
12831da177e4SLinus Torvalds 
1284adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
12851da177e4SLinus Torvalds 	if (rt) {
12861eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
12871eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
12881eb4f758SHannes Frederic Sowa 			if (ip6_del_rt(rt))
12891eb4f758SHannes Frederic Sowa 				dst_free(&rt->dst);
12901eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
12911da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
12921da177e4SLinus Torvalds 		}
12931da177e4SLinus Torvalds 	}
12941eb4f758SHannes Frederic Sowa }
12951da177e4SLinus Torvalds 
129645e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
129745e4fd26SMartin KaFai Lau {
129845e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
129945e4fd26SMartin KaFai Lau 
130045e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
130145e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
130245e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
130345e4fd26SMartin KaFai Lau }
130445e4fd26SMartin KaFai Lau 
130545e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
130645e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13071da177e4SLinus Torvalds {
13081da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13091da177e4SLinus Torvalds 
131045e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
131145e4fd26SMartin KaFai Lau 		return;
131245e4fd26SMartin KaFai Lau 
131381aded24SDavid S. Miller 	dst_confirm(dst);
131445e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
131545e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
131645e4fd26SMartin KaFai Lau 		return;
131781aded24SDavid S. Miller 
131845e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_CACHE) {
131945e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
132045e4fd26SMartin KaFai Lau 	} else {
132145e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
132245e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13239d289715SHagen Paul Pfeifer 
132445e4fd26SMartin KaFai Lau 		if (iph) {
132545e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
132645e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
132745e4fd26SMartin KaFai Lau 		} else if (sk) {
132845e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
132945e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
133045e4fd26SMartin KaFai Lau 		} else {
133145e4fd26SMartin KaFai Lau 			return;
13321da177e4SLinus Torvalds 		}
133345e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
133445e4fd26SMartin KaFai Lau 		if (nrt6) {
133545e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
133645e4fd26SMartin KaFai Lau 
133745e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
133845e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
133945e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
134045e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
134145e4fd26SMartin KaFai Lau 			 */
134245e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
134345e4fd26SMartin KaFai Lau 		}
134445e4fd26SMartin KaFai Lau 	}
134545e4fd26SMartin KaFai Lau }
134645e4fd26SMartin KaFai Lau 
134745e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
134845e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
134945e4fd26SMartin KaFai Lau {
135045e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
13511da177e4SLinus Torvalds }
13521da177e4SLinus Torvalds 
135342ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
135442ae66c8SDavid S. Miller 		     int oif, u32 mark)
135581aded24SDavid S. Miller {
135681aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
135781aded24SDavid S. Miller 	struct dst_entry *dst;
135881aded24SDavid S. Miller 	struct flowi6 fl6;
135981aded24SDavid S. Miller 
136081aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
136181aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
13621b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
136381aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
136481aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
13656502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
136681aded24SDavid S. Miller 
136781aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
136881aded24SDavid S. Miller 	if (!dst->error)
136945e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
137081aded24SDavid S. Miller 	dst_release(dst);
137181aded24SDavid S. Miller }
137281aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
137381aded24SDavid S. Miller 
137481aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
137581aded24SDavid S. Miller {
137681aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
137781aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
137881aded24SDavid S. Miller }
137981aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
138081aded24SDavid S. Miller 
1381b55b76b2SDuan Jiong /* Handle redirects */
1382b55b76b2SDuan Jiong struct ip6rd_flowi {
1383b55b76b2SDuan Jiong 	struct flowi6 fl6;
1384b55b76b2SDuan Jiong 	struct in6_addr gateway;
1385b55b76b2SDuan Jiong };
1386b55b76b2SDuan Jiong 
1387b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1388b55b76b2SDuan Jiong 					     struct fib6_table *table,
1389b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1390b55b76b2SDuan Jiong 					     int flags)
1391b55b76b2SDuan Jiong {
1392b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1393b55b76b2SDuan Jiong 	struct rt6_info *rt;
1394b55b76b2SDuan Jiong 	struct fib6_node *fn;
1395b55b76b2SDuan Jiong 
1396b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1397b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1398b55b76b2SDuan Jiong 	 *
1399b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1400b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1401b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1402b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1403b55b76b2SDuan Jiong 	 * routes.
1404b55b76b2SDuan Jiong 	 */
1405b55b76b2SDuan Jiong 
1406b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1407b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1408b55b76b2SDuan Jiong restart:
1409b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1410b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1411b55b76b2SDuan Jiong 			continue;
1412b55b76b2SDuan Jiong 		if (rt->dst.error)
1413b55b76b2SDuan Jiong 			break;
1414b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1415b55b76b2SDuan Jiong 			continue;
1416b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1417b55b76b2SDuan Jiong 			continue;
1418b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1419b55b76b2SDuan Jiong 			continue;
1420b55b76b2SDuan Jiong 		break;
1421b55b76b2SDuan Jiong 	}
1422b55b76b2SDuan Jiong 
1423b55b76b2SDuan Jiong 	if (!rt)
1424b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1425b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1426b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1427b0a1ba59SMartin KaFai Lau 		goto out;
1428b0a1ba59SMartin KaFai Lau 	}
1429b0a1ba59SMartin KaFai Lau 
1430b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1431a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1432a3c00e46SMartin KaFai Lau 		if (fn)
1433a3c00e46SMartin KaFai Lau 			goto restart;
1434b55b76b2SDuan Jiong 	}
1435a3c00e46SMartin KaFai Lau 
1436b0a1ba59SMartin KaFai Lau out:
1437b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1438b55b76b2SDuan Jiong 
1439b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1440b55b76b2SDuan Jiong 
1441b55b76b2SDuan Jiong 	return rt;
1442b55b76b2SDuan Jiong };
1443b55b76b2SDuan Jiong 
1444b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1445b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1446b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1447b55b76b2SDuan Jiong {
1448b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1449b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1450b55b76b2SDuan Jiong 
1451b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1452b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1453b55b76b2SDuan Jiong 
1454b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1455b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1456b55b76b2SDuan Jiong }
1457b55b76b2SDuan Jiong 
14583a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
14593a5ad2eeSDavid S. Miller {
14603a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
14613a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
14623a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
14633a5ad2eeSDavid S. Miller 
14643a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1465e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
14663a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
14673a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
14683a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
14693a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
14706502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
14713a5ad2eeSDavid S. Miller 
1472b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
14736700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
14743a5ad2eeSDavid S. Miller 	dst_release(dst);
14753a5ad2eeSDavid S. Miller }
14763a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
14773a5ad2eeSDavid S. Miller 
1478c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1479c92a59ecSDuan Jiong 			    u32 mark)
1480c92a59ecSDuan Jiong {
1481c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1482c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1483c92a59ecSDuan Jiong 	struct dst_entry *dst;
1484c92a59ecSDuan Jiong 	struct flowi6 fl6;
1485c92a59ecSDuan Jiong 
1486c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1487e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1488c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1489c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1490c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1491c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1492c92a59ecSDuan Jiong 
1493b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1494c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1495c92a59ecSDuan Jiong 	dst_release(dst);
1496c92a59ecSDuan Jiong }
1497c92a59ecSDuan Jiong 
14983a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
14993a5ad2eeSDavid S. Miller {
15003a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
15013a5ad2eeSDavid S. Miller }
15023a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15033a5ad2eeSDavid S. Miller 
15040dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15051da177e4SLinus Torvalds {
15060dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15070dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15080dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15090dbaee3bSDavid S. Miller 
15101da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15111da177e4SLinus Torvalds 
15125578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15135578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15141da177e4SLinus Torvalds 
15151da177e4SLinus Torvalds 	/*
15161da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15171da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15181da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15191da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15201da177e4SLinus Torvalds 	 */
15211da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15221da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15231da177e4SLinus Torvalds 	return mtu;
15241da177e4SLinus Torvalds }
15251da177e4SLinus Torvalds 
1526ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1527d33e4553SDavid S. Miller {
15284b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
15294b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1530d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1531618f9bc7SSteffen Klassert 
1532618f9bc7SSteffen Klassert 	if (mtu)
153330f78d8eSEric Dumazet 		goto out;
1534618f9bc7SSteffen Klassert 
15354b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
15364b32b5adSMartin KaFai Lau 	if (mtu)
15374b32b5adSMartin KaFai Lau 		goto out;
15384b32b5adSMartin KaFai Lau 
1539618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1540d33e4553SDavid S. Miller 
1541d33e4553SDavid S. Miller 	rcu_read_lock();
1542d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1543d33e4553SDavid S. Miller 	if (idev)
1544d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1545d33e4553SDavid S. Miller 	rcu_read_unlock();
1546d33e4553SDavid S. Miller 
154730f78d8eSEric Dumazet out:
154830f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1549d33e4553SDavid S. Miller }
1550d33e4553SDavid S. Miller 
15513b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
15523b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
15535d0bbeebSThomas Graf 
15543b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
155587a11578SDavid S. Miller 				  struct flowi6 *fl6)
15561da177e4SLinus Torvalds {
155787a11578SDavid S. Miller 	struct dst_entry *dst;
15581da177e4SLinus Torvalds 	struct rt6_info *rt;
15591da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1560c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
15611da177e4SLinus Torvalds 
156238308473SDavid S. Miller 	if (unlikely(!idev))
1563122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
15641da177e4SLinus Torvalds 
15658b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, dev, 0, NULL);
156638308473SDavid S. Miller 	if (unlikely(!rt)) {
15671da177e4SLinus Torvalds 		in6_dev_put(idev);
156887a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
15691da177e4SLinus Torvalds 		goto out;
15701da177e4SLinus Torvalds 	}
15711da177e4SLinus Torvalds 
15728e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
15738e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1574d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1575550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
157687a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
15778e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
15788e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
157914edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
15801da177e4SLinus Torvalds 
15813b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1582d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1583d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
15843b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
15851da177e4SLinus Torvalds 
15865578689aSDaniel Lezcano 	fib6_force_start_gc(net);
15871da177e4SLinus Torvalds 
158887a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
158987a11578SDavid S. Miller 
15901da177e4SLinus Torvalds out:
159187a11578SDavid S. Miller 	return dst;
15921da177e4SLinus Torvalds }
15931da177e4SLinus Torvalds 
15943d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
15951da177e4SLinus Torvalds {
1596e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
15973d0f24a7SStephen Hemminger 	int more = 0;
15981da177e4SLinus Torvalds 
15993b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16003b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16015d0bbeebSThomas Graf 
16021da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16031da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16041da177e4SLinus Torvalds 			*pprev = dst->next;
16051da177e4SLinus Torvalds 			dst_free(dst);
16061da177e4SLinus Torvalds 		} else {
16071da177e4SLinus Torvalds 			pprev = &dst->next;
16083d0f24a7SStephen Hemminger 			++more;
16091da177e4SLinus Torvalds 		}
16101da177e4SLinus Torvalds 	}
16111da177e4SLinus Torvalds 
16123b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16135d0bbeebSThomas Graf 
16143d0f24a7SStephen Hemminger 	return more;
16151da177e4SLinus Torvalds }
16161da177e4SLinus Torvalds 
16171e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16181e493d19SDavid S. Miller 			    void *arg)
16191e493d19SDavid S. Miller {
16201e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16211e493d19SDavid S. Miller 
16221e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
16231e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
16241e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
16251e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
16261e493d19SDavid S. Miller 		if (func(rt, arg)) {
16271e493d19SDavid S. Miller 			*pprev = dst->next;
16281e493d19SDavid S. Miller 			dst_free(dst);
16291e493d19SDavid S. Miller 		} else {
16301e493d19SDavid S. Miller 			pprev = &dst->next;
16311e493d19SDavid S. Miller 		}
16321e493d19SDavid S. Miller 	}
16331e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
16341e493d19SDavid S. Miller }
16351e493d19SDavid S. Miller 
1636569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
16371da177e4SLinus Torvalds {
163886393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
16397019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
16407019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
16417019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
16427019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
16437019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1644fc66f95cSEric Dumazet 	int entries;
16451da177e4SLinus Torvalds 
1646fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
164749a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1648fc66f95cSEric Dumazet 	    entries <= rt_max_size)
16491da177e4SLinus Torvalds 		goto out;
16501da177e4SLinus Torvalds 
16516891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
165214956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1653fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1654fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
16557019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
16561da177e4SLinus Torvalds out:
16577019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1658fc66f95cSEric Dumazet 	return entries > rt_max_size;
16591da177e4SLinus Torvalds }
16601da177e4SLinus Torvalds 
1661e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1662e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1663e715b6d3SFlorian Westphal {
1664e715b6d3SFlorian Westphal 	struct nlattr *nla;
1665e715b6d3SFlorian Westphal 	int remaining;
1666e715b6d3SFlorian Westphal 	u32 *mp;
1667e715b6d3SFlorian Westphal 
166863159f29SIan Morris 	if (!cfg->fc_mx)
1669e715b6d3SFlorian Westphal 		return 0;
1670e715b6d3SFlorian Westphal 
1671e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1672e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1673e715b6d3SFlorian Westphal 		return -ENOMEM;
1674e715b6d3SFlorian Westphal 
1675e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1676e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1677e715b6d3SFlorian Westphal 
1678e715b6d3SFlorian Westphal 		if (type) {
1679ea697639SDaniel Borkmann 			u32 val;
1680ea697639SDaniel Borkmann 
1681e715b6d3SFlorian Westphal 			if (unlikely(type > RTAX_MAX))
1682e715b6d3SFlorian Westphal 				goto err;
1683ea697639SDaniel Borkmann 			if (type == RTAX_CC_ALGO) {
1684ea697639SDaniel Borkmann 				char tmp[TCP_CA_NAME_MAX];
1685e715b6d3SFlorian Westphal 
1686ea697639SDaniel Borkmann 				nla_strlcpy(tmp, nla, sizeof(tmp));
1687ea697639SDaniel Borkmann 				val = tcp_ca_get_key_by_name(tmp);
1688ea697639SDaniel Borkmann 				if (val == TCP_CA_UNSPEC)
1689ea697639SDaniel Borkmann 					goto err;
1690ea697639SDaniel Borkmann 			} else {
1691ea697639SDaniel Borkmann 				val = nla_get_u32(nla);
1692ea697639SDaniel Borkmann 			}
1693ea697639SDaniel Borkmann 
1694ea697639SDaniel Borkmann 			mp[type - 1] = val;
1695e715b6d3SFlorian Westphal 			__set_bit(type - 1, mxc->mx_valid);
1696e715b6d3SFlorian Westphal 		}
1697e715b6d3SFlorian Westphal 	}
1698e715b6d3SFlorian Westphal 
1699e715b6d3SFlorian Westphal 	mxc->mx = mp;
1700e715b6d3SFlorian Westphal 
1701e715b6d3SFlorian Westphal 	return 0;
1702e715b6d3SFlorian Westphal  err:
1703e715b6d3SFlorian Westphal 	kfree(mp);
1704e715b6d3SFlorian Westphal 	return -EINVAL;
1705e715b6d3SFlorian Westphal }
17061da177e4SLinus Torvalds 
170786872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
17081da177e4SLinus Torvalds {
17091da177e4SLinus Torvalds 	int err;
17105578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
17111da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
17121da177e4SLinus Torvalds 	struct net_device *dev = NULL;
17131da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1714c71099acSThomas Graf 	struct fib6_table *table;
1715e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
17161da177e4SLinus Torvalds 	int addr_type;
17171da177e4SLinus Torvalds 
171886872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
17191da177e4SLinus Torvalds 		return -EINVAL;
17201da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
172186872cb5SThomas Graf 	if (cfg->fc_src_len)
17221da177e4SLinus Torvalds 		return -EINVAL;
17231da177e4SLinus Torvalds #endif
172486872cb5SThomas Graf 	if (cfg->fc_ifindex) {
17251da177e4SLinus Torvalds 		err = -ENODEV;
17265578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
17271da177e4SLinus Torvalds 		if (!dev)
17281da177e4SLinus Torvalds 			goto out;
17291da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
17301da177e4SLinus Torvalds 		if (!idev)
17311da177e4SLinus Torvalds 			goto out;
17321da177e4SLinus Torvalds 	}
17331da177e4SLinus Torvalds 
173486872cb5SThomas Graf 	if (cfg->fc_metric == 0)
173586872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
17361da177e4SLinus Torvalds 
1737c71099acSThomas Graf 	err = -ENOBUFS;
173838308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1739d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1740d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
174138308473SDavid S. Miller 		if (!table) {
1742f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1743d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1744d71314b4SMatti Vaittinen 		}
1745d71314b4SMatti Vaittinen 	} else {
1746d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1747d71314b4SMatti Vaittinen 	}
174838308473SDavid S. Miller 
174938308473SDavid S. Miller 	if (!table)
1750c71099acSThomas Graf 		goto out;
1751c71099acSThomas Graf 
1752c88507fbSSabrina Dubroca 	rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table);
17531da177e4SLinus Torvalds 
175438308473SDavid S. Miller 	if (!rt) {
17551da177e4SLinus Torvalds 		err = -ENOMEM;
17561da177e4SLinus Torvalds 		goto out;
17571da177e4SLinus Torvalds 	}
17581da177e4SLinus Torvalds 
17591716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
17601716a961SGao feng 		rt6_set_expires(rt, jiffies +
17611716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
17621716a961SGao feng 	else
17631716a961SGao feng 		rt6_clean_expires(rt);
17641da177e4SLinus Torvalds 
176586872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
176686872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
176786872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
176886872cb5SThomas Graf 
176986872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
17701da177e4SLinus Torvalds 
17711da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1772d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1773ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1774ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
17751da177e4SLinus Torvalds 	else
1776d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
17771da177e4SLinus Torvalds 
1778d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
17791da177e4SLinus Torvalds 
178019e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
178119e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
178219e42e45SRoopa Prabhu 
178319e42e45SRoopa Prabhu 		err = lwtunnel_build_state(dev, cfg->fc_encap_type,
178419e42e45SRoopa Prabhu 					   cfg->fc_encap, &lwtstate);
178519e42e45SRoopa Prabhu 		if (err)
178619e42e45SRoopa Prabhu 			goto out;
17875a6228a0SNicolas Dichtel 		rt->rt6i_lwtstate = lwtstate_get(lwtstate);
1788*25368623STom Herbert 		if (lwtunnel_output_redirect(rt->rt6i_lwtstate)) {
1789*25368623STom Herbert 			rt->rt6i_lwtstate->orig_output = rt->dst.output;
179074a0f2feSRoopa Prabhu 			rt->dst.output = lwtunnel_output6;
179119e42e45SRoopa Prabhu 		}
1792*25368623STom Herbert 		if (lwtunnel_input_redirect(rt->rt6i_lwtstate)) {
1793*25368623STom Herbert 			rt->rt6i_lwtstate->orig_input = rt->dst.input;
1794*25368623STom Herbert 			rt->dst.input = lwtunnel_input6;
1795*25368623STom Herbert 		}
1796*25368623STom Herbert 	}
179719e42e45SRoopa Prabhu 
179886872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
179986872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1800afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
180111d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
18021da177e4SLinus Torvalds 
18031da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
180486872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
180586872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
18061da177e4SLinus Torvalds #endif
18071da177e4SLinus Torvalds 
180886872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
18091da177e4SLinus Torvalds 
18101da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
18111da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
18121da177e4SLinus Torvalds 	 */
181386872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
181438308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
181538308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
181638308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
18171da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
18185578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
18191da177e4SLinus Torvalds 			if (dev) {
18201da177e4SLinus Torvalds 				dev_put(dev);
18211da177e4SLinus Torvalds 				in6_dev_put(idev);
18221da177e4SLinus Torvalds 			}
18235578689aSDaniel Lezcano 			dev = net->loopback_dev;
18241da177e4SLinus Torvalds 			dev_hold(dev);
18251da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
18261da177e4SLinus Torvalds 			if (!idev) {
18271da177e4SLinus Torvalds 				err = -ENODEV;
18281da177e4SLinus Torvalds 				goto out;
18291da177e4SLinus Torvalds 			}
18301da177e4SLinus Torvalds 		}
18311da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1832ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1833ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1834ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1835aad88724SEric Dumazet 			rt->dst.output = dst_discard_sk;
18367150aedeSKamala R 			rt->dst.input = dst_discard;
1837ef2c7d7bSNicolas Dichtel 			break;
1838ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1839ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
18407150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
18417150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1842ef2c7d7bSNicolas Dichtel 			break;
1843b4949ab2SNicolas Dichtel 		case RTN_THROW:
1844ef2c7d7bSNicolas Dichtel 		default:
18457150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
18467150aedeSKamala R 					: -ENETUNREACH;
18477150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
18487150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1849ef2c7d7bSNicolas Dichtel 			break;
1850ef2c7d7bSNicolas Dichtel 		}
18511da177e4SLinus Torvalds 		goto install_route;
18521da177e4SLinus Torvalds 	}
18531da177e4SLinus Torvalds 
185486872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1855b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
18561da177e4SLinus Torvalds 		int gwa_type;
18571da177e4SLinus Torvalds 
185886872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1859330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
186048ed7b26SFlorian Westphal 
186148ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
186248ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
186348ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
186448ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
186548ed7b26SFlorian Westphal 		 */
186648ed7b26SFlorian Westphal 		err = -EINVAL;
1867330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1868330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1869330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
187048ed7b26SFlorian Westphal 			goto out;
187148ed7b26SFlorian Westphal 
18724e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
18731da177e4SLinus Torvalds 
18741da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
18751da177e4SLinus Torvalds 			struct rt6_info *grt;
18761da177e4SLinus Torvalds 
18771da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
18781da177e4SLinus Torvalds 			   addresses as nexthop address.
18791da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
18801da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
18811da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
18821da177e4SLinus Torvalds 			   some exceptions. --ANK
18831da177e4SLinus Torvalds 			 */
18841da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
18851da177e4SLinus Torvalds 				goto out;
18861da177e4SLinus Torvalds 
18875578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
18881da177e4SLinus Torvalds 
18891da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
189038308473SDavid S. Miller 			if (!grt)
18911da177e4SLinus Torvalds 				goto out;
18921da177e4SLinus Torvalds 			if (dev) {
1893d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
189494e187c0SAmerigo Wang 					ip6_rt_put(grt);
18951da177e4SLinus Torvalds 					goto out;
18961da177e4SLinus Torvalds 				}
18971da177e4SLinus Torvalds 			} else {
1898d1918542SDavid S. Miller 				dev = grt->dst.dev;
18991da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
19001da177e4SLinus Torvalds 				dev_hold(dev);
19011da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
19021da177e4SLinus Torvalds 			}
19031da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
19041da177e4SLinus Torvalds 				err = 0;
190594e187c0SAmerigo Wang 			ip6_rt_put(grt);
19061da177e4SLinus Torvalds 
19071da177e4SLinus Torvalds 			if (err)
19081da177e4SLinus Torvalds 				goto out;
19091da177e4SLinus Torvalds 		}
19101da177e4SLinus Torvalds 		err = -EINVAL;
191138308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
19121da177e4SLinus Torvalds 			goto out;
19131da177e4SLinus Torvalds 	}
19141da177e4SLinus Torvalds 
19151da177e4SLinus Torvalds 	err = -ENODEV;
191638308473SDavid S. Miller 	if (!dev)
19171da177e4SLinus Torvalds 		goto out;
19181da177e4SLinus Torvalds 
1919c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1920c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1921c3968a85SDaniel Walter 			err = -EINVAL;
1922c3968a85SDaniel Walter 			goto out;
1923c3968a85SDaniel Walter 		}
19244e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1925c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1926c3968a85SDaniel Walter 	} else
1927c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1928c3968a85SDaniel Walter 
192986872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
19301da177e4SLinus Torvalds 
19311da177e4SLinus Torvalds install_route:
1932d8d1f30bSChangli Gao 	rt->dst.dev = dev;
19331da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1934c71099acSThomas Graf 	rt->rt6i_table = table;
193563152fc0SDaniel Lezcano 
1936c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
193763152fc0SDaniel Lezcano 
1938e715b6d3SFlorian Westphal 	err = ip6_convert_metrics(&mxc, cfg);
1939e715b6d3SFlorian Westphal 	if (err)
1940e715b6d3SFlorian Westphal 		goto out;
19411da177e4SLinus Torvalds 
1942e715b6d3SFlorian Westphal 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
1943e715b6d3SFlorian Westphal 
1944e715b6d3SFlorian Westphal 	kfree(mxc.mx);
1945e715b6d3SFlorian Westphal 	return err;
19461da177e4SLinus Torvalds out:
19471da177e4SLinus Torvalds 	if (dev)
19481da177e4SLinus Torvalds 		dev_put(dev);
19491da177e4SLinus Torvalds 	if (idev)
19501da177e4SLinus Torvalds 		in6_dev_put(idev);
19511da177e4SLinus Torvalds 	if (rt)
1952d8d1f30bSChangli Gao 		dst_free(&rt->dst);
19531da177e4SLinus Torvalds 	return err;
19541da177e4SLinus Torvalds }
19551da177e4SLinus Torvalds 
195686872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
19571da177e4SLinus Torvalds {
19581da177e4SLinus Torvalds 	int err;
1959c71099acSThomas Graf 	struct fib6_table *table;
1960d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
19611da177e4SLinus Torvalds 
19626825a26cSGao feng 	if (rt == net->ipv6.ip6_null_entry) {
19636825a26cSGao feng 		err = -ENOENT;
19646825a26cSGao feng 		goto out;
19656825a26cSGao feng 	}
19666c813a72SPatrick McHardy 
1967c71099acSThomas Graf 	table = rt->rt6i_table;
1968c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
196986872cb5SThomas Graf 	err = fib6_del(rt, info);
1970c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
19711da177e4SLinus Torvalds 
19726825a26cSGao feng out:
197394e187c0SAmerigo Wang 	ip6_rt_put(rt);
19741da177e4SLinus Torvalds 	return err;
19751da177e4SLinus Torvalds }
19761da177e4SLinus Torvalds 
1977e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1978e0a1ad73SThomas Graf {
19794d1169c1SDenis V. Lunev 	struct nl_info info = {
1980d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
19814d1169c1SDenis V. Lunev 	};
1982528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1983e0a1ad73SThomas Graf }
1984e0a1ad73SThomas Graf 
198586872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
19861da177e4SLinus Torvalds {
1987c71099acSThomas Graf 	struct fib6_table *table;
19881da177e4SLinus Torvalds 	struct fib6_node *fn;
19891da177e4SLinus Torvalds 	struct rt6_info *rt;
19901da177e4SLinus Torvalds 	int err = -ESRCH;
19911da177e4SLinus Torvalds 
19925578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
199338308473SDavid S. Miller 	if (!table)
1994c71099acSThomas Graf 		return err;
19951da177e4SLinus Torvalds 
1996c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1997c71099acSThomas Graf 
1998c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
199986872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
200086872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
20011da177e4SLinus Torvalds 
20021da177e4SLinus Torvalds 	if (fn) {
2003d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
20041f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
20051f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
20061f56a01fSMartin KaFai Lau 				continue;
200786872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2008d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2009d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
20101da177e4SLinus Torvalds 				continue;
201186872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
201286872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
20131da177e4SLinus Torvalds 				continue;
201486872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
20151da177e4SLinus Torvalds 				continue;
2016d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2017c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
20181da177e4SLinus Torvalds 
201986872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
20201da177e4SLinus Torvalds 		}
20211da177e4SLinus Torvalds 	}
2022c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds 	return err;
20251da177e4SLinus Torvalds }
20261da177e4SLinus Torvalds 
20276700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2028a6279458SYOSHIFUJI Hideaki {
2029e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
2030a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2031e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2032e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2033e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2034e8599ff4SDavid S. Miller 	struct neighbour *neigh;
203571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
20366e157b6aSDavid S. Miller 	int optlen, on_link;
20376e157b6aSDavid S. Miller 	u8 *lladdr;
2038e8599ff4SDavid S. Miller 
203929a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
204071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2041e8599ff4SDavid S. Miller 
2042e8599ff4SDavid S. Miller 	if (optlen < 0) {
20436e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2044e8599ff4SDavid S. Miller 		return;
2045e8599ff4SDavid S. Miller 	}
2046e8599ff4SDavid S. Miller 
204771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2048e8599ff4SDavid S. Miller 
204971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
20506e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2051e8599ff4SDavid S. Miller 		return;
2052e8599ff4SDavid S. Miller 	}
2053e8599ff4SDavid S. Miller 
20546e157b6aSDavid S. Miller 	on_link = 0;
205571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2056e8599ff4SDavid S. Miller 		on_link = 1;
205771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2058e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
20596e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2060e8599ff4SDavid S. Miller 		return;
2061e8599ff4SDavid S. Miller 	}
2062e8599ff4SDavid S. Miller 
2063e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2064e8599ff4SDavid S. Miller 	if (!in6_dev)
2065e8599ff4SDavid S. Miller 		return;
2066e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2067e8599ff4SDavid S. Miller 		return;
2068e8599ff4SDavid S. Miller 
2069e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2070e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2071e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2072e8599ff4SDavid S. Miller 	 */
2073e8599ff4SDavid S. Miller 
207471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
2075e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2076e8599ff4SDavid S. Miller 		return;
2077e8599ff4SDavid S. Miller 	}
20786e157b6aSDavid S. Miller 
20796e157b6aSDavid S. Miller 	lladdr = NULL;
2080e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2081e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2082e8599ff4SDavid S. Miller 					     skb->dev);
2083e8599ff4SDavid S. Miller 		if (!lladdr) {
2084e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2085e8599ff4SDavid S. Miller 			return;
2086e8599ff4SDavid S. Miller 		}
2087e8599ff4SDavid S. Miller 	}
2088e8599ff4SDavid S. Miller 
20896e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
20906e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
20916e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
20926e157b6aSDavid S. Miller 		return;
20936e157b6aSDavid S. Miller 	}
20946e157b6aSDavid S. Miller 
20956e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
20966e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
20976e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
20986e157b6aSDavid S. Miller 	 */
20996e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
21006e157b6aSDavid S. Miller 
210171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2102e8599ff4SDavid S. Miller 	if (!neigh)
2103e8599ff4SDavid S. Miller 		return;
2104e8599ff4SDavid S. Miller 
21051da177e4SLinus Torvalds 	/*
21061da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
21071da177e4SLinus Torvalds 	 */
21081da177e4SLinus Torvalds 
21091da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
21101da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
21111da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
21121da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
21131da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
21141da177e4SLinus Torvalds 		     );
21151da177e4SLinus Torvalds 
211683a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
211738308473SDavid S. Miller 	if (!nrt)
21181da177e4SLinus Torvalds 		goto out;
21191da177e4SLinus Torvalds 
21201da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
21211da177e4SLinus Torvalds 	if (on_link)
21221da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
21231da177e4SLinus Torvalds 
21244e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
21251da177e4SLinus Torvalds 
212640e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
21271da177e4SLinus Torvalds 		goto out;
21281da177e4SLinus Torvalds 
2129d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2130d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
213171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
213260592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
21338d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
21348d71740cSTom Tucker 
21351da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
21366e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2137e0a1ad73SThomas Graf 		ip6_del_rt(rt);
21381da177e4SLinus Torvalds 	}
21391da177e4SLinus Torvalds 
21401da177e4SLinus Torvalds out:
2141e8599ff4SDavid S. Miller 	neigh_release(neigh);
21426e157b6aSDavid S. Miller }
21436e157b6aSDavid S. Miller 
21441da177e4SLinus Torvalds /*
21451da177e4SLinus Torvalds  *	Misc support functions
21461da177e4SLinus Torvalds  */
21471da177e4SLinus Torvalds 
21484b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
21494b32b5adSMartin KaFai Lau {
21504b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
21514b32b5adSMartin KaFai Lau 
21524b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
21534b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
21544b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
21554b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
21564b32b5adSMartin KaFai Lau }
21574b32b5adSMartin KaFai Lau 
215883a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
21591da177e4SLinus Torvalds {
2160d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2161d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
216283a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2163d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
21641da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
21651da177e4SLinus Torvalds 	if (rt->rt6i_idev)
21661da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2167d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
21684e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
21691716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
21701716a961SGao feng 	rt6_set_from(rt, ort);
217183a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
21721da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
217383a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
21741da177e4SLinus Torvalds #endif
217583a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2176c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
21775a6228a0SNicolas Dichtel 	rt->rt6i_lwtstate = lwtstate_get(ort->rt6i_lwtstate);
21781da177e4SLinus Torvalds }
21791da177e4SLinus Torvalds 
218070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2181efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2182b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2183b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
218470ceb4f5SYOSHIFUJI Hideaki {
218570ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
218670ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2187c71099acSThomas Graf 	struct fib6_table *table;
218870ceb4f5SYOSHIFUJI Hideaki 
2189efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
219038308473SDavid S. Miller 	if (!table)
2191c71099acSThomas Graf 		return NULL;
2192c71099acSThomas Graf 
21935744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2194c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
219570ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
219670ceb4f5SYOSHIFUJI Hideaki 		goto out;
219770ceb4f5SYOSHIFUJI Hideaki 
2198d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2199d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
220070ceb4f5SYOSHIFUJI Hideaki 			continue;
220170ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
220270ceb4f5SYOSHIFUJI Hideaki 			continue;
220370ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
220470ceb4f5SYOSHIFUJI Hideaki 			continue;
2205d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
220670ceb4f5SYOSHIFUJI Hideaki 		break;
220770ceb4f5SYOSHIFUJI Hideaki 	}
220870ceb4f5SYOSHIFUJI Hideaki out:
22095744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
221070ceb4f5SYOSHIFUJI Hideaki 	return rt;
221170ceb4f5SYOSHIFUJI Hideaki }
221270ceb4f5SYOSHIFUJI Hideaki 
2213efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2214b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2215b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
221695c96174SEric Dumazet 					   unsigned int pref)
221770ceb4f5SYOSHIFUJI Hideaki {
221886872cb5SThomas Graf 	struct fib6_config cfg = {
221986872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
2220238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
222186872cb5SThomas Graf 		.fc_ifindex	= ifindex,
222286872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
222386872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
222486872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
222515e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2226efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2227efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
222886872cb5SThomas Graf 	};
222970ceb4f5SYOSHIFUJI Hideaki 
22304e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
22314e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
223286872cb5SThomas Graf 
2233e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2234e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
223586872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
223670ceb4f5SYOSHIFUJI Hideaki 
223786872cb5SThomas Graf 	ip6_route_add(&cfg);
223870ceb4f5SYOSHIFUJI Hideaki 
2239efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
224070ceb4f5SYOSHIFUJI Hideaki }
224170ceb4f5SYOSHIFUJI Hideaki #endif
224270ceb4f5SYOSHIFUJI Hideaki 
2243b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
22441da177e4SLinus Torvalds {
22451da177e4SLinus Torvalds 	struct rt6_info *rt;
2246c71099acSThomas Graf 	struct fib6_table *table;
22471da177e4SLinus Torvalds 
2248c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
224938308473SDavid S. Miller 	if (!table)
2250c71099acSThomas Graf 		return NULL;
22511da177e4SLinus Torvalds 
22525744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2253d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2254d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2255045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
22561da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
22571da177e4SLinus Torvalds 			break;
22581da177e4SLinus Torvalds 	}
22591da177e4SLinus Torvalds 	if (rt)
2260d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
22615744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
22621da177e4SLinus Torvalds 	return rt;
22631da177e4SLinus Torvalds }
22641da177e4SLinus Torvalds 
2265b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2266ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2267ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
22681da177e4SLinus Torvalds {
226986872cb5SThomas Graf 	struct fib6_config cfg = {
227086872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
2271238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
227286872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
227386872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
227486872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
227515e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
22765578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2277c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
227886872cb5SThomas Graf 	};
22791da177e4SLinus Torvalds 
22804e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
22811da177e4SLinus Torvalds 
228286872cb5SThomas Graf 	ip6_route_add(&cfg);
22831da177e4SLinus Torvalds 
22841da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
22851da177e4SLinus Torvalds }
22861da177e4SLinus Torvalds 
22877b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
22881da177e4SLinus Torvalds {
22891da177e4SLinus Torvalds 	struct rt6_info *rt;
2290c71099acSThomas Graf 	struct fib6_table *table;
2291c71099acSThomas Graf 
2292c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
22937b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
229438308473SDavid S. Miller 	if (!table)
2295c71099acSThomas Graf 		return;
22961da177e4SLinus Torvalds 
22971da177e4SLinus Torvalds restart:
2298c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2299d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
23003e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
23013e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2302d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2303c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2304e0a1ad73SThomas Graf 			ip6_del_rt(rt);
23051da177e4SLinus Torvalds 			goto restart;
23061da177e4SLinus Torvalds 		}
23071da177e4SLinus Torvalds 	}
2308c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
23091da177e4SLinus Torvalds }
23101da177e4SLinus Torvalds 
23115578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
23125578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
231386872cb5SThomas Graf 				 struct fib6_config *cfg)
231486872cb5SThomas Graf {
231586872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
231686872cb5SThomas Graf 
231786872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
231886872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
231986872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
232086872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
232186872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
232286872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
232386872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
232486872cb5SThomas Graf 
23255578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2326f1243c2dSBenjamin Thery 
23274e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
23284e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
23294e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
233086872cb5SThomas Graf }
233186872cb5SThomas Graf 
23325578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
23331da177e4SLinus Torvalds {
233486872cb5SThomas Graf 	struct fib6_config cfg;
23351da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
23361da177e4SLinus Torvalds 	int err;
23371da177e4SLinus Torvalds 
23381da177e4SLinus Torvalds 	switch (cmd) {
23391da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
23401da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2341af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
23421da177e4SLinus Torvalds 			return -EPERM;
23431da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
23441da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
23451da177e4SLinus Torvalds 		if (err)
23461da177e4SLinus Torvalds 			return -EFAULT;
23471da177e4SLinus Torvalds 
23485578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
234986872cb5SThomas Graf 
23501da177e4SLinus Torvalds 		rtnl_lock();
23511da177e4SLinus Torvalds 		switch (cmd) {
23521da177e4SLinus Torvalds 		case SIOCADDRT:
235386872cb5SThomas Graf 			err = ip6_route_add(&cfg);
23541da177e4SLinus Torvalds 			break;
23551da177e4SLinus Torvalds 		case SIOCDELRT:
235686872cb5SThomas Graf 			err = ip6_route_del(&cfg);
23571da177e4SLinus Torvalds 			break;
23581da177e4SLinus Torvalds 		default:
23591da177e4SLinus Torvalds 			err = -EINVAL;
23601da177e4SLinus Torvalds 		}
23611da177e4SLinus Torvalds 		rtnl_unlock();
23621da177e4SLinus Torvalds 
23631da177e4SLinus Torvalds 		return err;
23643ff50b79SStephen Hemminger 	}
23651da177e4SLinus Torvalds 
23661da177e4SLinus Torvalds 	return -EINVAL;
23671da177e4SLinus Torvalds }
23681da177e4SLinus Torvalds 
23691da177e4SLinus Torvalds /*
23701da177e4SLinus Torvalds  *	Drop the packet on the floor
23711da177e4SLinus Torvalds  */
23721da177e4SLinus Torvalds 
2373d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
23741da177e4SLinus Torvalds {
2375612f09e8SYOSHIFUJI Hideaki 	int type;
2376adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2377612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2378612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
23790660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
238045bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
23813bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
23823bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2383612f09e8SYOSHIFUJI Hideaki 			break;
2384612f09e8SYOSHIFUJI Hideaki 		}
2385612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2386612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
23873bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
23883bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2389612f09e8SYOSHIFUJI Hideaki 		break;
2390612f09e8SYOSHIFUJI Hideaki 	}
23913ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
23921da177e4SLinus Torvalds 	kfree_skb(skb);
23931da177e4SLinus Torvalds 	return 0;
23941da177e4SLinus Torvalds }
23951da177e4SLinus Torvalds 
23969ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
23979ce8ade0SThomas Graf {
2398612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
23999ce8ade0SThomas Graf }
24009ce8ade0SThomas Graf 
2401aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb)
24021da177e4SLinus Torvalds {
2403adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2404612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
24051da177e4SLinus Torvalds }
24061da177e4SLinus Torvalds 
24079ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
24089ce8ade0SThomas Graf {
2409612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
24109ce8ade0SThomas Graf }
24119ce8ade0SThomas Graf 
2412aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb)
24139ce8ade0SThomas Graf {
2414adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2415612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
24169ce8ade0SThomas Graf }
24179ce8ade0SThomas Graf 
24181da177e4SLinus Torvalds /*
24191da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
24201da177e4SLinus Torvalds  */
24211da177e4SLinus Torvalds 
24221da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
24231da177e4SLinus Torvalds 				    const struct in6_addr *addr,
24248f031519SDavid S. Miller 				    bool anycast)
24251da177e4SLinus Torvalds {
2426c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2427a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2428a3300ef4SHannes Frederic Sowa 					    DST_NOCOUNT, NULL);
2429a3300ef4SHannes Frederic Sowa 	if (!rt)
24301da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
24311da177e4SLinus Torvalds 
24321da177e4SLinus Torvalds 	in6_dev_hold(idev);
24331da177e4SLinus Torvalds 
243411d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2435d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2436d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
24371da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
24381da177e4SLinus Torvalds 
24391da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
244058c4fb86SYOSHIFUJI Hideaki 	if (anycast)
244158c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
244258c4fb86SYOSHIFUJI Hideaki 	else
24431da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
24441da177e4SLinus Torvalds 
2445550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
24464e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
24471da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
24485578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
24491da177e4SLinus Torvalds 
2450d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
24511da177e4SLinus Torvalds 
24521da177e4SLinus Torvalds 	return rt;
24531da177e4SLinus Torvalds }
24541da177e4SLinus Torvalds 
2455c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2456c3968a85SDaniel Walter 			struct rt6_info *rt,
2457b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2458c3968a85SDaniel Walter 			unsigned int prefs,
2459c3968a85SDaniel Walter 			struct in6_addr *saddr)
2460c3968a85SDaniel Walter {
2461e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2462e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2463c3968a85SDaniel Walter 	int err = 0;
2464e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
24654e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2466c3968a85SDaniel Walter 	else
2467c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2468c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2469c3968a85SDaniel Walter 	return err;
2470c3968a85SDaniel Walter }
2471c3968a85SDaniel Walter 
2472c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2473c3968a85SDaniel Walter struct arg_dev_net_ip {
2474c3968a85SDaniel Walter 	struct net_device *dev;
2475c3968a85SDaniel Walter 	struct net *net;
2476c3968a85SDaniel Walter 	struct in6_addr *addr;
2477c3968a85SDaniel Walter };
2478c3968a85SDaniel Walter 
2479c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2480c3968a85SDaniel Walter {
2481c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2482c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2483c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2484c3968a85SDaniel Walter 
2485d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2486c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2487c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2488c3968a85SDaniel Walter 		/* remove prefsrc entry */
2489c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2490c3968a85SDaniel Walter 	}
2491c3968a85SDaniel Walter 	return 0;
2492c3968a85SDaniel Walter }
2493c3968a85SDaniel Walter 
2494c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2495c3968a85SDaniel Walter {
2496c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2497c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2498c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2499c3968a85SDaniel Walter 		.net = net,
2500c3968a85SDaniel Walter 		.addr = &ifp->addr,
2501c3968a85SDaniel Walter 	};
25020c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2503c3968a85SDaniel Walter }
2504c3968a85SDaniel Walter 
2505be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2506be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2507be7a010dSDuan Jiong 
2508be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2509be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2510be7a010dSDuan Jiong {
2511be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2512be7a010dSDuan Jiong 
2513be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2514be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2515be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2516be7a010dSDuan Jiong 		return -1;
2517be7a010dSDuan Jiong 	}
2518be7a010dSDuan Jiong 	return 0;
2519be7a010dSDuan Jiong }
2520be7a010dSDuan Jiong 
2521be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2522be7a010dSDuan Jiong {
2523be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2524be7a010dSDuan Jiong }
2525be7a010dSDuan Jiong 
25268ed67789SDaniel Lezcano struct arg_dev_net {
25278ed67789SDaniel Lezcano 	struct net_device *dev;
25288ed67789SDaniel Lezcano 	struct net *net;
25298ed67789SDaniel Lezcano };
25308ed67789SDaniel Lezcano 
25311da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
25321da177e4SLinus Torvalds {
2533bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2534bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
25358ed67789SDaniel Lezcano 
2536d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2537c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
25381da177e4SLinus Torvalds 		return -1;
2539c159d30cSDavid S. Miller 
25401da177e4SLinus Torvalds 	return 0;
25411da177e4SLinus Torvalds }
25421da177e4SLinus Torvalds 
2543f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
25441da177e4SLinus Torvalds {
25458ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
25468ed67789SDaniel Lezcano 		.dev = dev,
25478ed67789SDaniel Lezcano 		.net = net,
25488ed67789SDaniel Lezcano 	};
25498ed67789SDaniel Lezcano 
25500c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
25511e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
25528d0b94afSMartin KaFai Lau 	rt6_uncached_list_flush_dev(net, dev);
25531da177e4SLinus Torvalds }
25541da177e4SLinus Torvalds 
255595c96174SEric Dumazet struct rt6_mtu_change_arg {
25561da177e4SLinus Torvalds 	struct net_device *dev;
255795c96174SEric Dumazet 	unsigned int mtu;
25581da177e4SLinus Torvalds };
25591da177e4SLinus Torvalds 
25601da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
25611da177e4SLinus Torvalds {
25621da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
25631da177e4SLinus Torvalds 	struct inet6_dev *idev;
25641da177e4SLinus Torvalds 
25651da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
25661da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
25671da177e4SLinus Torvalds 	   We still use this lock to block changes
25681da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
25691da177e4SLinus Torvalds 	*/
25701da177e4SLinus Torvalds 
25711da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
257238308473SDavid S. Miller 	if (!idev)
25731da177e4SLinus Torvalds 		return 0;
25741da177e4SLinus Torvalds 
25751da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
25761da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
25771da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
25781da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
25791da177e4SLinus Torvalds 	 */
25801da177e4SLinus Torvalds 	/*
25811da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
25821da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
25831da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
25841da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
25851da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
25861da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
25871da177e4SLinus Torvalds 	   PMTU discouvery.
25881da177e4SLinus Torvalds 	 */
2589d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
25904b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
25914b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
25924b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
25934b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
25944b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
25954b32b5adSMartin KaFai Lau 			 * been updated.
25964b32b5adSMartin KaFai Lau 			 */
25974b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
25984b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
25994b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2600d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
26014b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2602defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2603566cfd8fSSimon Arlott 		}
26044b32b5adSMartin KaFai Lau 	}
26051da177e4SLinus Torvalds 	return 0;
26061da177e4SLinus Torvalds }
26071da177e4SLinus Torvalds 
260895c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
26091da177e4SLinus Torvalds {
2610c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2611c71099acSThomas Graf 		.dev = dev,
2612c71099acSThomas Graf 		.mtu = mtu,
2613c71099acSThomas Graf 	};
26141da177e4SLinus Torvalds 
26150c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
26161da177e4SLinus Torvalds }
26171da177e4SLinus Torvalds 
2618ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
26195176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
262086872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2621ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
262286872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
262386872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
262451ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2625c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
262619e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
262719e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
262886872cb5SThomas Graf };
262986872cb5SThomas Graf 
263086872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
263186872cb5SThomas Graf 			      struct fib6_config *cfg)
26321da177e4SLinus Torvalds {
263386872cb5SThomas Graf 	struct rtmsg *rtm;
263486872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2635c78ba6d6SLubomir Rintel 	unsigned int pref;
263686872cb5SThomas Graf 	int err;
26371da177e4SLinus Torvalds 
263886872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
263986872cb5SThomas Graf 	if (err < 0)
264086872cb5SThomas Graf 		goto errout;
26411da177e4SLinus Torvalds 
264286872cb5SThomas Graf 	err = -EINVAL;
264386872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
264486872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
264586872cb5SThomas Graf 
264686872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
264786872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
264886872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
264986872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
265086872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2651ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
265286872cb5SThomas Graf 
2653ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2654ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2655b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2656b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
265786872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
265886872cb5SThomas Graf 
2659ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2660ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2661ab79ad14SMaciej Żenczykowski 
26621f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
26631f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
26641f56a01fSMartin KaFai Lau 
266515e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
266686872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
26673b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
266886872cb5SThomas Graf 
266986872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
267067b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
267186872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
26721da177e4SLinus Torvalds 	}
267386872cb5SThomas Graf 
267486872cb5SThomas Graf 	if (tb[RTA_DST]) {
267586872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
267686872cb5SThomas Graf 
267786872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
267886872cb5SThomas Graf 			goto errout;
267986872cb5SThomas Graf 
268086872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
26811da177e4SLinus Torvalds 	}
268286872cb5SThomas Graf 
268386872cb5SThomas Graf 	if (tb[RTA_SRC]) {
268486872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
268586872cb5SThomas Graf 
268686872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
268786872cb5SThomas Graf 			goto errout;
268886872cb5SThomas Graf 
268986872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
26901da177e4SLinus Torvalds 	}
269186872cb5SThomas Graf 
2692c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
269367b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2694c3968a85SDaniel Walter 
269586872cb5SThomas Graf 	if (tb[RTA_OIF])
269686872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
269786872cb5SThomas Graf 
269886872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
269986872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
270086872cb5SThomas Graf 
270186872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
270286872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
270386872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
27041da177e4SLinus Torvalds 	}
270586872cb5SThomas Graf 
270686872cb5SThomas Graf 	if (tb[RTA_TABLE])
270786872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
270886872cb5SThomas Graf 
270951ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
271051ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
271151ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
271251ebd318SNicolas Dichtel 	}
271351ebd318SNicolas Dichtel 
2714c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2715c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2716c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2717c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2718c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2719c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2720c78ba6d6SLubomir Rintel 	}
2721c78ba6d6SLubomir Rintel 
272219e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
272319e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
272419e42e45SRoopa Prabhu 
272519e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP_TYPE])
272619e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
272719e42e45SRoopa Prabhu 
272886872cb5SThomas Graf 	err = 0;
272986872cb5SThomas Graf errout:
273086872cb5SThomas Graf 	return err;
27311da177e4SLinus Torvalds }
27321da177e4SLinus Torvalds 
273351ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add)
273451ebd318SNicolas Dichtel {
273551ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
273651ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
273751ebd318SNicolas Dichtel 	int remaining;
273851ebd318SNicolas Dichtel 	int attrlen;
273951ebd318SNicolas Dichtel 	int err = 0, last_err = 0;
274051ebd318SNicolas Dichtel 
274135f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
274251ebd318SNicolas Dichtel beginning:
274351ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
274451ebd318SNicolas Dichtel 
274551ebd318SNicolas Dichtel 	/* Parse a Multipath Entry */
274651ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
274751ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
274851ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
274951ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
275051ebd318SNicolas Dichtel 
275151ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
275251ebd318SNicolas Dichtel 		if (attrlen > 0) {
275351ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
275451ebd318SNicolas Dichtel 
275551ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
275651ebd318SNicolas Dichtel 			if (nla) {
275767b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
275851ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
275951ebd318SNicolas Dichtel 			}
276019e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
276119e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
276219e42e45SRoopa Prabhu 			if (nla)
276319e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
276451ebd318SNicolas Dichtel 		}
276551ebd318SNicolas Dichtel 		err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
276651ebd318SNicolas Dichtel 		if (err) {
276751ebd318SNicolas Dichtel 			last_err = err;
276851ebd318SNicolas Dichtel 			/* If we are trying to remove a route, do not stop the
276951ebd318SNicolas Dichtel 			 * loop when ip6_route_del() fails (because next hop is
277051ebd318SNicolas Dichtel 			 * already gone), we should try to remove all next hops.
277151ebd318SNicolas Dichtel 			 */
277251ebd318SNicolas Dichtel 			if (add) {
277351ebd318SNicolas Dichtel 				/* If add fails, we should try to delete all
277451ebd318SNicolas Dichtel 				 * next hops that have been already added.
277551ebd318SNicolas Dichtel 				 */
277651ebd318SNicolas Dichtel 				add = 0;
277735f1b4e9SMichal Kubeček 				remaining = cfg->fc_mp_len - remaining;
277851ebd318SNicolas Dichtel 				goto beginning;
277951ebd318SNicolas Dichtel 			}
278051ebd318SNicolas Dichtel 		}
27811a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
278227596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
278327596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
278427596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
278527596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
278627596472SMichal Kubeček 		 * be added to it.
27871a72418bSNicolas Dichtel 		 */
278827596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
278927596472SMichal Kubeček 						     NLM_F_REPLACE);
279051ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
279151ebd318SNicolas Dichtel 	}
279251ebd318SNicolas Dichtel 
279351ebd318SNicolas Dichtel 	return last_err;
279451ebd318SNicolas Dichtel }
279551ebd318SNicolas Dichtel 
2796661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
27971da177e4SLinus Torvalds {
279886872cb5SThomas Graf 	struct fib6_config cfg;
279986872cb5SThomas Graf 	int err;
28001da177e4SLinus Torvalds 
280186872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
280286872cb5SThomas Graf 	if (err < 0)
280386872cb5SThomas Graf 		return err;
280486872cb5SThomas Graf 
280551ebd318SNicolas Dichtel 	if (cfg.fc_mp)
280651ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 0);
280751ebd318SNicolas Dichtel 	else
280886872cb5SThomas Graf 		return ip6_route_del(&cfg);
28091da177e4SLinus Torvalds }
28101da177e4SLinus Torvalds 
2811661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
28121da177e4SLinus Torvalds {
281386872cb5SThomas Graf 	struct fib6_config cfg;
281486872cb5SThomas Graf 	int err;
28151da177e4SLinus Torvalds 
281686872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
281786872cb5SThomas Graf 	if (err < 0)
281886872cb5SThomas Graf 		return err;
281986872cb5SThomas Graf 
282051ebd318SNicolas Dichtel 	if (cfg.fc_mp)
282151ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 1);
282251ebd318SNicolas Dichtel 	else
282386872cb5SThomas Graf 		return ip6_route_add(&cfg);
28241da177e4SLinus Torvalds }
28251da177e4SLinus Torvalds 
282619e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
2827339bf98fSThomas Graf {
2828339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2829339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2830339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2831339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2832339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2833339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2834339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2835339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2836339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
28376a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2838ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
2839c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
284019e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
284119e42e45SRoopa Prabhu 	       + lwtunnel_get_encap_size(rt->rt6i_lwtstate);
2842339bf98fSThomas Graf }
2843339bf98fSThomas Graf 
2844191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2845191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
28460d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
284715e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
28487bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
28491da177e4SLinus Torvalds {
28504b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
28511da177e4SLinus Torvalds 	struct rtmsg *rtm;
28521da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2853e3703b3dSThomas Graf 	long expires;
28549e762a4aSPatrick McHardy 	u32 table;
28551da177e4SLinus Torvalds 
28561da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
28571da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
28581da177e4SLinus Torvalds 			/* success since this is not a prefix route */
28591da177e4SLinus Torvalds 			return 1;
28601da177e4SLinus Torvalds 		}
28611da177e4SLinus Torvalds 	}
28621da177e4SLinus Torvalds 
286315e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
286438308473SDavid S. Miller 	if (!nlh)
286526932566SPatrick McHardy 		return -EMSGSIZE;
28662d7202bfSThomas Graf 
28672d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
28681da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
28691da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
28701da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
28711da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2872c71099acSThomas Graf 	if (rt->rt6i_table)
28739e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2874c71099acSThomas Graf 	else
28759e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
28769e762a4aSPatrick McHardy 	rtm->rtm_table = table;
2877c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
2878c78679e8SDavid S. Miller 		goto nla_put_failure;
2879ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
2880ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
2881ef2c7d7bSNicolas Dichtel 		case -EINVAL:
2882ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
2883ef2c7d7bSNicolas Dichtel 			break;
2884ef2c7d7bSNicolas Dichtel 		case -EACCES:
2885ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
2886ef2c7d7bSNicolas Dichtel 			break;
2887b4949ab2SNicolas Dichtel 		case -EAGAIN:
2888b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
2889b4949ab2SNicolas Dichtel 			break;
2890ef2c7d7bSNicolas Dichtel 		default:
28911da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
2892ef2c7d7bSNicolas Dichtel 			break;
2893ef2c7d7bSNicolas Dichtel 		}
2894ef2c7d7bSNicolas Dichtel 	}
2895ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2896ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2897d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
28981da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
28991da177e4SLinus Torvalds 	else
29001da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
29011da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
290235103d11SAndy Gospodarek 	if (!netif_carrier_ok(rt->dst.dev)) {
2903cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
290435103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
290535103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
290635103d11SAndy Gospodarek 	}
29071da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
29081da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
29091da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
29101da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
2911f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
2912f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
29131da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
2914f0396f60SDenis Ovsienko 		else
2915f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
2916f0396f60SDenis Ovsienko 	}
29171da177e4SLinus Torvalds 
29181da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
29191da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
29201da177e4SLinus Torvalds 
29211da177e4SLinus Torvalds 	if (dst) {
2922930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
2923c78679e8SDavid S. Miller 			goto nla_put_failure;
29241da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
29251da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
2926930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
2927c78679e8SDavid S. Miller 			goto nla_put_failure;
29281da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
29291da177e4SLinus Torvalds 	if (src) {
2930930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
2931c78679e8SDavid S. Miller 			goto nla_put_failure;
29321da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
2933c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
2934930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
2935c78679e8SDavid S. Miller 		goto nla_put_failure;
29361da177e4SLinus Torvalds #endif
29377bc570c8SYOSHIFUJI Hideaki 	if (iif) {
29387bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
29397bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
29408229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
29417bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
29427bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
29437bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
29447bc570c8SYOSHIFUJI Hideaki 						return 0;
29457bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
29467bc570c8SYOSHIFUJI Hideaki 				} else {
29477bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
29487bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
29497bc570c8SYOSHIFUJI Hideaki 				}
29507bc570c8SYOSHIFUJI Hideaki 			}
29517bc570c8SYOSHIFUJI Hideaki 		} else
29527bc570c8SYOSHIFUJI Hideaki #endif
2953c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
2954c78679e8SDavid S. Miller 				goto nla_put_failure;
29557bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
29561da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2957c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2958930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2959c78679e8SDavid S. Miller 			goto nla_put_failure;
2960c3968a85SDaniel Walter 	}
2961c3968a85SDaniel Walter 
2962c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2963c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
29644e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
2965930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2966c78679e8SDavid S. Miller 			goto nla_put_failure;
29671da177e4SLinus Torvalds 	}
29682d7202bfSThomas Graf 
29694b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
29704b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
29714b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
29724b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
29732d7202bfSThomas Graf 		goto nla_put_failure;
29742d7202bfSThomas Graf 
2975dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
2976930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
297794f826b8SEric Dumazet 			goto nla_put_failure;
297894f826b8SEric Dumazet 	}
29792d7202bfSThomas Graf 
2980c78679e8SDavid S. Miller 	if (rt->dst.dev &&
2981c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2982c78679e8SDavid S. Miller 		goto nla_put_failure;
2983c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2984c78679e8SDavid S. Miller 		goto nla_put_failure;
29858253947eSLi Wei 
29868253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
298769cdf8f9SYOSHIFUJI Hideaki 
298887a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
2989e3703b3dSThomas Graf 		goto nla_put_failure;
29901da177e4SLinus Torvalds 
2991c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
2992c78ba6d6SLubomir Rintel 		goto nla_put_failure;
2993c78ba6d6SLubomir Rintel 
299419e42e45SRoopa Prabhu 	lwtunnel_fill_encap(skb, rt->rt6i_lwtstate);
299519e42e45SRoopa Prabhu 
2996053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2997053c095aSJohannes Berg 	return 0;
29982d7202bfSThomas Graf 
29992d7202bfSThomas Graf nla_put_failure:
300026932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
300126932566SPatrick McHardy 	return -EMSGSIZE;
30021da177e4SLinus Torvalds }
30031da177e4SLinus Torvalds 
30041b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
30051da177e4SLinus Torvalds {
30061da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
30071da177e4SLinus Torvalds 	int prefix;
30081da177e4SLinus Torvalds 
30092d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
30102d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
30111da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
30121da177e4SLinus Torvalds 	} else
30131da177e4SLinus Torvalds 		prefix = 0;
30141da177e4SLinus Torvalds 
3015191cd582SBrian Haley 	return rt6_fill_node(arg->net,
3016191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
301715e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
30187bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
30191da177e4SLinus Torvalds }
30201da177e4SLinus Torvalds 
3021661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
30221da177e4SLinus Torvalds {
30233b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3024ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
30251da177e4SLinus Torvalds 	struct rt6_info *rt;
3026ab364a6fSThomas Graf 	struct sk_buff *skb;
3027ab364a6fSThomas Graf 	struct rtmsg *rtm;
30284c9483b2SDavid S. Miller 	struct flowi6 fl6;
302972331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3030ab364a6fSThomas Graf 
3031ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3032ab364a6fSThomas Graf 	if (err < 0)
3033ab364a6fSThomas Graf 		goto errout;
3034ab364a6fSThomas Graf 
3035ab364a6fSThomas Graf 	err = -EINVAL;
30364c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
3037ab364a6fSThomas Graf 
3038ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3039ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3040ab364a6fSThomas Graf 			goto errout;
3041ab364a6fSThomas Graf 
30424e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3043ab364a6fSThomas Graf 	}
3044ab364a6fSThomas Graf 
3045ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3046ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3047ab364a6fSThomas Graf 			goto errout;
3048ab364a6fSThomas Graf 
30494e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3050ab364a6fSThomas Graf 	}
3051ab364a6fSThomas Graf 
3052ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3053ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3054ab364a6fSThomas Graf 
3055ab364a6fSThomas Graf 	if (tb[RTA_OIF])
305672331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3057ab364a6fSThomas Graf 
30582e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
30592e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
30602e47b291SLorenzo Colitti 
3061ab364a6fSThomas Graf 	if (iif) {
3062ab364a6fSThomas Graf 		struct net_device *dev;
306372331bc0SShmulik Ladkani 		int flags = 0;
306472331bc0SShmulik Ladkani 
30655578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3066ab364a6fSThomas Graf 		if (!dev) {
3067ab364a6fSThomas Graf 			err = -ENODEV;
3068ab364a6fSThomas Graf 			goto errout;
3069ab364a6fSThomas Graf 		}
307072331bc0SShmulik Ladkani 
307172331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
307272331bc0SShmulik Ladkani 
307372331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
307472331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
307572331bc0SShmulik Ladkani 
307672331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
307772331bc0SShmulik Ladkani 							       flags);
307872331bc0SShmulik Ladkani 	} else {
307972331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
308072331bc0SShmulik Ladkani 
308172331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3082ab364a6fSThomas Graf 	}
30831da177e4SLinus Torvalds 
30841da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
308538308473SDavid S. Miller 	if (!skb) {
308694e187c0SAmerigo Wang 		ip6_rt_put(rt);
3087ab364a6fSThomas Graf 		err = -ENOBUFS;
3088ab364a6fSThomas Graf 		goto errout;
3089ab364a6fSThomas Graf 	}
30901da177e4SLinus Torvalds 
30911da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
30921da177e4SLinus Torvalds 	   through good chunk of routing engine.
30931da177e4SLinus Torvalds 	 */
3094459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
30951da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
30961da177e4SLinus Torvalds 
3097d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
30981da177e4SLinus Torvalds 
30994c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
310015e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
31017bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
31021da177e4SLinus Torvalds 	if (err < 0) {
3103ab364a6fSThomas Graf 		kfree_skb(skb);
3104ab364a6fSThomas Graf 		goto errout;
31051da177e4SLinus Torvalds 	}
31061da177e4SLinus Torvalds 
310715e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3108ab364a6fSThomas Graf errout:
31091da177e4SLinus Torvalds 	return err;
31101da177e4SLinus Torvalds }
31111da177e4SLinus Torvalds 
311286872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
31131da177e4SLinus Torvalds {
31141da177e4SLinus Torvalds 	struct sk_buff *skb;
31155578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3116528c4cebSDenis V. Lunev 	u32 seq;
3117528c4cebSDenis V. Lunev 	int err;
31180d51aa80SJamal Hadi Salim 
3119528c4cebSDenis V. Lunev 	err = -ENOBUFS;
312038308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
312186872cb5SThomas Graf 
312219e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
312338308473SDavid S. Miller 	if (!skb)
312421713ebcSThomas Graf 		goto errout;
31251da177e4SLinus Torvalds 
3126191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
312715e47304SEric W. Biederman 				event, info->portid, seq, 0, 0, 0);
312826932566SPatrick McHardy 	if (err < 0) {
312926932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
313026932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
313126932566SPatrick McHardy 		kfree_skb(skb);
313226932566SPatrick McHardy 		goto errout;
313326932566SPatrick McHardy 	}
313415e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
31355578689aSDaniel Lezcano 		    info->nlh, gfp_any());
31361ce85fe4SPablo Neira Ayuso 	return;
313721713ebcSThomas Graf errout:
313821713ebcSThomas Graf 	if (err < 0)
31395578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
31401da177e4SLinus Torvalds }
31411da177e4SLinus Torvalds 
31428ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3143351638e7SJiri Pirko 				unsigned long event, void *ptr)
31448ed67789SDaniel Lezcano {
3145351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3146c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
31478ed67789SDaniel Lezcano 
31488ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3149d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
31508ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
31518ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3152d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
31538ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3154d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
31558ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
31568ed67789SDaniel Lezcano #endif
31578ed67789SDaniel Lezcano 	}
31588ed67789SDaniel Lezcano 
31598ed67789SDaniel Lezcano 	return NOTIFY_OK;
31608ed67789SDaniel Lezcano }
31618ed67789SDaniel Lezcano 
31621da177e4SLinus Torvalds /*
31631da177e4SLinus Torvalds  *	/proc
31641da177e4SLinus Torvalds  */
31651da177e4SLinus Torvalds 
31661da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
31671da177e4SLinus Torvalds 
316833120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
316933120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
317033120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
317133120b30SAlexey Dobriyan 	.read		= seq_read,
317233120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
31738d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
317433120b30SAlexey Dobriyan };
317533120b30SAlexey Dobriyan 
31761da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
31771da177e4SLinus Torvalds {
317869ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
31791da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
318069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
318169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
318269ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
318369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
318469ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3185fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
318669ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
31871da177e4SLinus Torvalds 
31881da177e4SLinus Torvalds 	return 0;
31891da177e4SLinus Torvalds }
31901da177e4SLinus Torvalds 
31911da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
31921da177e4SLinus Torvalds {
3193de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
319469ddb805SDaniel Lezcano }
319569ddb805SDaniel Lezcano 
31969a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
31971da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
31981da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
31991da177e4SLinus Torvalds 	.read	 = seq_read,
32001da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3201b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
32021da177e4SLinus Torvalds };
32031da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
32041da177e4SLinus Torvalds 
32051da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
32061da177e4SLinus Torvalds 
32071da177e4SLinus Torvalds static
3208fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
32091da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
32101da177e4SLinus Torvalds {
3211c486da34SLucian Adrian Grijincu 	struct net *net;
3212c486da34SLucian Adrian Grijincu 	int delay;
3213c486da34SLucian Adrian Grijincu 	if (!write)
3214c486da34SLucian Adrian Grijincu 		return -EINVAL;
3215c486da34SLucian Adrian Grijincu 
3216c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3217c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
32188d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
32192ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
32201da177e4SLinus Torvalds 	return 0;
32211da177e4SLinus Torvalds }
32221da177e4SLinus Torvalds 
3223fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
32241da177e4SLinus Torvalds 	{
32251da177e4SLinus Torvalds 		.procname	=	"flush",
32264990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
32271da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
322889c8b3a1SDave Jones 		.mode		=	0200,
32296d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
32301da177e4SLinus Torvalds 	},
32311da177e4SLinus Torvalds 	{
32321da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
32339a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
32341da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32351da177e4SLinus Torvalds 		.mode		=	0644,
32366d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
32371da177e4SLinus Torvalds 	},
32381da177e4SLinus Torvalds 	{
32391da177e4SLinus Torvalds 		.procname	=	"max_size",
32404990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
32411da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32421da177e4SLinus Torvalds 		.mode		=	0644,
32436d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
32441da177e4SLinus Torvalds 	},
32451da177e4SLinus Torvalds 	{
32461da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
32474990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
32481da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32491da177e4SLinus Torvalds 		.mode		=	0644,
32506d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32511da177e4SLinus Torvalds 	},
32521da177e4SLinus Torvalds 	{
32531da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
32544990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
32551da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32561da177e4SLinus Torvalds 		.mode		=	0644,
32576d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32581da177e4SLinus Torvalds 	},
32591da177e4SLinus Torvalds 	{
32601da177e4SLinus Torvalds 		.procname	=	"gc_interval",
32614990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
32621da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32631da177e4SLinus Torvalds 		.mode		=	0644,
32646d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32651da177e4SLinus Torvalds 	},
32661da177e4SLinus Torvalds 	{
32671da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
32684990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
32691da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32701da177e4SLinus Torvalds 		.mode		=	0644,
3271f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
32721da177e4SLinus Torvalds 	},
32731da177e4SLinus Torvalds 	{
32741da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
32754990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
32761da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32771da177e4SLinus Torvalds 		.mode		=	0644,
32786d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32791da177e4SLinus Torvalds 	},
32801da177e4SLinus Torvalds 	{
32811da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
32824990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
32831da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32841da177e4SLinus Torvalds 		.mode		=	0644,
3285f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
32861da177e4SLinus Torvalds 	},
32871da177e4SLinus Torvalds 	{
32881da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
32894990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
32901da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32911da177e4SLinus Torvalds 		.mode		=	0644,
32926d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
32931da177e4SLinus Torvalds 	},
3294f8572d8fSEric W. Biederman 	{ }
32951da177e4SLinus Torvalds };
32961da177e4SLinus Torvalds 
32972c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3298760f2d01SDaniel Lezcano {
3299760f2d01SDaniel Lezcano 	struct ctl_table *table;
3300760f2d01SDaniel Lezcano 
3301760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3302760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3303760f2d01SDaniel Lezcano 			GFP_KERNEL);
33045ee09105SYOSHIFUJI Hideaki 
33055ee09105SYOSHIFUJI Hideaki 	if (table) {
33065ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3307c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
330886393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
33095ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
33105ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
33115ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
33125ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
33135ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
33145ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
33155ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
33169c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3317464dc801SEric W. Biederman 
3318464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3319464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3320464dc801SEric W. Biederman 			table[0].procname = NULL;
33215ee09105SYOSHIFUJI Hideaki 	}
33225ee09105SYOSHIFUJI Hideaki 
3323760f2d01SDaniel Lezcano 	return table;
3324760f2d01SDaniel Lezcano }
33251da177e4SLinus Torvalds #endif
33261da177e4SLinus Torvalds 
33272c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3328cdb18761SDaniel Lezcano {
3329633d424bSPavel Emelyanov 	int ret = -ENOMEM;
33308ed67789SDaniel Lezcano 
333186393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
333286393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3333f2fc6a54SBenjamin Thery 
3334fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3335fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3336fc66f95cSEric Dumazet 
33378ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
33388ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
33398ed67789SDaniel Lezcano 					   GFP_KERNEL);
33408ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3341fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3342d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
33438ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3344d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
334562fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
334662fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
33478ed67789SDaniel Lezcano 
33488ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
33498ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
33508ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
33518ed67789SDaniel Lezcano 					       GFP_KERNEL);
335268fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
335368fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3354d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
33558ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3356d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
335762fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
335862fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
33598ed67789SDaniel Lezcano 
33608ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
33618ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
33628ed67789SDaniel Lezcano 					       GFP_KERNEL);
336368fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
336468fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3365d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
33668ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3367d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
336862fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
336962fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
33708ed67789SDaniel Lezcano #endif
33718ed67789SDaniel Lezcano 
3372b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3373b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3374b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3375b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3376b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3377b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3378b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3379b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3380b339a47cSPeter Zijlstra 
33816891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
33826891a346SBenjamin Thery 
33838ed67789SDaniel Lezcano 	ret = 0;
33848ed67789SDaniel Lezcano out:
33858ed67789SDaniel Lezcano 	return ret;
3386f2fc6a54SBenjamin Thery 
338768fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
338868fffc67SPeter Zijlstra out_ip6_prohibit_entry:
338968fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
339068fffc67SPeter Zijlstra out_ip6_null_entry:
339168fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
339268fffc67SPeter Zijlstra #endif
3393fc66f95cSEric Dumazet out_ip6_dst_entries:
3394fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3395f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3396f2fc6a54SBenjamin Thery 	goto out;
3397cdb18761SDaniel Lezcano }
3398cdb18761SDaniel Lezcano 
33992c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3400cdb18761SDaniel Lezcano {
34018ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
34028ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
34038ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
34048ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
34058ed67789SDaniel Lezcano #endif
340641bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3407cdb18761SDaniel Lezcano }
3408cdb18761SDaniel Lezcano 
3409d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3410d189634eSThomas Graf {
3411d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3412d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3413d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3414d189634eSThomas Graf #endif
3415d189634eSThomas Graf 	return 0;
3416d189634eSThomas Graf }
3417d189634eSThomas Graf 
3418d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3419d189634eSThomas Graf {
3420d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3421ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3422ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3423d189634eSThomas Graf #endif
3424d189634eSThomas Graf }
3425d189634eSThomas Graf 
3426cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3427cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3428cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3429cdb18761SDaniel Lezcano };
3430cdb18761SDaniel Lezcano 
3431c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3432c3426b47SDavid S. Miller {
3433c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3434c3426b47SDavid S. Miller 
3435c3426b47SDavid S. Miller 	if (!bp)
3436c3426b47SDavid S. Miller 		return -ENOMEM;
3437c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3438c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3439c3426b47SDavid S. Miller 	return 0;
3440c3426b47SDavid S. Miller }
3441c3426b47SDavid S. Miller 
3442c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3443c3426b47SDavid S. Miller {
3444c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3445c3426b47SDavid S. Miller 
3446c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
344756a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3448c3426b47SDavid S. Miller 	kfree(bp);
3449c3426b47SDavid S. Miller }
3450c3426b47SDavid S. Miller 
34512b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3452c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3453c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3454c3426b47SDavid S. Miller };
3455c3426b47SDavid S. Miller 
3456d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3457d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3458d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3459d189634eSThomas Graf };
3460d189634eSThomas Graf 
34618ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
34628ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
34638ed67789SDaniel Lezcano 	.priority = 0,
34648ed67789SDaniel Lezcano };
34658ed67789SDaniel Lezcano 
3466433d49c3SDaniel Lezcano int __init ip6_route_init(void)
34671da177e4SLinus Torvalds {
3468433d49c3SDaniel Lezcano 	int ret;
34698d0b94afSMartin KaFai Lau 	int cpu;
3470433d49c3SDaniel Lezcano 
34719a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
34729a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
34739a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
34749a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
34759a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3476c19a28e1SFernando Carrijo 		goto out;
347714e50e57SDavid S. Miller 
3478fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
34798ed67789SDaniel Lezcano 	if (ret)
3480bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3481bdb3289fSDaniel Lezcano 
3482c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3483c3426b47SDavid S. Miller 	if (ret)
3484e8803b6cSDavid S. Miller 		goto out_dst_entries;
34852a0c451aSThomas Graf 
34867e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
34877e52b33bSDavid S. Miller 	if (ret)
34887e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3489c3426b47SDavid S. Miller 
34905dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
34915dc121e9SArnaud Ebalard 
34928ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
34938ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
34948ed67789SDaniel Lezcano 	 * manually for init_net */
3495d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
34968ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3497bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3498d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
34998ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3500d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
35018ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3502bdb3289fSDaniel Lezcano   #endif
3503e8803b6cSDavid S. Miller 	ret = fib6_init();
3504433d49c3SDaniel Lezcano 	if (ret)
35058ed67789SDaniel Lezcano 		goto out_register_subsys;
3506433d49c3SDaniel Lezcano 
3507433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3508433d49c3SDaniel Lezcano 	if (ret)
3509e8803b6cSDavid S. Miller 		goto out_fib6_init;
3510c35b7e72SDaniel Lezcano 
3511433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3512433d49c3SDaniel Lezcano 	if (ret)
3513433d49c3SDaniel Lezcano 		goto xfrm6_init;
35147e5449c2SDaniel Lezcano 
3515d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3516d189634eSThomas Graf 	if (ret)
3517d189634eSThomas Graf 		goto fib6_rules_init;
3518d189634eSThomas Graf 
3519433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3520c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3521c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3522c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3523d189634eSThomas Graf 		goto out_register_late_subsys;
3524433d49c3SDaniel Lezcano 
35258ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3526cdb18761SDaniel Lezcano 	if (ret)
3527d189634eSThomas Graf 		goto out_register_late_subsys;
35288ed67789SDaniel Lezcano 
35298d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
35308d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
35318d0b94afSMartin KaFai Lau 
35328d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
35338d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
35348d0b94afSMartin KaFai Lau 	}
35358d0b94afSMartin KaFai Lau 
3536433d49c3SDaniel Lezcano out:
3537433d49c3SDaniel Lezcano 	return ret;
3538433d49c3SDaniel Lezcano 
3539d189634eSThomas Graf out_register_late_subsys:
3540d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3541433d49c3SDaniel Lezcano fib6_rules_init:
3542433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3543433d49c3SDaniel Lezcano xfrm6_init:
3544433d49c3SDaniel Lezcano 	xfrm6_fini();
35452a0c451aSThomas Graf out_fib6_init:
35462a0c451aSThomas Graf 	fib6_gc_cleanup();
35478ed67789SDaniel Lezcano out_register_subsys:
35488ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
35497e52b33bSDavid S. Miller out_register_inetpeer:
35507e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3551fc66f95cSEric Dumazet out_dst_entries:
3552fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3553433d49c3SDaniel Lezcano out_kmem_cache:
3554f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3555433d49c3SDaniel Lezcano 	goto out;
35561da177e4SLinus Torvalds }
35571da177e4SLinus Torvalds 
35581da177e4SLinus Torvalds void ip6_route_cleanup(void)
35591da177e4SLinus Torvalds {
35608ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3561d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3562101367c2SThomas Graf 	fib6_rules_cleanup();
35631da177e4SLinus Torvalds 	xfrm6_fini();
35641da177e4SLinus Torvalds 	fib6_gc_cleanup();
3565c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
35668ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
356741bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3568f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
35691da177e4SLinus Torvalds }
3570