xref: /openbmc/linux/net/ipv6/route.c (revision 904af04d30f303d96902584206457128c3051d8d)
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>
57*904af04dSJiri Benc #include <net/dst_metadata.h>
581da177e4SLinus Torvalds #include <net/xfrm.h>
598d71740cSTom Tucker #include <net/netevent.h>
6021713ebcSThomas Graf #include <net/netlink.h>
6151ebd318SNicolas Dichtel #include <net/nexthop.h>
6219e42e45SRoopa Prabhu #include <net/lwtunnel.h>
63*904af04dSJiri Benc #include <net/ip_tunnels.h>
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds #include <asm/uaccess.h>
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
681da177e4SLinus Torvalds #include <linux/sysctl.h>
691da177e4SLinus Torvalds #endif
701da177e4SLinus Torvalds 
71afc154e9SHannes Frederic Sowa enum rt6_nud_state {
727e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
737e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
747e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
75afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
76afc154e9SHannes Frederic Sowa };
77afc154e9SHannes Frederic Sowa 
7883a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort);
791da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
800dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
81ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
821da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
831da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
841da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
851da177e4SLinus Torvalds 				       struct net_device *dev, int how);
86569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
89aad88724SEric Dumazet static int		ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb);
907150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
91aad88724SEric Dumazet static int		ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb);
921da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
936700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
946700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
956700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
966700c270SDavid S. Miller 					struct sk_buff *skb);
974b32b5adSMartin KaFai Lau static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
9852bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
991da177e4SLinus Torvalds 
10070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
101efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
102b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
103b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
10495c96174SEric Dumazet 					   unsigned int pref);
105efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
106b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
107b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
10870ceb4f5SYOSHIFUJI Hideaki #endif
10970ceb4f5SYOSHIFUJI Hideaki 
1108d0b94afSMartin KaFai Lau struct uncached_list {
1118d0b94afSMartin KaFai Lau 	spinlock_t		lock;
1128d0b94afSMartin KaFai Lau 	struct list_head	head;
1138d0b94afSMartin KaFai Lau };
1148d0b94afSMartin KaFai Lau 
1158d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1168d0b94afSMartin KaFai Lau 
1178d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt)
1188d0b94afSMartin KaFai Lau {
1198d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1208d0b94afSMartin KaFai Lau 
1218d0b94afSMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
1228d0b94afSMartin KaFai Lau 	rt->rt6i_uncached_list = ul;
1238d0b94afSMartin KaFai Lau 
1248d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
1258d0b94afSMartin KaFai Lau 	list_add_tail(&rt->rt6i_uncached, &ul->head);
1268d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
1278d0b94afSMartin KaFai Lau }
1288d0b94afSMartin KaFai Lau 
1298d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt)
1308d0b94afSMartin KaFai Lau {
1318d0b94afSMartin KaFai Lau 	if (!list_empty(&rt->rt6i_uncached)) {
1328d0b94afSMartin KaFai Lau 		struct uncached_list *ul = rt->rt6i_uncached_list;
1338d0b94afSMartin KaFai Lau 
1348d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1358d0b94afSMartin KaFai Lau 		list_del(&rt->rt6i_uncached);
1368d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1378d0b94afSMartin KaFai Lau 	}
1388d0b94afSMartin KaFai Lau }
1398d0b94afSMartin KaFai Lau 
1408d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
1418d0b94afSMartin KaFai Lau {
1428d0b94afSMartin KaFai Lau 	struct net_device *loopback_dev = net->loopback_dev;
1438d0b94afSMartin KaFai Lau 	int cpu;
1448d0b94afSMartin KaFai Lau 
1458d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
1468d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
1478d0b94afSMartin KaFai Lau 		struct rt6_info *rt;
1488d0b94afSMartin KaFai Lau 
1498d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1508d0b94afSMartin KaFai Lau 		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
1518d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
1528d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
1538d0b94afSMartin KaFai Lau 
1548d0b94afSMartin KaFai Lau 			if (rt_idev && (rt_idev->dev == dev || !dev) &&
1558d0b94afSMartin KaFai Lau 			    rt_idev->dev != loopback_dev) {
1568d0b94afSMartin KaFai Lau 				rt->rt6i_idev = in6_dev_get(loopback_dev);
1578d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
1588d0b94afSMartin KaFai Lau 			}
1598d0b94afSMartin KaFai Lau 
1608d0b94afSMartin KaFai Lau 			if (rt_dev && (rt_dev == dev || !dev) &&
1618d0b94afSMartin KaFai Lau 			    rt_dev != loopback_dev) {
1628d0b94afSMartin KaFai Lau 				rt->dst.dev = loopback_dev;
1638d0b94afSMartin KaFai Lau 				dev_hold(rt->dst.dev);
1648d0b94afSMartin KaFai Lau 				dev_put(rt_dev);
1658d0b94afSMartin KaFai Lau 			}
1668d0b94afSMartin KaFai Lau 		}
1678d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1688d0b94afSMartin KaFai Lau 	}
1698d0b94afSMartin KaFai Lau }
1708d0b94afSMartin KaFai Lau 
171d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
172d52d3997SMartin KaFai Lau {
173d52d3997SMartin KaFai Lau 	return dst_metrics_write_ptr(rt->dst.from);
174d52d3997SMartin KaFai Lau }
175d52d3997SMartin KaFai Lau 
17606582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
17706582540SDavid S. Miller {
17806582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
17906582540SDavid S. Miller 
180d52d3997SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU)
181d52d3997SMartin KaFai Lau 		return rt6_pcpu_cow_metrics(rt);
182d52d3997SMartin KaFai Lau 	else if (rt->rt6i_flags & RTF_CACHE)
1834b32b5adSMartin KaFai Lau 		return NULL;
1844b32b5adSMartin KaFai Lau 	else
1853b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
18606582540SDavid S. Miller }
18706582540SDavid S. Miller 
188f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
189f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
190f894cbf8SDavid S. Miller 					     const void *daddr)
19139232973SDavid S. Miller {
19239232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
19339232973SDavid S. Miller 
194a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
19539232973SDavid S. Miller 		return (const void *) p;
196f894cbf8SDavid S. Miller 	else if (skb)
197f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
19839232973SDavid S. Miller 	return daddr;
19939232973SDavid S. Miller }
20039232973SDavid S. Miller 
201f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
202f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
203f894cbf8SDavid S. Miller 					  const void *daddr)
204d3aaeb38SDavid S. Miller {
20539232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
20639232973SDavid S. Miller 	struct neighbour *n;
20739232973SDavid S. Miller 
208f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
2098e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
210f83c7790SDavid S. Miller 	if (n)
211f83c7790SDavid S. Miller 		return n;
212f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
213f83c7790SDavid S. Miller }
214f83c7790SDavid S. Miller 
2159a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2161da177e4SLinus Torvalds 	.family			=	AF_INET6,
2171da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2181da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2191da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2200dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
221ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
22206582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
2231da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2241da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2251da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2261da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2271da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2286e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2291ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
230d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
2311da177e4SLinus Torvalds };
2321da177e4SLinus Torvalds 
233ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
234ec831ea7SRoland Dreier {
235618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
236618f9bc7SSteffen Klassert 
237618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
238ec831ea7SRoland Dreier }
239ec831ea7SRoland Dreier 
2406700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2416700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
24214e50e57SDavid S. Miller {
24314e50e57SDavid S. Miller }
24414e50e57SDavid S. Miller 
2456700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2466700c270SDavid S. Miller 				      struct sk_buff *skb)
247b587ee3bSDavid S. Miller {
248b587ee3bSDavid S. Miller }
249b587ee3bSDavid S. Miller 
2500972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
2510972ddb2SHeld Bernhard 					 unsigned long old)
2520972ddb2SHeld Bernhard {
2530972ddb2SHeld Bernhard 	return NULL;
2540972ddb2SHeld Bernhard }
2550972ddb2SHeld Bernhard 
25614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
25714e50e57SDavid S. Miller 	.family			=	AF_INET6,
25814e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
25914e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
260ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
261214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
26214e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
263b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2640972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
265d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
26614e50e57SDavid S. Miller };
26714e50e57SDavid S. Miller 
26862fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
26914edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
27062fa8a84SDavid S. Miller };
27162fa8a84SDavid S. Miller 
272fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2731da177e4SLinus Torvalds 	.dst = {
2741da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2751da177e4SLinus Torvalds 		.__use		= 1,
2762c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2771da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2781da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2791da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2801da177e4SLinus Torvalds 	},
2811da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2824f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2831da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2841da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2851da177e4SLinus Torvalds };
2861da177e4SLinus Torvalds 
287101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
288101367c2SThomas Graf 
289fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
290101367c2SThomas Graf 	.dst = {
291101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
292101367c2SThomas Graf 		.__use		= 1,
2932c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
294101367c2SThomas Graf 		.error		= -EACCES,
2959ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2969ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
297101367c2SThomas Graf 	},
298101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2994f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
300101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
301101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
302101367c2SThomas Graf };
303101367c2SThomas Graf 
304fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
305101367c2SThomas Graf 	.dst = {
306101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
307101367c2SThomas Graf 		.__use		= 1,
3082c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
309101367c2SThomas Graf 		.error		= -EINVAL,
310352e512cSHerbert Xu 		.input		= dst_discard,
311aad88724SEric Dumazet 		.output		= dst_discard_sk,
312101367c2SThomas Graf 	},
313101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3144f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
315101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
316101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
317101367c2SThomas Graf };
318101367c2SThomas Graf 
319101367c2SThomas Graf #endif
320101367c2SThomas Graf 
3211da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
322d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net,
323957c665fSDavid S. Miller 					struct net_device *dev,
3248b96d22dSDavid S. Miller 					int flags,
3258b96d22dSDavid S. Miller 					struct fib6_table *table)
3261da177e4SLinus Torvalds {
32797bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3286f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
329cf911662SDavid S. Miller 
33097bab73fSDavid S. Miller 	if (rt) {
3318104891bSSteffen Klassert 		struct dst_entry *dst = &rt->dst;
3328104891bSSteffen Klassert 
3338104891bSSteffen Klassert 		memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
33451ebd318SNicolas Dichtel 		INIT_LIST_HEAD(&rt->rt6i_siblings);
3358d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&rt->rt6i_uncached);
33697bab73fSDavid S. Miller 	}
337cf911662SDavid S. Miller 	return rt;
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
340d52d3997SMartin KaFai Lau static struct rt6_info *ip6_dst_alloc(struct net *net,
341d52d3997SMartin KaFai Lau 				      struct net_device *dev,
342d52d3997SMartin KaFai Lau 				      int flags,
343d52d3997SMartin KaFai Lau 				      struct fib6_table *table)
344d52d3997SMartin KaFai Lau {
345d52d3997SMartin KaFai Lau 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags, table);
346d52d3997SMartin KaFai Lau 
347d52d3997SMartin KaFai Lau 	if (rt) {
348d52d3997SMartin KaFai Lau 		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
349d52d3997SMartin KaFai Lau 		if (rt->rt6i_pcpu) {
350d52d3997SMartin KaFai Lau 			int cpu;
351d52d3997SMartin KaFai Lau 
352d52d3997SMartin KaFai Lau 			for_each_possible_cpu(cpu) {
353d52d3997SMartin KaFai Lau 				struct rt6_info **p;
354d52d3997SMartin KaFai Lau 
355d52d3997SMartin KaFai Lau 				p = per_cpu_ptr(rt->rt6i_pcpu, cpu);
356d52d3997SMartin KaFai Lau 				/* no one shares rt */
357d52d3997SMartin KaFai Lau 				*p =  NULL;
358d52d3997SMartin KaFai Lau 			}
359d52d3997SMartin KaFai Lau 		} else {
360d52d3997SMartin KaFai Lau 			dst_destroy((struct dst_entry *)rt);
361d52d3997SMartin KaFai Lau 			return NULL;
362d52d3997SMartin KaFai Lau 		}
363d52d3997SMartin KaFai Lau 	}
364d52d3997SMartin KaFai Lau 
365d52d3997SMartin KaFai Lau 	return rt;
366d52d3997SMartin KaFai Lau }
367d52d3997SMartin KaFai Lau 
3681da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3691da177e4SLinus Torvalds {
3701da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
371ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
3728d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3731da177e4SLinus Torvalds 
3748e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
375d52d3997SMartin KaFai Lau 	free_percpu(rt->rt6i_pcpu);
3768d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
3778d0b94afSMartin KaFai Lau 
3788d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
37938308473SDavid S. Miller 	if (idev) {
3801da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3811da177e4SLinus Torvalds 		in6_dev_put(idev);
3821da177e4SLinus Torvalds 	}
3831716a961SGao feng 
384ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
385ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
386b3419363SDavid S. Miller }
387b3419363SDavid S. Miller 
3881da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3891da177e4SLinus Torvalds 			   int how)
3901da177e4SLinus Torvalds {
3911da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3921da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3935a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
394c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3951da177e4SLinus Torvalds 
39697cac082SDavid S. Miller 	if (dev != loopback_dev) {
39797cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3985a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3995a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
40038308473SDavid S. Miller 			if (loopback_idev) {
4011da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
4021da177e4SLinus Torvalds 				in6_dev_put(idev);
4031da177e4SLinus Torvalds 			}
4041da177e4SLinus Torvalds 		}
40597cac082SDavid S. Miller 	}
4061da177e4SLinus Torvalds }
4071da177e4SLinus Torvalds 
408a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4091da177e4SLinus Torvalds {
4101716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4111716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
412a50feda5SEric Dumazet 			return true;
4131716a961SGao feng 	} else if (rt->dst.from) {
4143fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
4151716a961SGao feng 	}
416a50feda5SEric Dumazet 	return false;
4171da177e4SLinus Torvalds }
4181da177e4SLinus Torvalds 
41951ebd318SNicolas Dichtel /* Multipath route selection:
42051ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
42151ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
42251ebd318SNicolas Dichtel  */
42351ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
42451ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
42551ebd318SNicolas Dichtel {
42651ebd318SNicolas Dichtel 	unsigned int val = fl6->flowi6_proto;
42751ebd318SNicolas Dichtel 
428c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->daddr);
429c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->saddr);
43051ebd318SNicolas Dichtel 
43151ebd318SNicolas Dichtel 	/* Work only if this not encapsulated */
43251ebd318SNicolas Dichtel 	switch (fl6->flowi6_proto) {
43351ebd318SNicolas Dichtel 	case IPPROTO_UDP:
43451ebd318SNicolas Dichtel 	case IPPROTO_TCP:
43551ebd318SNicolas Dichtel 	case IPPROTO_SCTP:
436b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_sport;
437b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_dport;
43851ebd318SNicolas Dichtel 		break;
43951ebd318SNicolas Dichtel 
44051ebd318SNicolas Dichtel 	case IPPROTO_ICMPV6:
441b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_type;
442b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_code;
44351ebd318SNicolas Dichtel 		break;
44451ebd318SNicolas Dichtel 	}
44551ebd318SNicolas Dichtel 	/* RFC6438 recommands to use flowlabel */
446b3ce5ae1SNicolas Dichtel 	val ^= (__force u32)fl6->flowlabel;
44751ebd318SNicolas Dichtel 
44851ebd318SNicolas Dichtel 	/* Perhaps, we need to tune, this function? */
44951ebd318SNicolas Dichtel 	val = val ^ (val >> 7) ^ (val >> 12);
45051ebd318SNicolas Dichtel 	return val % candidate_count;
45151ebd318SNicolas Dichtel }
45251ebd318SNicolas Dichtel 
45351ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
45452bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
45552bd4c0cSNicolas Dichtel 					     int strict)
45651ebd318SNicolas Dichtel {
45751ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
45851ebd318SNicolas Dichtel 	int route_choosen;
45951ebd318SNicolas Dichtel 
46051ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
46151ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
46251ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
46351ebd318SNicolas Dichtel 	 */
46451ebd318SNicolas Dichtel 	if (route_choosen)
46551ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
46651ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
46751ebd318SNicolas Dichtel 			route_choosen--;
46851ebd318SNicolas Dichtel 			if (route_choosen == 0) {
46952bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
47052bd4c0cSNicolas Dichtel 					break;
47151ebd318SNicolas Dichtel 				match = sibling;
47251ebd318SNicolas Dichtel 				break;
47351ebd318SNicolas Dichtel 			}
47451ebd318SNicolas Dichtel 		}
47551ebd318SNicolas Dichtel 	return match;
47651ebd318SNicolas Dichtel }
47751ebd318SNicolas Dichtel 
4781da177e4SLinus Torvalds /*
479c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4801da177e4SLinus Torvalds  */
4811da177e4SLinus Torvalds 
4828ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4838ed67789SDaniel Lezcano 						    struct rt6_info *rt,
484b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4851da177e4SLinus Torvalds 						    int oif,
486d420895eSYOSHIFUJI Hideaki 						    int flags)
4871da177e4SLinus Torvalds {
4881da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4891da177e4SLinus Torvalds 	struct rt6_info *sprt;
4901da177e4SLinus Torvalds 
491dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
492dd3abc4eSYOSHIFUJI Hideaki 		goto out;
493dd3abc4eSYOSHIFUJI Hideaki 
494d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
495d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
496dd3abc4eSYOSHIFUJI Hideaki 
497dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4981da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4991da177e4SLinus Torvalds 				return sprt;
5001da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
50138308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
5021da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
503d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
5041da177e4SLinus Torvalds 						continue;
5051da177e4SLinus Torvalds 					if (local && (!oif ||
5061da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
5071da177e4SLinus Torvalds 						continue;
5081da177e4SLinus Torvalds 				}
5091da177e4SLinus Torvalds 				local = sprt;
5101da177e4SLinus Torvalds 			}
511dd3abc4eSYOSHIFUJI Hideaki 		} else {
512dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
513dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
514dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
515dd3abc4eSYOSHIFUJI Hideaki 		}
5161da177e4SLinus Torvalds 	}
5171da177e4SLinus Torvalds 
518dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
5191da177e4SLinus Torvalds 		if (local)
5201da177e4SLinus Torvalds 			return local;
5211da177e4SLinus Torvalds 
522d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
5238ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
5241da177e4SLinus Torvalds 	}
525dd3abc4eSYOSHIFUJI Hideaki out:
5261da177e4SLinus Torvalds 	return rt;
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds 
52927097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
530c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
531c2f17e82SHannes Frederic Sowa 	struct work_struct work;
532c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
533c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
534c2f17e82SHannes Frederic Sowa };
535c2f17e82SHannes Frederic Sowa 
536c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
537c2f17e82SHannes Frederic Sowa {
538c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
539c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
540c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
541c2f17e82SHannes Frederic Sowa 
542c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
543ab450605SJiri Benc 	ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL, NULL);
544c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
545662f5533SMichael Büsch 	kfree(work);
546c2f17e82SHannes Frederic Sowa }
547c2f17e82SHannes Frederic Sowa 
54827097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
54927097255SYOSHIFUJI Hideaki {
550990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
551f2c31e32SEric Dumazet 	struct neighbour *neigh;
55227097255SYOSHIFUJI Hideaki 	/*
55327097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
55427097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
55527097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
55627097255SYOSHIFUJI Hideaki 	 *
55727097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
55827097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
55927097255SYOSHIFUJI Hideaki 	 */
5602152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
561fdd6681dSAmerigo Wang 		return;
5622152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5632152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5642152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5658d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5668d6c31bfSMartin KaFai Lau 			goto out;
5678d6c31bfSMartin KaFai Lau 
568990edb42SMartin KaFai Lau 		work = NULL;
5692152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
570990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
571990edb42SMartin KaFai Lau 		    time_after(jiffies,
572990edb42SMartin KaFai Lau 			       neigh->updated +
573990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
574c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
575990edb42SMartin KaFai Lau 			if (work)
5767e980569SJiri Benc 				__neigh_set_probe_once(neigh);
577990edb42SMartin KaFai Lau 		}
578c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
579990edb42SMartin KaFai Lau 	} else {
580990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
581990edb42SMartin KaFai Lau 	}
582c2f17e82SHannes Frederic Sowa 
583c2f17e82SHannes Frederic Sowa 	if (work) {
584c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
585c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
586c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
587c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
588c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
589c2f17e82SHannes Frederic Sowa 	}
590990edb42SMartin KaFai Lau 
5918d6c31bfSMartin KaFai Lau out:
5922152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
593f2c31e32SEric Dumazet }
59427097255SYOSHIFUJI Hideaki #else
59527097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
59627097255SYOSHIFUJI Hideaki {
59727097255SYOSHIFUJI Hideaki }
59827097255SYOSHIFUJI Hideaki #endif
59927097255SYOSHIFUJI Hideaki 
6001da177e4SLinus Torvalds /*
601554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
6021da177e4SLinus Torvalds  */
603b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
6041da177e4SLinus Torvalds {
605d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
606161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
607554cfb7eSYOSHIFUJI Hideaki 		return 2;
608161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
609161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
610161980f4SDavid S. Miller 		return 1;
611554cfb7eSYOSHIFUJI Hideaki 	return 0;
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds 
614afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
6151da177e4SLinus Torvalds {
616f2c31e32SEric Dumazet 	struct neighbour *neigh;
617afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
618f2c31e32SEric Dumazet 
6194d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
6204d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
621afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
622145a3621SYOSHIFUJI Hideaki / 吉藤英明 
623145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
624145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
625145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
626145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
627554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
628afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
629398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
630a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
631afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6327e980569SJiri Benc 		else
6337e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
634398bcbebSYOSHIFUJI Hideaki #endif
635145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
636afc154e9SHannes Frederic Sowa 	} else {
637afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6387e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
639a5a81f0bSPaul Marks 	}
640145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
641145a3621SYOSHIFUJI Hideaki / 吉藤英明 
642a5a81f0bSPaul Marks 	return ret;
6431da177e4SLinus Torvalds }
6441da177e4SLinus Torvalds 
645554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
646554cfb7eSYOSHIFUJI Hideaki 			   int strict)
647554cfb7eSYOSHIFUJI Hideaki {
648a5a81f0bSPaul Marks 	int m;
6494d0c5911SYOSHIFUJI Hideaki 
6504d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
65177d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
652afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
653ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
654ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
655ebacaaa0SYOSHIFUJI Hideaki #endif
656afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
657afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
658afc154e9SHannes Frederic Sowa 		if (n < 0)
659afc154e9SHannes Frederic Sowa 			return n;
660afc154e9SHannes Frederic Sowa 	}
661554cfb7eSYOSHIFUJI Hideaki 	return m;
662554cfb7eSYOSHIFUJI Hideaki }
663554cfb7eSYOSHIFUJI Hideaki 
664f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
665afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
666afc154e9SHannes Frederic Sowa 				   bool *do_rr)
667554cfb7eSYOSHIFUJI Hideaki {
668554cfb7eSYOSHIFUJI Hideaki 	int m;
669afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
67035103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
67135103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
67235103d11SAndy Gospodarek 
67335103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
67435103d11SAndy Gospodarek 	    idev->cnf.ignore_routes_with_linkdown)
67535103d11SAndy Gospodarek 		goto out;
676554cfb7eSYOSHIFUJI Hideaki 
677554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
678f11e6659SDavid S. Miller 		goto out;
679554cfb7eSYOSHIFUJI Hideaki 
680554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6817e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
682afc154e9SHannes Frederic Sowa 		match_do_rr = true;
683afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6847e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
685f11e6659SDavid S. Miller 		goto out;
6861da177e4SLinus Torvalds 	}
687f11e6659SDavid S. Miller 
688afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
689afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
690afc154e9SHannes Frederic Sowa 
6917e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
692afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
693afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
694afc154e9SHannes Frederic Sowa 		*mpri = m;
695afc154e9SHannes Frederic Sowa 		match = rt;
696afc154e9SHannes Frederic Sowa 	}
697f11e6659SDavid S. Miller out:
698f11e6659SDavid S. Miller 	return match;
6991da177e4SLinus Torvalds }
7001da177e4SLinus Torvalds 
701f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
702f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
703afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
704afc154e9SHannes Frederic Sowa 				     bool *do_rr)
705f11e6659SDavid S. Miller {
7069fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
707f11e6659SDavid S. Miller 	int mpri = -1;
708f11e6659SDavid S. Miller 
709f11e6659SDavid S. Miller 	match = NULL;
7109fbdcfafSSteffen Klassert 	cont = NULL;
7119fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
7129fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7139fbdcfafSSteffen Klassert 			cont = rt;
7149fbdcfafSSteffen Klassert 			break;
7159fbdcfafSSteffen Klassert 		}
7169fbdcfafSSteffen Klassert 
717afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7189fbdcfafSSteffen Klassert 	}
7199fbdcfafSSteffen Klassert 
7209fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
7219fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7229fbdcfafSSteffen Klassert 			cont = rt;
7239fbdcfafSSteffen Klassert 			break;
7249fbdcfafSSteffen Klassert 		}
7259fbdcfafSSteffen Klassert 
7269fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7279fbdcfafSSteffen Klassert 	}
7289fbdcfafSSteffen Klassert 
7299fbdcfafSSteffen Klassert 	if (match || !cont)
7309fbdcfafSSteffen Klassert 		return match;
7319fbdcfafSSteffen Klassert 
7329fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
733afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
734f11e6659SDavid S. Miller 
735f11e6659SDavid S. Miller 	return match;
736f11e6659SDavid S. Miller }
737f11e6659SDavid S. Miller 
738f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
739f11e6659SDavid S. Miller {
740f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7418ed67789SDaniel Lezcano 	struct net *net;
742afc154e9SHannes Frederic Sowa 	bool do_rr = false;
743f11e6659SDavid S. Miller 
744f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
745f11e6659SDavid S. Miller 	if (!rt0)
746f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
747f11e6659SDavid S. Miller 
748afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
749afc154e9SHannes Frederic Sowa 			     &do_rr);
750f11e6659SDavid S. Miller 
751afc154e9SHannes Frederic Sowa 	if (do_rr) {
752d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
753f11e6659SDavid S. Miller 
754554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
755f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
756f11e6659SDavid S. Miller 			next = fn->leaf;
757f11e6659SDavid S. Miller 
758f11e6659SDavid S. Miller 		if (next != rt0)
759f11e6659SDavid S. Miller 			fn->rr_ptr = next;
760554cfb7eSYOSHIFUJI Hideaki 	}
761554cfb7eSYOSHIFUJI Hideaki 
762d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
763a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7641da177e4SLinus Torvalds }
7651da177e4SLinus Torvalds 
7668b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7678b9df265SMartin KaFai Lau {
7688b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7698b9df265SMartin KaFai Lau }
7708b9df265SMartin KaFai Lau 
77170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
77270ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
773b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
77470ceb4f5SYOSHIFUJI Hideaki {
775c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
77670ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
77770ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
77870ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7794bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
78070ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
78170ceb4f5SYOSHIFUJI Hideaki 
78270ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
78370ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78470ceb4f5SYOSHIFUJI Hideaki 	}
78570ceb4f5SYOSHIFUJI Hideaki 
78670ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
78770ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
78870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
79070ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
79170ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
79270ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
79370ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
79470ceb4f5SYOSHIFUJI Hideaki 		}
79570ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
79670ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
79770ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
79870ceb4f5SYOSHIFUJI Hideaki 		}
79970ceb4f5SYOSHIFUJI Hideaki 	}
80070ceb4f5SYOSHIFUJI Hideaki 
80170ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
80270ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
8033933fc95SJens Rosenboom 		return -EINVAL;
80470ceb4f5SYOSHIFUJI Hideaki 
8054bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
80670ceb4f5SYOSHIFUJI Hideaki 
80770ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
80870ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
80970ceb4f5SYOSHIFUJI Hideaki 	else {
81070ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
81170ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
81270ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
81370ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
81470ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
81570ceb4f5SYOSHIFUJI Hideaki 	}
81670ceb4f5SYOSHIFUJI Hideaki 
817f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
818f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
819f104a567SDuan Jiong 	else
820f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
821f104a567SDuan Jiong 					gwaddr, dev->ifindex);
82270ceb4f5SYOSHIFUJI Hideaki 
82370ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
824e0a1ad73SThomas Graf 		ip6_del_rt(rt);
82570ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
82670ceb4f5SYOSHIFUJI Hideaki 	}
82770ceb4f5SYOSHIFUJI Hideaki 
82870ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
829efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
83070ceb4f5SYOSHIFUJI Hideaki 					pref);
83170ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
83270ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
83370ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
83470ceb4f5SYOSHIFUJI Hideaki 
83570ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8361716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8371716a961SGao feng 			rt6_clean_expires(rt);
8381716a961SGao feng 		else
8391716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8401716a961SGao feng 
84194e187c0SAmerigo Wang 		ip6_rt_put(rt);
84270ceb4f5SYOSHIFUJI Hideaki 	}
84370ceb4f5SYOSHIFUJI Hideaki 	return 0;
84470ceb4f5SYOSHIFUJI Hideaki }
84570ceb4f5SYOSHIFUJI Hideaki #endif
84670ceb4f5SYOSHIFUJI Hideaki 
847a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
848a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
849a3c00e46SMartin KaFai Lau {
850a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
851a3c00e46SMartin KaFai Lau 	while (1) {
852a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
853a3c00e46SMartin KaFai Lau 			return NULL;
854a3c00e46SMartin KaFai Lau 		pn = fn->parent;
855a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
856a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
857a3c00e46SMartin KaFai Lau 		else
858a3c00e46SMartin KaFai Lau 			fn = pn;
859a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
860a3c00e46SMartin KaFai Lau 			return fn;
861a3c00e46SMartin KaFai Lau 	}
862a3c00e46SMartin KaFai Lau }
863c71099acSThomas Graf 
8648ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8658ed67789SDaniel Lezcano 					     struct fib6_table *table,
8664c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8671da177e4SLinus Torvalds {
8681da177e4SLinus Torvalds 	struct fib6_node *fn;
8691da177e4SLinus Torvalds 	struct rt6_info *rt;
8701da177e4SLinus Torvalds 
871c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8724c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
873c71099acSThomas Graf restart:
874c71099acSThomas Graf 	rt = fn->leaf;
8754c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
87651ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
87752bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
878a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
879a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
880a3c00e46SMartin KaFai Lau 		if (fn)
881a3c00e46SMartin KaFai Lau 			goto restart;
882a3c00e46SMartin KaFai Lau 	}
883d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
884c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8851da177e4SLinus Torvalds 	return rt;
886c71099acSThomas Graf 
887c71099acSThomas Graf }
888c71099acSThomas Graf 
889ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
890ea6e574eSFlorian Westphal 				    int flags)
891ea6e574eSFlorian Westphal {
892ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
893ea6e574eSFlorian Westphal }
894ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
895ea6e574eSFlorian Westphal 
8969acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8979acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
898c71099acSThomas Graf {
8994c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
9004c9483b2SDavid S. Miller 		.flowi6_oif = oif,
9014c9483b2SDavid S. Miller 		.daddr = *daddr,
902c71099acSThomas Graf 	};
903c71099acSThomas Graf 	struct dst_entry *dst;
90477d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
905c71099acSThomas Graf 
906adaa70bbSThomas Graf 	if (saddr) {
9074c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
908adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
909adaa70bbSThomas Graf 	}
910adaa70bbSThomas Graf 
9114c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
912c71099acSThomas Graf 	if (dst->error == 0)
913c71099acSThomas Graf 		return (struct rt6_info *) dst;
914c71099acSThomas Graf 
915c71099acSThomas Graf 	dst_release(dst);
916c71099acSThomas Graf 
9171da177e4SLinus Torvalds 	return NULL;
9181da177e4SLinus Torvalds }
9197159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
9207159039aSYOSHIFUJI Hideaki 
921c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
9221da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
9231da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
9241da177e4SLinus Torvalds    be destroyed.
9251da177e4SLinus Torvalds  */
9261da177e4SLinus Torvalds 
927e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
928e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9291da177e4SLinus Torvalds {
9301da177e4SLinus Torvalds 	int err;
931c71099acSThomas Graf 	struct fib6_table *table;
9321da177e4SLinus Torvalds 
933c71099acSThomas Graf 	table = rt->rt6i_table;
934c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
935e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
936c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 	return err;
9391da177e4SLinus Torvalds }
9401da177e4SLinus Torvalds 
94140e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
94240e22e8fSThomas Graf {
943e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
944e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
945e715b6d3SFlorian Westphal 
946e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
94740e22e8fSThomas Graf }
94840e22e8fSThomas Graf 
9498b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
95021efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
951b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9521da177e4SLinus Torvalds {
9531da177e4SLinus Torvalds 	struct rt6_info *rt;
9541da177e4SLinus Torvalds 
9551da177e4SLinus Torvalds 	/*
9561da177e4SLinus Torvalds 	 *	Clone the route.
9571da177e4SLinus Torvalds 	 */
9581da177e4SLinus Torvalds 
959d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
96083a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9611da177e4SLinus Torvalds 
962d52d3997SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev,
96383a09abdSMartin KaFai Lau 			     0, ort->rt6i_table);
96483a09abdSMartin KaFai Lau 
96583a09abdSMartin KaFai Lau 	if (!rt)
96683a09abdSMartin KaFai Lau 		return NULL;
96783a09abdSMartin KaFai Lau 
96883a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9698b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
97083a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
97183a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
97283a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
97383a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9748b9df265SMartin KaFai Lau 
9758b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
976bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
97721efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
97858c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9791da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9801da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9814e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9821da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9831da177e4SLinus Torvalds 		}
9841da177e4SLinus Torvalds #endif
98595a9a5baSYOSHIFUJI Hideaki 	}
98695a9a5baSYOSHIFUJI Hideaki 
987299d9939SYOSHIFUJI Hideaki 	return rt;
988299d9939SYOSHIFUJI Hideaki }
989299d9939SYOSHIFUJI Hideaki 
990d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
991d52d3997SMartin KaFai Lau {
992d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
993d52d3997SMartin KaFai Lau 
994d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
995d52d3997SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags,
996d52d3997SMartin KaFai Lau 				  rt->rt6i_table);
997d52d3997SMartin KaFai Lau 
998d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
999d52d3997SMartin KaFai Lau 		return NULL;
1000d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
1001d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
1002d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
1003d52d3997SMartin KaFai Lau 	return pcpu_rt;
1004d52d3997SMartin KaFai Lau }
1005d52d3997SMartin KaFai Lau 
1006d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
1007d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
1008d52d3997SMartin KaFai Lau {
1009d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
1010d52d3997SMartin KaFai Lau 
1011d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
1012d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
1013d52d3997SMartin KaFai Lau 
1014d52d3997SMartin KaFai Lau 	if (pcpu_rt)
1015d52d3997SMartin KaFai Lau 		goto done;
1016d52d3997SMartin KaFai Lau 
1017d52d3997SMartin KaFai Lau 	pcpu_rt = ip6_rt_pcpu_alloc(rt);
1018d52d3997SMartin KaFai Lau 	if (!pcpu_rt) {
1019d52d3997SMartin KaFai Lau 		struct net *net = dev_net(rt->dst.dev);
1020d52d3997SMartin KaFai Lau 
1021d52d3997SMartin KaFai Lau 		pcpu_rt = net->ipv6.ip6_null_entry;
1022d52d3997SMartin KaFai Lau 		goto done;
1023d52d3997SMartin KaFai Lau 	}
1024d52d3997SMartin KaFai Lau 
1025d52d3997SMartin KaFai Lau 	prev = cmpxchg(p, NULL, pcpu_rt);
1026d52d3997SMartin KaFai Lau 	if (prev) {
1027d52d3997SMartin KaFai Lau 		/* If someone did it before us, return prev instead */
1028d52d3997SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
1029d52d3997SMartin KaFai Lau 		pcpu_rt = prev;
1030d52d3997SMartin KaFai Lau 	}
1031d52d3997SMartin KaFai Lau 
1032d52d3997SMartin KaFai Lau done:
1033d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1034d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
1035d52d3997SMartin KaFai Lau 	return pcpu_rt;
1036d52d3997SMartin KaFai Lau }
1037d52d3997SMartin KaFai Lau 
10388ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
10394c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
10401da177e4SLinus Torvalds {
1041367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
104245e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1043c71099acSThomas Graf 	int strict = 0;
10441da177e4SLinus Torvalds 
104577d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1046367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1047367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10481da177e4SLinus Torvalds 
1049c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10501da177e4SLinus Torvalds 
10514c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1052367efcb9SMartin KaFai Lau 	saved_fn = fn;
10531da177e4SLinus Torvalds 
1054a3c00e46SMartin KaFai Lau redo_rt6_select:
1055367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
105652bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1057367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1058a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1059a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1060a3c00e46SMartin KaFai Lau 		if (fn)
1061a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1062367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1063367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1064367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1065367efcb9SMartin KaFai Lau 			fn = saved_fn;
1066367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1067367efcb9SMartin KaFai Lau 		}
1068a3c00e46SMartin KaFai Lau 	}
1069a3c00e46SMartin KaFai Lau 
1070d52d3997SMartin KaFai Lau 
1071d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10723da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1073c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10741da177e4SLinus Torvalds 
1075d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1076d52d3997SMartin KaFai Lau 		return rt;
10773da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10783da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10793da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10803da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10813da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10823da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
10833da59bd9SMartin KaFai Lau 		 */
1084c71099acSThomas Graf 
10853da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
10863da59bd9SMartin KaFai Lau 
1087d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1088d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1089d52d3997SMartin KaFai Lau 
10903da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
10913da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
10923da59bd9SMartin KaFai Lau 
10933da59bd9SMartin KaFai Lau 		if (uncached_rt)
10948d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
10953da59bd9SMartin KaFai Lau 		else
10963da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1097d52d3997SMartin KaFai Lau 
10983da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
10993da59bd9SMartin KaFai Lau 		return uncached_rt;
11003da59bd9SMartin KaFai Lau 
1101d52d3997SMartin KaFai Lau 	} else {
1102d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1103d52d3997SMartin KaFai Lau 
1104d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1105d52d3997SMartin KaFai Lau 
1106d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1107d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1108d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1109d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1110d52d3997SMartin KaFai Lau 
1111d52d3997SMartin KaFai Lau 		return pcpu_rt;
1112d52d3997SMartin KaFai Lau 	}
1113c71099acSThomas Graf }
1114c71099acSThomas Graf 
11158ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11164c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11174acad72dSPavel Emelyanov {
11184c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11194acad72dSPavel Emelyanov }
11204acad72dSPavel Emelyanov 
112172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
112272331bc0SShmulik Ladkani 						struct net_device *dev,
112372331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
112472331bc0SShmulik Ladkani {
112572331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
112672331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
112772331bc0SShmulik Ladkani 
112872331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
112972331bc0SShmulik Ladkani }
113072331bc0SShmulik Ladkani 
1131c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1132c71099acSThomas Graf {
1133b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1134c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1135adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1136*904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
11374c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
11384c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
11394c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11404c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11416502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11424c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11434c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1144c71099acSThomas Graf 	};
1145adaa70bbSThomas Graf 
1146*904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
1147*904af04dSJiri Benc 	if (tun_info && tun_info->mode == IP_TUNNEL_INFO_RX)
1148*904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
114906e9d040SJiri Benc 	skb_dst_drop(skb);
115072331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1151c71099acSThomas Graf }
1152c71099acSThomas Graf 
11538ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11544c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1155c71099acSThomas Graf {
11564c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1157c71099acSThomas Graf }
1158c71099acSThomas Graf 
11599c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
11604c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1161c71099acSThomas Graf {
1162c71099acSThomas Graf 	int flags = 0;
1163c71099acSThomas Graf 
11641fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
11654dc27d1cSDavid McCullough 
11664c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
116777d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1168c71099acSThomas Graf 
11694c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
1170adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
11710c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
11720c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1173adaa70bbSThomas Graf 
11744c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
11751da177e4SLinus Torvalds }
11767159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
11771da177e4SLinus Torvalds 
11782774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
117914e50e57SDavid S. Miller {
11805c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
118114e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
118214e50e57SDavid S. Miller 
1183f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
118414e50e57SDavid S. Miller 	if (rt) {
1185d8d1f30bSChangli Gao 		new = &rt->dst;
118614e50e57SDavid S. Miller 
11878104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
11888104891bSSteffen Klassert 
118914e50e57SDavid S. Miller 		new->__use = 1;
1190352e512cSHerbert Xu 		new->input = dst_discard;
1191aad88724SEric Dumazet 		new->output = dst_discard_sk;
119214e50e57SDavid S. Miller 
119321efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
119421efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
119521efcfa0SEric Dumazet 		else
1196defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
119714e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
119814e50e57SDavid S. Miller 		if (rt->rt6i_idev)
119914e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
120014e50e57SDavid S. Miller 
12014e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
12021716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
120314e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
120414e50e57SDavid S. Miller 
120514e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
120614e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
120714e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
120814e50e57SDavid S. Miller #endif
120914e50e57SDavid S. Miller 
121014e50e57SDavid S. Miller 		dst_free(new);
121114e50e57SDavid S. Miller 	}
121214e50e57SDavid S. Miller 
121369ead7afSDavid S. Miller 	dst_release(dst_orig);
121469ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
121514e50e57SDavid S. Miller }
121614e50e57SDavid S. Miller 
12171da177e4SLinus Torvalds /*
12181da177e4SLinus Torvalds  *	Destination cache support functions
12191da177e4SLinus Torvalds  */
12201da177e4SLinus Torvalds 
12214b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12224b32b5adSMartin KaFai Lau {
12234b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12244b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12254b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12264b32b5adSMartin KaFai Lau }
12274b32b5adSMartin KaFai Lau 
12283da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12293da59bd9SMartin KaFai Lau {
12303da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12313da59bd9SMartin KaFai Lau 		return NULL;
12323da59bd9SMartin KaFai Lau 
12333da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12343da59bd9SMartin KaFai Lau 		return NULL;
12353da59bd9SMartin KaFai Lau 
12363da59bd9SMartin KaFai Lau 	return &rt->dst;
12373da59bd9SMartin KaFai Lau }
12383da59bd9SMartin KaFai Lau 
12393da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12403da59bd9SMartin KaFai Lau {
12413da59bd9SMartin KaFai Lau 	if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12423da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12433da59bd9SMartin KaFai Lau 		return &rt->dst;
12443da59bd9SMartin KaFai Lau 	else
12453da59bd9SMartin KaFai Lau 		return NULL;
12463da59bd9SMartin KaFai Lau }
12473da59bd9SMartin KaFai Lau 
12481da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12491da177e4SLinus Torvalds {
12501da177e4SLinus Torvalds 	struct rt6_info *rt;
12511da177e4SLinus Torvalds 
12521da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12531da177e4SLinus Torvalds 
12546f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12556f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12566f3118b5SNicolas Dichtel 	 * into this function always.
12576f3118b5SNicolas Dichtel 	 */
1258e3bc10bdSHannes Frederic Sowa 
12594b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
12604b32b5adSMartin KaFai Lau 
1261d52d3997SMartin KaFai Lau 	if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE))
12623da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
12633da59bd9SMartin KaFai Lau 	else
12643da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
12651da177e4SLinus Torvalds }
12661da177e4SLinus Torvalds 
12671da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
12681da177e4SLinus Torvalds {
12691da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
12701da177e4SLinus Torvalds 
12711da177e4SLinus Torvalds 	if (rt) {
127254c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
127354c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1274e0a1ad73SThomas Graf 				ip6_del_rt(rt);
127554c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
12761da177e4SLinus Torvalds 			}
127754c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
127854c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
127954c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
128054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
128154c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
128254c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
12831da177e4SLinus Torvalds }
12841da177e4SLinus Torvalds 
12851da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
12861da177e4SLinus Torvalds {
12871da177e4SLinus Torvalds 	struct rt6_info *rt;
12881da177e4SLinus Torvalds 
12893ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
12901da177e4SLinus Torvalds 
1291adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
12921da177e4SLinus Torvalds 	if (rt) {
12931eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
12941eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
12951eb4f758SHannes Frederic Sowa 			if (ip6_del_rt(rt))
12961eb4f758SHannes Frederic Sowa 				dst_free(&rt->dst);
12971eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
12981da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
12991da177e4SLinus Torvalds 		}
13001da177e4SLinus Torvalds 	}
13011eb4f758SHannes Frederic Sowa }
13021da177e4SLinus Torvalds 
130345e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
130445e4fd26SMartin KaFai Lau {
130545e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
130645e4fd26SMartin KaFai Lau 
130745e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
130845e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
130945e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
131045e4fd26SMartin KaFai Lau }
131145e4fd26SMartin KaFai Lau 
131245e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
131345e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13141da177e4SLinus Torvalds {
13151da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13161da177e4SLinus Torvalds 
131745e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
131845e4fd26SMartin KaFai Lau 		return;
131945e4fd26SMartin KaFai Lau 
132081aded24SDavid S. Miller 	dst_confirm(dst);
132145e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
132245e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
132345e4fd26SMartin KaFai Lau 		return;
132481aded24SDavid S. Miller 
132545e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_CACHE) {
132645e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
132745e4fd26SMartin KaFai Lau 	} else {
132845e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
132945e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13309d289715SHagen Paul Pfeifer 
133145e4fd26SMartin KaFai Lau 		if (iph) {
133245e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
133345e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
133445e4fd26SMartin KaFai Lau 		} else if (sk) {
133545e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
133645e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
133745e4fd26SMartin KaFai Lau 		} else {
133845e4fd26SMartin KaFai Lau 			return;
13391da177e4SLinus Torvalds 		}
134045e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
134145e4fd26SMartin KaFai Lau 		if (nrt6) {
134245e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
134345e4fd26SMartin KaFai Lau 
134445e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
134545e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
134645e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
134745e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
134845e4fd26SMartin KaFai Lau 			 */
134945e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
135045e4fd26SMartin KaFai Lau 		}
135145e4fd26SMartin KaFai Lau 	}
135245e4fd26SMartin KaFai Lau }
135345e4fd26SMartin KaFai Lau 
135445e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
135545e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
135645e4fd26SMartin KaFai Lau {
135745e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
13581da177e4SLinus Torvalds }
13591da177e4SLinus Torvalds 
136042ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
136142ae66c8SDavid S. Miller 		     int oif, u32 mark)
136281aded24SDavid S. Miller {
136381aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
136481aded24SDavid S. Miller 	struct dst_entry *dst;
136581aded24SDavid S. Miller 	struct flowi6 fl6;
136681aded24SDavid S. Miller 
136781aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
136881aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
13691b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
137081aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
137181aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
13726502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
137381aded24SDavid S. Miller 
137481aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
137581aded24SDavid S. Miller 	if (!dst->error)
137645e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
137781aded24SDavid S. Miller 	dst_release(dst);
137881aded24SDavid S. Miller }
137981aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
138081aded24SDavid S. Miller 
138181aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
138281aded24SDavid S. Miller {
138381aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
138481aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
138581aded24SDavid S. Miller }
138681aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
138781aded24SDavid S. Miller 
1388b55b76b2SDuan Jiong /* Handle redirects */
1389b55b76b2SDuan Jiong struct ip6rd_flowi {
1390b55b76b2SDuan Jiong 	struct flowi6 fl6;
1391b55b76b2SDuan Jiong 	struct in6_addr gateway;
1392b55b76b2SDuan Jiong };
1393b55b76b2SDuan Jiong 
1394b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1395b55b76b2SDuan Jiong 					     struct fib6_table *table,
1396b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1397b55b76b2SDuan Jiong 					     int flags)
1398b55b76b2SDuan Jiong {
1399b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1400b55b76b2SDuan Jiong 	struct rt6_info *rt;
1401b55b76b2SDuan Jiong 	struct fib6_node *fn;
1402b55b76b2SDuan Jiong 
1403b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1404b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1405b55b76b2SDuan Jiong 	 *
1406b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1407b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1408b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1409b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1410b55b76b2SDuan Jiong 	 * routes.
1411b55b76b2SDuan Jiong 	 */
1412b55b76b2SDuan Jiong 
1413b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1414b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1415b55b76b2SDuan Jiong restart:
1416b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1417b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1418b55b76b2SDuan Jiong 			continue;
1419b55b76b2SDuan Jiong 		if (rt->dst.error)
1420b55b76b2SDuan Jiong 			break;
1421b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1422b55b76b2SDuan Jiong 			continue;
1423b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1424b55b76b2SDuan Jiong 			continue;
1425b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1426b55b76b2SDuan Jiong 			continue;
1427b55b76b2SDuan Jiong 		break;
1428b55b76b2SDuan Jiong 	}
1429b55b76b2SDuan Jiong 
1430b55b76b2SDuan Jiong 	if (!rt)
1431b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1432b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1433b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1434b0a1ba59SMartin KaFai Lau 		goto out;
1435b0a1ba59SMartin KaFai Lau 	}
1436b0a1ba59SMartin KaFai Lau 
1437b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1438a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1439a3c00e46SMartin KaFai Lau 		if (fn)
1440a3c00e46SMartin KaFai Lau 			goto restart;
1441b55b76b2SDuan Jiong 	}
1442a3c00e46SMartin KaFai Lau 
1443b0a1ba59SMartin KaFai Lau out:
1444b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1445b55b76b2SDuan Jiong 
1446b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1447b55b76b2SDuan Jiong 
1448b55b76b2SDuan Jiong 	return rt;
1449b55b76b2SDuan Jiong };
1450b55b76b2SDuan Jiong 
1451b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1452b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1453b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1454b55b76b2SDuan Jiong {
1455b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1456b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1457b55b76b2SDuan Jiong 
1458b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1459b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1460b55b76b2SDuan Jiong 
1461b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1462b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1463b55b76b2SDuan Jiong }
1464b55b76b2SDuan Jiong 
14653a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
14663a5ad2eeSDavid S. Miller {
14673a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
14683a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
14693a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
14703a5ad2eeSDavid S. Miller 
14713a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1472e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
14733a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
14743a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
14753a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
14763a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
14776502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
14783a5ad2eeSDavid S. Miller 
1479b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
14806700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
14813a5ad2eeSDavid S. Miller 	dst_release(dst);
14823a5ad2eeSDavid S. Miller }
14833a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
14843a5ad2eeSDavid S. Miller 
1485c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1486c92a59ecSDuan Jiong 			    u32 mark)
1487c92a59ecSDuan Jiong {
1488c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1489c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1490c92a59ecSDuan Jiong 	struct dst_entry *dst;
1491c92a59ecSDuan Jiong 	struct flowi6 fl6;
1492c92a59ecSDuan Jiong 
1493c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1494e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1495c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1496c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1497c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1498c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1499c92a59ecSDuan Jiong 
1500b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1501c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1502c92a59ecSDuan Jiong 	dst_release(dst);
1503c92a59ecSDuan Jiong }
1504c92a59ecSDuan Jiong 
15053a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
15063a5ad2eeSDavid S. Miller {
15073a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
15083a5ad2eeSDavid S. Miller }
15093a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15103a5ad2eeSDavid S. Miller 
15110dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15121da177e4SLinus Torvalds {
15130dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15140dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15150dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15160dbaee3bSDavid S. Miller 
15171da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15181da177e4SLinus Torvalds 
15195578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15205578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15211da177e4SLinus Torvalds 
15221da177e4SLinus Torvalds 	/*
15231da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15241da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15251da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15261da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15271da177e4SLinus Torvalds 	 */
15281da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15291da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15301da177e4SLinus Torvalds 	return mtu;
15311da177e4SLinus Torvalds }
15321da177e4SLinus Torvalds 
1533ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1534d33e4553SDavid S. Miller {
15354b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
15364b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1537d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1538618f9bc7SSteffen Klassert 
1539618f9bc7SSteffen Klassert 	if (mtu)
154030f78d8eSEric Dumazet 		goto out;
1541618f9bc7SSteffen Klassert 
15424b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
15434b32b5adSMartin KaFai Lau 	if (mtu)
15444b32b5adSMartin KaFai Lau 		goto out;
15454b32b5adSMartin KaFai Lau 
1546618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1547d33e4553SDavid S. Miller 
1548d33e4553SDavid S. Miller 	rcu_read_lock();
1549d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1550d33e4553SDavid S. Miller 	if (idev)
1551d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1552d33e4553SDavid S. Miller 	rcu_read_unlock();
1553d33e4553SDavid S. Miller 
155430f78d8eSEric Dumazet out:
155530f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1556d33e4553SDavid S. Miller }
1557d33e4553SDavid S. Miller 
15583b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
15593b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
15605d0bbeebSThomas Graf 
15613b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
156287a11578SDavid S. Miller 				  struct flowi6 *fl6)
15631da177e4SLinus Torvalds {
156487a11578SDavid S. Miller 	struct dst_entry *dst;
15651da177e4SLinus Torvalds 	struct rt6_info *rt;
15661da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1567c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
15681da177e4SLinus Torvalds 
156938308473SDavid S. Miller 	if (unlikely(!idev))
1570122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
15711da177e4SLinus Torvalds 
15728b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, dev, 0, NULL);
157338308473SDavid S. Miller 	if (unlikely(!rt)) {
15741da177e4SLinus Torvalds 		in6_dev_put(idev);
157587a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
15761da177e4SLinus Torvalds 		goto out;
15771da177e4SLinus Torvalds 	}
15781da177e4SLinus Torvalds 
15798e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
15808e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1581d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1582550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
158387a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
15848e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
15858e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
158614edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
15871da177e4SLinus Torvalds 
15883b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1589d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1590d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
15913b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
15921da177e4SLinus Torvalds 
15935578689aSDaniel Lezcano 	fib6_force_start_gc(net);
15941da177e4SLinus Torvalds 
159587a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
159687a11578SDavid S. Miller 
15971da177e4SLinus Torvalds out:
159887a11578SDavid S. Miller 	return dst;
15991da177e4SLinus Torvalds }
16001da177e4SLinus Torvalds 
16013d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
16021da177e4SLinus Torvalds {
1603e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
16043d0f24a7SStephen Hemminger 	int more = 0;
16051da177e4SLinus Torvalds 
16063b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16073b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16085d0bbeebSThomas Graf 
16091da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16101da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16111da177e4SLinus Torvalds 			*pprev = dst->next;
16121da177e4SLinus Torvalds 			dst_free(dst);
16131da177e4SLinus Torvalds 		} else {
16141da177e4SLinus Torvalds 			pprev = &dst->next;
16153d0f24a7SStephen Hemminger 			++more;
16161da177e4SLinus Torvalds 		}
16171da177e4SLinus Torvalds 	}
16181da177e4SLinus Torvalds 
16193b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16205d0bbeebSThomas Graf 
16213d0f24a7SStephen Hemminger 	return more;
16221da177e4SLinus Torvalds }
16231da177e4SLinus Torvalds 
16241e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16251e493d19SDavid S. Miller 			    void *arg)
16261e493d19SDavid S. Miller {
16271e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16281e493d19SDavid S. Miller 
16291e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
16301e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
16311e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
16321e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
16331e493d19SDavid S. Miller 		if (func(rt, arg)) {
16341e493d19SDavid S. Miller 			*pprev = dst->next;
16351e493d19SDavid S. Miller 			dst_free(dst);
16361e493d19SDavid S. Miller 		} else {
16371e493d19SDavid S. Miller 			pprev = &dst->next;
16381e493d19SDavid S. Miller 		}
16391e493d19SDavid S. Miller 	}
16401e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
16411e493d19SDavid S. Miller }
16421e493d19SDavid S. Miller 
1643569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
16441da177e4SLinus Torvalds {
164586393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
16467019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
16477019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
16487019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
16497019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
16507019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1651fc66f95cSEric Dumazet 	int entries;
16521da177e4SLinus Torvalds 
1653fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
165449a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1655fc66f95cSEric Dumazet 	    entries <= rt_max_size)
16561da177e4SLinus Torvalds 		goto out;
16571da177e4SLinus Torvalds 
16586891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
165914956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1660fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1661fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
16627019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
16631da177e4SLinus Torvalds out:
16647019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1665fc66f95cSEric Dumazet 	return entries > rt_max_size;
16661da177e4SLinus Torvalds }
16671da177e4SLinus Torvalds 
1668e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1669e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1670e715b6d3SFlorian Westphal {
1671e715b6d3SFlorian Westphal 	struct nlattr *nla;
1672e715b6d3SFlorian Westphal 	int remaining;
1673e715b6d3SFlorian Westphal 	u32 *mp;
1674e715b6d3SFlorian Westphal 
167563159f29SIan Morris 	if (!cfg->fc_mx)
1676e715b6d3SFlorian Westphal 		return 0;
1677e715b6d3SFlorian Westphal 
1678e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1679e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1680e715b6d3SFlorian Westphal 		return -ENOMEM;
1681e715b6d3SFlorian Westphal 
1682e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1683e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1684e715b6d3SFlorian Westphal 
1685e715b6d3SFlorian Westphal 		if (type) {
1686ea697639SDaniel Borkmann 			u32 val;
1687ea697639SDaniel Borkmann 
1688e715b6d3SFlorian Westphal 			if (unlikely(type > RTAX_MAX))
1689e715b6d3SFlorian Westphal 				goto err;
1690ea697639SDaniel Borkmann 			if (type == RTAX_CC_ALGO) {
1691ea697639SDaniel Borkmann 				char tmp[TCP_CA_NAME_MAX];
1692e715b6d3SFlorian Westphal 
1693ea697639SDaniel Borkmann 				nla_strlcpy(tmp, nla, sizeof(tmp));
1694ea697639SDaniel Borkmann 				val = tcp_ca_get_key_by_name(tmp);
1695ea697639SDaniel Borkmann 				if (val == TCP_CA_UNSPEC)
1696ea697639SDaniel Borkmann 					goto err;
1697ea697639SDaniel Borkmann 			} else {
1698ea697639SDaniel Borkmann 				val = nla_get_u32(nla);
1699ea697639SDaniel Borkmann 			}
1700ea697639SDaniel Borkmann 
1701ea697639SDaniel Borkmann 			mp[type - 1] = val;
1702e715b6d3SFlorian Westphal 			__set_bit(type - 1, mxc->mx_valid);
1703e715b6d3SFlorian Westphal 		}
1704e715b6d3SFlorian Westphal 	}
1705e715b6d3SFlorian Westphal 
1706e715b6d3SFlorian Westphal 	mxc->mx = mp;
1707e715b6d3SFlorian Westphal 
1708e715b6d3SFlorian Westphal 	return 0;
1709e715b6d3SFlorian Westphal  err:
1710e715b6d3SFlorian Westphal 	kfree(mp);
1711e715b6d3SFlorian Westphal 	return -EINVAL;
1712e715b6d3SFlorian Westphal }
17131da177e4SLinus Torvalds 
171486872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
17151da177e4SLinus Torvalds {
17161da177e4SLinus Torvalds 	int err;
17175578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
17181da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
17191da177e4SLinus Torvalds 	struct net_device *dev = NULL;
17201da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1721c71099acSThomas Graf 	struct fib6_table *table;
1722e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
17231da177e4SLinus Torvalds 	int addr_type;
17241da177e4SLinus Torvalds 
172586872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
17261da177e4SLinus Torvalds 		return -EINVAL;
17271da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
172886872cb5SThomas Graf 	if (cfg->fc_src_len)
17291da177e4SLinus Torvalds 		return -EINVAL;
17301da177e4SLinus Torvalds #endif
173186872cb5SThomas Graf 	if (cfg->fc_ifindex) {
17321da177e4SLinus Torvalds 		err = -ENODEV;
17335578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
17341da177e4SLinus Torvalds 		if (!dev)
17351da177e4SLinus Torvalds 			goto out;
17361da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
17371da177e4SLinus Torvalds 		if (!idev)
17381da177e4SLinus Torvalds 			goto out;
17391da177e4SLinus Torvalds 	}
17401da177e4SLinus Torvalds 
174186872cb5SThomas Graf 	if (cfg->fc_metric == 0)
174286872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
17431da177e4SLinus Torvalds 
1744c71099acSThomas Graf 	err = -ENOBUFS;
174538308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1746d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1747d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
174838308473SDavid S. Miller 		if (!table) {
1749f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1750d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1751d71314b4SMatti Vaittinen 		}
1752d71314b4SMatti Vaittinen 	} else {
1753d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1754d71314b4SMatti Vaittinen 	}
175538308473SDavid S. Miller 
175638308473SDavid S. Miller 	if (!table)
1757c71099acSThomas Graf 		goto out;
1758c71099acSThomas Graf 
1759c88507fbSSabrina Dubroca 	rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table);
17601da177e4SLinus Torvalds 
176138308473SDavid S. Miller 	if (!rt) {
17621da177e4SLinus Torvalds 		err = -ENOMEM;
17631da177e4SLinus Torvalds 		goto out;
17641da177e4SLinus Torvalds 	}
17651da177e4SLinus Torvalds 
17661716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
17671716a961SGao feng 		rt6_set_expires(rt, jiffies +
17681716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
17691716a961SGao feng 	else
17701716a961SGao feng 		rt6_clean_expires(rt);
17711da177e4SLinus Torvalds 
177286872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
177386872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
177486872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
177586872cb5SThomas Graf 
177686872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
17771da177e4SLinus Torvalds 
17781da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1779d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1780ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1781ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
17821da177e4SLinus Torvalds 	else
1783d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
17841da177e4SLinus Torvalds 
1785d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
17861da177e4SLinus Torvalds 
178719e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
178819e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
178919e42e45SRoopa Prabhu 
179019e42e45SRoopa Prabhu 		err = lwtunnel_build_state(dev, cfg->fc_encap_type,
179119e42e45SRoopa Prabhu 					   cfg->fc_encap, &lwtstate);
179219e42e45SRoopa Prabhu 		if (err)
179319e42e45SRoopa Prabhu 			goto out;
179461adedf3SJiri Benc 		rt->dst.lwtstate = lwtstate_get(lwtstate);
179561adedf3SJiri Benc 		if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
179661adedf3SJiri Benc 			rt->dst.lwtstate->orig_output = rt->dst.output;
179761adedf3SJiri Benc 			rt->dst.output = lwtunnel_output;
179819e42e45SRoopa Prabhu 		}
179961adedf3SJiri Benc 		if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
180061adedf3SJiri Benc 			rt->dst.lwtstate->orig_input = rt->dst.input;
180161adedf3SJiri Benc 			rt->dst.input = lwtunnel_input;
180225368623STom Herbert 		}
180325368623STom Herbert 	}
180419e42e45SRoopa Prabhu 
180586872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
180686872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1807afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
180811d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
18091da177e4SLinus Torvalds 
18101da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
181186872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
181286872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
18131da177e4SLinus Torvalds #endif
18141da177e4SLinus Torvalds 
181586872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
18161da177e4SLinus Torvalds 
18171da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
18181da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
18191da177e4SLinus Torvalds 	 */
182086872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
182138308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
182238308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
182338308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
18241da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
18255578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
18261da177e4SLinus Torvalds 			if (dev) {
18271da177e4SLinus Torvalds 				dev_put(dev);
18281da177e4SLinus Torvalds 				in6_dev_put(idev);
18291da177e4SLinus Torvalds 			}
18305578689aSDaniel Lezcano 			dev = net->loopback_dev;
18311da177e4SLinus Torvalds 			dev_hold(dev);
18321da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
18331da177e4SLinus Torvalds 			if (!idev) {
18341da177e4SLinus Torvalds 				err = -ENODEV;
18351da177e4SLinus Torvalds 				goto out;
18361da177e4SLinus Torvalds 			}
18371da177e4SLinus Torvalds 		}
18381da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1839ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1840ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1841ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1842aad88724SEric Dumazet 			rt->dst.output = dst_discard_sk;
18437150aedeSKamala R 			rt->dst.input = dst_discard;
1844ef2c7d7bSNicolas Dichtel 			break;
1845ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1846ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
18477150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
18487150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1849ef2c7d7bSNicolas Dichtel 			break;
1850b4949ab2SNicolas Dichtel 		case RTN_THROW:
1851ef2c7d7bSNicolas Dichtel 		default:
18527150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
18537150aedeSKamala R 					: -ENETUNREACH;
18547150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
18557150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1856ef2c7d7bSNicolas Dichtel 			break;
1857ef2c7d7bSNicolas Dichtel 		}
18581da177e4SLinus Torvalds 		goto install_route;
18591da177e4SLinus Torvalds 	}
18601da177e4SLinus Torvalds 
186186872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1862b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
18631da177e4SLinus Torvalds 		int gwa_type;
18641da177e4SLinus Torvalds 
186586872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1866330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
186748ed7b26SFlorian Westphal 
186848ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
186948ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
187048ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
187148ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
187248ed7b26SFlorian Westphal 		 */
187348ed7b26SFlorian Westphal 		err = -EINVAL;
1874330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1875330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1876330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
187748ed7b26SFlorian Westphal 			goto out;
187848ed7b26SFlorian Westphal 
18794e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
18801da177e4SLinus Torvalds 
18811da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
18821da177e4SLinus Torvalds 			struct rt6_info *grt;
18831da177e4SLinus Torvalds 
18841da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
18851da177e4SLinus Torvalds 			   addresses as nexthop address.
18861da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
18871da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
18881da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
18891da177e4SLinus Torvalds 			   some exceptions. --ANK
18901da177e4SLinus Torvalds 			 */
18911da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
18921da177e4SLinus Torvalds 				goto out;
18931da177e4SLinus Torvalds 
18945578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
18951da177e4SLinus Torvalds 
18961da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
189738308473SDavid S. Miller 			if (!grt)
18981da177e4SLinus Torvalds 				goto out;
18991da177e4SLinus Torvalds 			if (dev) {
1900d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
190194e187c0SAmerigo Wang 					ip6_rt_put(grt);
19021da177e4SLinus Torvalds 					goto out;
19031da177e4SLinus Torvalds 				}
19041da177e4SLinus Torvalds 			} else {
1905d1918542SDavid S. Miller 				dev = grt->dst.dev;
19061da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
19071da177e4SLinus Torvalds 				dev_hold(dev);
19081da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
19091da177e4SLinus Torvalds 			}
19101da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
19111da177e4SLinus Torvalds 				err = 0;
191294e187c0SAmerigo Wang 			ip6_rt_put(grt);
19131da177e4SLinus Torvalds 
19141da177e4SLinus Torvalds 			if (err)
19151da177e4SLinus Torvalds 				goto out;
19161da177e4SLinus Torvalds 		}
19171da177e4SLinus Torvalds 		err = -EINVAL;
191838308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
19191da177e4SLinus Torvalds 			goto out;
19201da177e4SLinus Torvalds 	}
19211da177e4SLinus Torvalds 
19221da177e4SLinus Torvalds 	err = -ENODEV;
192338308473SDavid S. Miller 	if (!dev)
19241da177e4SLinus Torvalds 		goto out;
19251da177e4SLinus Torvalds 
1926c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1927c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1928c3968a85SDaniel Walter 			err = -EINVAL;
1929c3968a85SDaniel Walter 			goto out;
1930c3968a85SDaniel Walter 		}
19314e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1932c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1933c3968a85SDaniel Walter 	} else
1934c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1935c3968a85SDaniel Walter 
193686872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
19371da177e4SLinus Torvalds 
19381da177e4SLinus Torvalds install_route:
1939d8d1f30bSChangli Gao 	rt->dst.dev = dev;
19401da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1941c71099acSThomas Graf 	rt->rt6i_table = table;
194263152fc0SDaniel Lezcano 
1943c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
194463152fc0SDaniel Lezcano 
1945e715b6d3SFlorian Westphal 	err = ip6_convert_metrics(&mxc, cfg);
1946e715b6d3SFlorian Westphal 	if (err)
1947e715b6d3SFlorian Westphal 		goto out;
19481da177e4SLinus Torvalds 
1949e715b6d3SFlorian Westphal 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
1950e715b6d3SFlorian Westphal 
1951e715b6d3SFlorian Westphal 	kfree(mxc.mx);
1952e715b6d3SFlorian Westphal 	return err;
19531da177e4SLinus Torvalds out:
19541da177e4SLinus Torvalds 	if (dev)
19551da177e4SLinus Torvalds 		dev_put(dev);
19561da177e4SLinus Torvalds 	if (idev)
19571da177e4SLinus Torvalds 		in6_dev_put(idev);
19581da177e4SLinus Torvalds 	if (rt)
1959d8d1f30bSChangli Gao 		dst_free(&rt->dst);
19601da177e4SLinus Torvalds 	return err;
19611da177e4SLinus Torvalds }
19621da177e4SLinus Torvalds 
196386872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
19641da177e4SLinus Torvalds {
19651da177e4SLinus Torvalds 	int err;
1966c71099acSThomas Graf 	struct fib6_table *table;
1967d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
19681da177e4SLinus Torvalds 
19696825a26cSGao feng 	if (rt == net->ipv6.ip6_null_entry) {
19706825a26cSGao feng 		err = -ENOENT;
19716825a26cSGao feng 		goto out;
19726825a26cSGao feng 	}
19736c813a72SPatrick McHardy 
1974c71099acSThomas Graf 	table = rt->rt6i_table;
1975c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
197686872cb5SThomas Graf 	err = fib6_del(rt, info);
1977c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
19781da177e4SLinus Torvalds 
19796825a26cSGao feng out:
198094e187c0SAmerigo Wang 	ip6_rt_put(rt);
19811da177e4SLinus Torvalds 	return err;
19821da177e4SLinus Torvalds }
19831da177e4SLinus Torvalds 
1984e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1985e0a1ad73SThomas Graf {
19864d1169c1SDenis V. Lunev 	struct nl_info info = {
1987d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
19884d1169c1SDenis V. Lunev 	};
1989528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1990e0a1ad73SThomas Graf }
1991e0a1ad73SThomas Graf 
199286872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
19931da177e4SLinus Torvalds {
1994c71099acSThomas Graf 	struct fib6_table *table;
19951da177e4SLinus Torvalds 	struct fib6_node *fn;
19961da177e4SLinus Torvalds 	struct rt6_info *rt;
19971da177e4SLinus Torvalds 	int err = -ESRCH;
19981da177e4SLinus Torvalds 
19995578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
200038308473SDavid S. Miller 	if (!table)
2001c71099acSThomas Graf 		return err;
20021da177e4SLinus Torvalds 
2003c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2004c71099acSThomas Graf 
2005c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
200686872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
200786872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
20081da177e4SLinus Torvalds 
20091da177e4SLinus Torvalds 	if (fn) {
2010d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
20111f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
20121f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
20131f56a01fSMartin KaFai Lau 				continue;
201486872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2015d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2016d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
20171da177e4SLinus Torvalds 				continue;
201886872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
201986872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
20201da177e4SLinus Torvalds 				continue;
202186872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
20221da177e4SLinus Torvalds 				continue;
2023d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2024c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
20251da177e4SLinus Torvalds 
202686872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
20271da177e4SLinus Torvalds 		}
20281da177e4SLinus Torvalds 	}
2029c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
20301da177e4SLinus Torvalds 
20311da177e4SLinus Torvalds 	return err;
20321da177e4SLinus Torvalds }
20331da177e4SLinus Torvalds 
20346700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2035a6279458SYOSHIFUJI Hideaki {
2036e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
2037a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2038e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2039e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2040e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2041e8599ff4SDavid S. Miller 	struct neighbour *neigh;
204271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
20436e157b6aSDavid S. Miller 	int optlen, on_link;
20446e157b6aSDavid S. Miller 	u8 *lladdr;
2045e8599ff4SDavid S. Miller 
204629a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
204771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2048e8599ff4SDavid S. Miller 
2049e8599ff4SDavid S. Miller 	if (optlen < 0) {
20506e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2051e8599ff4SDavid S. Miller 		return;
2052e8599ff4SDavid S. Miller 	}
2053e8599ff4SDavid S. Miller 
205471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2055e8599ff4SDavid S. Miller 
205671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
20576e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2058e8599ff4SDavid S. Miller 		return;
2059e8599ff4SDavid S. Miller 	}
2060e8599ff4SDavid S. Miller 
20616e157b6aSDavid S. Miller 	on_link = 0;
206271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2063e8599ff4SDavid S. Miller 		on_link = 1;
206471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2065e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
20666e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2067e8599ff4SDavid S. Miller 		return;
2068e8599ff4SDavid S. Miller 	}
2069e8599ff4SDavid S. Miller 
2070e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2071e8599ff4SDavid S. Miller 	if (!in6_dev)
2072e8599ff4SDavid S. Miller 		return;
2073e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2074e8599ff4SDavid S. Miller 		return;
2075e8599ff4SDavid S. Miller 
2076e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2077e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2078e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2079e8599ff4SDavid S. Miller 	 */
2080e8599ff4SDavid S. Miller 
208171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
2082e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2083e8599ff4SDavid S. Miller 		return;
2084e8599ff4SDavid S. Miller 	}
20856e157b6aSDavid S. Miller 
20866e157b6aSDavid S. Miller 	lladdr = NULL;
2087e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2088e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2089e8599ff4SDavid S. Miller 					     skb->dev);
2090e8599ff4SDavid S. Miller 		if (!lladdr) {
2091e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2092e8599ff4SDavid S. Miller 			return;
2093e8599ff4SDavid S. Miller 		}
2094e8599ff4SDavid S. Miller 	}
2095e8599ff4SDavid S. Miller 
20966e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
20976e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
20986e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
20996e157b6aSDavid S. Miller 		return;
21006e157b6aSDavid S. Miller 	}
21016e157b6aSDavid S. Miller 
21026e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
21036e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
21046e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
21056e157b6aSDavid S. Miller 	 */
21066e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
21076e157b6aSDavid S. Miller 
210871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2109e8599ff4SDavid S. Miller 	if (!neigh)
2110e8599ff4SDavid S. Miller 		return;
2111e8599ff4SDavid S. Miller 
21121da177e4SLinus Torvalds 	/*
21131da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
21141da177e4SLinus Torvalds 	 */
21151da177e4SLinus Torvalds 
21161da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
21171da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
21181da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
21191da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
21201da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
21211da177e4SLinus Torvalds 		     );
21221da177e4SLinus Torvalds 
212383a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
212438308473SDavid S. Miller 	if (!nrt)
21251da177e4SLinus Torvalds 		goto out;
21261da177e4SLinus Torvalds 
21271da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
21281da177e4SLinus Torvalds 	if (on_link)
21291da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
21301da177e4SLinus Torvalds 
21314e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
21321da177e4SLinus Torvalds 
213340e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
21341da177e4SLinus Torvalds 		goto out;
21351da177e4SLinus Torvalds 
2136d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2137d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
213871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
213960592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
21408d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
21418d71740cSTom Tucker 
21421da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
21436e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2144e0a1ad73SThomas Graf 		ip6_del_rt(rt);
21451da177e4SLinus Torvalds 	}
21461da177e4SLinus Torvalds 
21471da177e4SLinus Torvalds out:
2148e8599ff4SDavid S. Miller 	neigh_release(neigh);
21496e157b6aSDavid S. Miller }
21506e157b6aSDavid S. Miller 
21511da177e4SLinus Torvalds /*
21521da177e4SLinus Torvalds  *	Misc support functions
21531da177e4SLinus Torvalds  */
21541da177e4SLinus Torvalds 
21554b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
21564b32b5adSMartin KaFai Lau {
21574b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
21584b32b5adSMartin KaFai Lau 
21594b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
21604b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
21614b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
21624b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
21634b32b5adSMartin KaFai Lau }
21644b32b5adSMartin KaFai Lau 
216583a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
21661da177e4SLinus Torvalds {
2167d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2168d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
216983a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2170d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
21711da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
21721da177e4SLinus Torvalds 	if (rt->rt6i_idev)
21731da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2174d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
21754e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
21761716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
21771716a961SGao feng 	rt6_set_from(rt, ort);
217883a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
21791da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
218083a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
21811da177e4SLinus Torvalds #endif
218283a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2183c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
218461adedf3SJiri Benc 	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
21851da177e4SLinus Torvalds }
21861da177e4SLinus Torvalds 
218770ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2188efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2189b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2190b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
219170ceb4f5SYOSHIFUJI Hideaki {
219270ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
219370ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2194c71099acSThomas Graf 	struct fib6_table *table;
219570ceb4f5SYOSHIFUJI Hideaki 
2196efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
219738308473SDavid S. Miller 	if (!table)
2198c71099acSThomas Graf 		return NULL;
2199c71099acSThomas Graf 
22005744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2201c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
220270ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
220370ceb4f5SYOSHIFUJI Hideaki 		goto out;
220470ceb4f5SYOSHIFUJI Hideaki 
2205d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2206d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
220770ceb4f5SYOSHIFUJI Hideaki 			continue;
220870ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
220970ceb4f5SYOSHIFUJI Hideaki 			continue;
221070ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
221170ceb4f5SYOSHIFUJI Hideaki 			continue;
2212d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
221370ceb4f5SYOSHIFUJI Hideaki 		break;
221470ceb4f5SYOSHIFUJI Hideaki 	}
221570ceb4f5SYOSHIFUJI Hideaki out:
22165744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
221770ceb4f5SYOSHIFUJI Hideaki 	return rt;
221870ceb4f5SYOSHIFUJI Hideaki }
221970ceb4f5SYOSHIFUJI Hideaki 
2220efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2221b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2222b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
222395c96174SEric Dumazet 					   unsigned int pref)
222470ceb4f5SYOSHIFUJI Hideaki {
222586872cb5SThomas Graf 	struct fib6_config cfg = {
222686872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
2227238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
222886872cb5SThomas Graf 		.fc_ifindex	= ifindex,
222986872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
223086872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
223186872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
223215e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2233efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2234efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
223586872cb5SThomas Graf 	};
223670ceb4f5SYOSHIFUJI Hideaki 
22374e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
22384e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
223986872cb5SThomas Graf 
2240e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2241e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
224286872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
224370ceb4f5SYOSHIFUJI Hideaki 
224486872cb5SThomas Graf 	ip6_route_add(&cfg);
224570ceb4f5SYOSHIFUJI Hideaki 
2246efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
224770ceb4f5SYOSHIFUJI Hideaki }
224870ceb4f5SYOSHIFUJI Hideaki #endif
224970ceb4f5SYOSHIFUJI Hideaki 
2250b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
22511da177e4SLinus Torvalds {
22521da177e4SLinus Torvalds 	struct rt6_info *rt;
2253c71099acSThomas Graf 	struct fib6_table *table;
22541da177e4SLinus Torvalds 
2255c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
225638308473SDavid S. Miller 	if (!table)
2257c71099acSThomas Graf 		return NULL;
22581da177e4SLinus Torvalds 
22595744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2260d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2261d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2262045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
22631da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
22641da177e4SLinus Torvalds 			break;
22651da177e4SLinus Torvalds 	}
22661da177e4SLinus Torvalds 	if (rt)
2267d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
22685744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
22691da177e4SLinus Torvalds 	return rt;
22701da177e4SLinus Torvalds }
22711da177e4SLinus Torvalds 
2272b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2273ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2274ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
22751da177e4SLinus Torvalds {
227686872cb5SThomas Graf 	struct fib6_config cfg = {
227786872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
2278238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
227986872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
228086872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
228186872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
228215e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
22835578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2284c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
228586872cb5SThomas Graf 	};
22861da177e4SLinus Torvalds 
22874e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
22881da177e4SLinus Torvalds 
228986872cb5SThomas Graf 	ip6_route_add(&cfg);
22901da177e4SLinus Torvalds 
22911da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
22921da177e4SLinus Torvalds }
22931da177e4SLinus Torvalds 
22947b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
22951da177e4SLinus Torvalds {
22961da177e4SLinus Torvalds 	struct rt6_info *rt;
2297c71099acSThomas Graf 	struct fib6_table *table;
2298c71099acSThomas Graf 
2299c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
23007b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
230138308473SDavid S. Miller 	if (!table)
2302c71099acSThomas Graf 		return;
23031da177e4SLinus Torvalds 
23041da177e4SLinus Torvalds restart:
2305c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2306d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
23073e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
23083e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2309d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2310c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2311e0a1ad73SThomas Graf 			ip6_del_rt(rt);
23121da177e4SLinus Torvalds 			goto restart;
23131da177e4SLinus Torvalds 		}
23141da177e4SLinus Torvalds 	}
2315c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
23161da177e4SLinus Torvalds }
23171da177e4SLinus Torvalds 
23185578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
23195578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
232086872cb5SThomas Graf 				 struct fib6_config *cfg)
232186872cb5SThomas Graf {
232286872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
232386872cb5SThomas Graf 
232486872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
232586872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
232686872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
232786872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
232886872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
232986872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
233086872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
233186872cb5SThomas Graf 
23325578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2333f1243c2dSBenjamin Thery 
23344e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
23354e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
23364e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
233786872cb5SThomas Graf }
233886872cb5SThomas Graf 
23395578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
23401da177e4SLinus Torvalds {
234186872cb5SThomas Graf 	struct fib6_config cfg;
23421da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
23431da177e4SLinus Torvalds 	int err;
23441da177e4SLinus Torvalds 
23451da177e4SLinus Torvalds 	switch (cmd) {
23461da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
23471da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2348af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
23491da177e4SLinus Torvalds 			return -EPERM;
23501da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
23511da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
23521da177e4SLinus Torvalds 		if (err)
23531da177e4SLinus Torvalds 			return -EFAULT;
23541da177e4SLinus Torvalds 
23555578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
235686872cb5SThomas Graf 
23571da177e4SLinus Torvalds 		rtnl_lock();
23581da177e4SLinus Torvalds 		switch (cmd) {
23591da177e4SLinus Torvalds 		case SIOCADDRT:
236086872cb5SThomas Graf 			err = ip6_route_add(&cfg);
23611da177e4SLinus Torvalds 			break;
23621da177e4SLinus Torvalds 		case SIOCDELRT:
236386872cb5SThomas Graf 			err = ip6_route_del(&cfg);
23641da177e4SLinus Torvalds 			break;
23651da177e4SLinus Torvalds 		default:
23661da177e4SLinus Torvalds 			err = -EINVAL;
23671da177e4SLinus Torvalds 		}
23681da177e4SLinus Torvalds 		rtnl_unlock();
23691da177e4SLinus Torvalds 
23701da177e4SLinus Torvalds 		return err;
23713ff50b79SStephen Hemminger 	}
23721da177e4SLinus Torvalds 
23731da177e4SLinus Torvalds 	return -EINVAL;
23741da177e4SLinus Torvalds }
23751da177e4SLinus Torvalds 
23761da177e4SLinus Torvalds /*
23771da177e4SLinus Torvalds  *	Drop the packet on the floor
23781da177e4SLinus Torvalds  */
23791da177e4SLinus Torvalds 
2380d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
23811da177e4SLinus Torvalds {
2382612f09e8SYOSHIFUJI Hideaki 	int type;
2383adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2384612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2385612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
23860660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
238745bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
23883bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
23893bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2390612f09e8SYOSHIFUJI Hideaki 			break;
2391612f09e8SYOSHIFUJI Hideaki 		}
2392612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2393612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
23943bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
23953bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2396612f09e8SYOSHIFUJI Hideaki 		break;
2397612f09e8SYOSHIFUJI Hideaki 	}
23983ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
23991da177e4SLinus Torvalds 	kfree_skb(skb);
24001da177e4SLinus Torvalds 	return 0;
24011da177e4SLinus Torvalds }
24021da177e4SLinus Torvalds 
24039ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
24049ce8ade0SThomas Graf {
2405612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
24069ce8ade0SThomas Graf }
24079ce8ade0SThomas Graf 
2408aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb)
24091da177e4SLinus Torvalds {
2410adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2411612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
24121da177e4SLinus Torvalds }
24131da177e4SLinus Torvalds 
24149ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
24159ce8ade0SThomas Graf {
2416612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
24179ce8ade0SThomas Graf }
24189ce8ade0SThomas Graf 
2419aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb)
24209ce8ade0SThomas Graf {
2421adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2422612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
24239ce8ade0SThomas Graf }
24249ce8ade0SThomas Graf 
24251da177e4SLinus Torvalds /*
24261da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
24271da177e4SLinus Torvalds  */
24281da177e4SLinus Torvalds 
24291da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
24301da177e4SLinus Torvalds 				    const struct in6_addr *addr,
24318f031519SDavid S. Miller 				    bool anycast)
24321da177e4SLinus Torvalds {
2433c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2434a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2435a3300ef4SHannes Frederic Sowa 					    DST_NOCOUNT, NULL);
2436a3300ef4SHannes Frederic Sowa 	if (!rt)
24371da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
24381da177e4SLinus Torvalds 
24391da177e4SLinus Torvalds 	in6_dev_hold(idev);
24401da177e4SLinus Torvalds 
244111d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2442d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2443d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
24441da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
24451da177e4SLinus Torvalds 
24461da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
244758c4fb86SYOSHIFUJI Hideaki 	if (anycast)
244858c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
244958c4fb86SYOSHIFUJI Hideaki 	else
24501da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
24511da177e4SLinus Torvalds 
2452550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
24534e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
24541da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
24555578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
24561da177e4SLinus Torvalds 
2457d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
24581da177e4SLinus Torvalds 
24591da177e4SLinus Torvalds 	return rt;
24601da177e4SLinus Torvalds }
24611da177e4SLinus Torvalds 
2462c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2463c3968a85SDaniel Walter 			struct rt6_info *rt,
2464b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2465c3968a85SDaniel Walter 			unsigned int prefs,
2466c3968a85SDaniel Walter 			struct in6_addr *saddr)
2467c3968a85SDaniel Walter {
2468e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2469e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2470c3968a85SDaniel Walter 	int err = 0;
2471e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
24724e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2473c3968a85SDaniel Walter 	else
2474c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2475c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2476c3968a85SDaniel Walter 	return err;
2477c3968a85SDaniel Walter }
2478c3968a85SDaniel Walter 
2479c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2480c3968a85SDaniel Walter struct arg_dev_net_ip {
2481c3968a85SDaniel Walter 	struct net_device *dev;
2482c3968a85SDaniel Walter 	struct net *net;
2483c3968a85SDaniel Walter 	struct in6_addr *addr;
2484c3968a85SDaniel Walter };
2485c3968a85SDaniel Walter 
2486c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2487c3968a85SDaniel Walter {
2488c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2489c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2490c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2491c3968a85SDaniel Walter 
2492d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2493c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2494c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2495c3968a85SDaniel Walter 		/* remove prefsrc entry */
2496c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2497c3968a85SDaniel Walter 	}
2498c3968a85SDaniel Walter 	return 0;
2499c3968a85SDaniel Walter }
2500c3968a85SDaniel Walter 
2501c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2502c3968a85SDaniel Walter {
2503c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2504c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2505c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2506c3968a85SDaniel Walter 		.net = net,
2507c3968a85SDaniel Walter 		.addr = &ifp->addr,
2508c3968a85SDaniel Walter 	};
25090c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2510c3968a85SDaniel Walter }
2511c3968a85SDaniel Walter 
2512be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2513be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2514be7a010dSDuan Jiong 
2515be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2516be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2517be7a010dSDuan Jiong {
2518be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2519be7a010dSDuan Jiong 
2520be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2521be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2522be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2523be7a010dSDuan Jiong 		return -1;
2524be7a010dSDuan Jiong 	}
2525be7a010dSDuan Jiong 	return 0;
2526be7a010dSDuan Jiong }
2527be7a010dSDuan Jiong 
2528be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2529be7a010dSDuan Jiong {
2530be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2531be7a010dSDuan Jiong }
2532be7a010dSDuan Jiong 
25338ed67789SDaniel Lezcano struct arg_dev_net {
25348ed67789SDaniel Lezcano 	struct net_device *dev;
25358ed67789SDaniel Lezcano 	struct net *net;
25368ed67789SDaniel Lezcano };
25378ed67789SDaniel Lezcano 
25381da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
25391da177e4SLinus Torvalds {
2540bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2541bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
25428ed67789SDaniel Lezcano 
2543d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2544c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
25451da177e4SLinus Torvalds 		return -1;
2546c159d30cSDavid S. Miller 
25471da177e4SLinus Torvalds 	return 0;
25481da177e4SLinus Torvalds }
25491da177e4SLinus Torvalds 
2550f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
25511da177e4SLinus Torvalds {
25528ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
25538ed67789SDaniel Lezcano 		.dev = dev,
25548ed67789SDaniel Lezcano 		.net = net,
25558ed67789SDaniel Lezcano 	};
25568ed67789SDaniel Lezcano 
25570c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
25581e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
25598d0b94afSMartin KaFai Lau 	rt6_uncached_list_flush_dev(net, dev);
25601da177e4SLinus Torvalds }
25611da177e4SLinus Torvalds 
256295c96174SEric Dumazet struct rt6_mtu_change_arg {
25631da177e4SLinus Torvalds 	struct net_device *dev;
256495c96174SEric Dumazet 	unsigned int mtu;
25651da177e4SLinus Torvalds };
25661da177e4SLinus Torvalds 
25671da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
25681da177e4SLinus Torvalds {
25691da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
25701da177e4SLinus Torvalds 	struct inet6_dev *idev;
25711da177e4SLinus Torvalds 
25721da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
25731da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
25741da177e4SLinus Torvalds 	   We still use this lock to block changes
25751da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
25761da177e4SLinus Torvalds 	*/
25771da177e4SLinus Torvalds 
25781da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
257938308473SDavid S. Miller 	if (!idev)
25801da177e4SLinus Torvalds 		return 0;
25811da177e4SLinus Torvalds 
25821da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
25831da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
25841da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
25851da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
25861da177e4SLinus Torvalds 	 */
25871da177e4SLinus Torvalds 	/*
25881da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
25891da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
25901da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
25911da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
25921da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
25931da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
25941da177e4SLinus Torvalds 	   PMTU discouvery.
25951da177e4SLinus Torvalds 	 */
2596d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
25974b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
25984b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
25994b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
26004b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
26014b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
26024b32b5adSMartin KaFai Lau 			 * been updated.
26034b32b5adSMartin KaFai Lau 			 */
26044b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
26054b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
26064b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2607d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
26084b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2609defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2610566cfd8fSSimon Arlott 		}
26114b32b5adSMartin KaFai Lau 	}
26121da177e4SLinus Torvalds 	return 0;
26131da177e4SLinus Torvalds }
26141da177e4SLinus Torvalds 
261595c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
26161da177e4SLinus Torvalds {
2617c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2618c71099acSThomas Graf 		.dev = dev,
2619c71099acSThomas Graf 		.mtu = mtu,
2620c71099acSThomas Graf 	};
26211da177e4SLinus Torvalds 
26220c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
26231da177e4SLinus Torvalds }
26241da177e4SLinus Torvalds 
2625ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
26265176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
262786872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2628ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
262986872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
263086872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
263151ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2632c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
263319e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
263419e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
263586872cb5SThomas Graf };
263686872cb5SThomas Graf 
263786872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
263886872cb5SThomas Graf 			      struct fib6_config *cfg)
26391da177e4SLinus Torvalds {
264086872cb5SThomas Graf 	struct rtmsg *rtm;
264186872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2642c78ba6d6SLubomir Rintel 	unsigned int pref;
264386872cb5SThomas Graf 	int err;
26441da177e4SLinus Torvalds 
264586872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
264686872cb5SThomas Graf 	if (err < 0)
264786872cb5SThomas Graf 		goto errout;
26481da177e4SLinus Torvalds 
264986872cb5SThomas Graf 	err = -EINVAL;
265086872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
265186872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
265286872cb5SThomas Graf 
265386872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
265486872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
265586872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
265686872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
265786872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2658ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
265986872cb5SThomas Graf 
2660ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2661ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2662b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2663b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
266486872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
266586872cb5SThomas Graf 
2666ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2667ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2668ab79ad14SMaciej Żenczykowski 
26691f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
26701f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
26711f56a01fSMartin KaFai Lau 
267215e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
267386872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
26743b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
267586872cb5SThomas Graf 
267686872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
267767b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
267886872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
26791da177e4SLinus Torvalds 	}
268086872cb5SThomas Graf 
268186872cb5SThomas Graf 	if (tb[RTA_DST]) {
268286872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
268386872cb5SThomas Graf 
268486872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
268586872cb5SThomas Graf 			goto errout;
268686872cb5SThomas Graf 
268786872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
26881da177e4SLinus Torvalds 	}
268986872cb5SThomas Graf 
269086872cb5SThomas Graf 	if (tb[RTA_SRC]) {
269186872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
269286872cb5SThomas Graf 
269386872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
269486872cb5SThomas Graf 			goto errout;
269586872cb5SThomas Graf 
269686872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
26971da177e4SLinus Torvalds 	}
269886872cb5SThomas Graf 
2699c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
270067b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2701c3968a85SDaniel Walter 
270286872cb5SThomas Graf 	if (tb[RTA_OIF])
270386872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
270486872cb5SThomas Graf 
270586872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
270686872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
270786872cb5SThomas Graf 
270886872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
270986872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
271086872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
27111da177e4SLinus Torvalds 	}
271286872cb5SThomas Graf 
271386872cb5SThomas Graf 	if (tb[RTA_TABLE])
271486872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
271586872cb5SThomas Graf 
271651ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
271751ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
271851ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
271951ebd318SNicolas Dichtel 	}
272051ebd318SNicolas Dichtel 
2721c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2722c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2723c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2724c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2725c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2726c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2727c78ba6d6SLubomir Rintel 	}
2728c78ba6d6SLubomir Rintel 
272919e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
273019e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
273119e42e45SRoopa Prabhu 
273219e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP_TYPE])
273319e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
273419e42e45SRoopa Prabhu 
273586872cb5SThomas Graf 	err = 0;
273686872cb5SThomas Graf errout:
273786872cb5SThomas Graf 	return err;
27381da177e4SLinus Torvalds }
27391da177e4SLinus Torvalds 
274051ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add)
274151ebd318SNicolas Dichtel {
274251ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
274351ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
274451ebd318SNicolas Dichtel 	int remaining;
274551ebd318SNicolas Dichtel 	int attrlen;
274651ebd318SNicolas Dichtel 	int err = 0, last_err = 0;
274751ebd318SNicolas Dichtel 
274835f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
274951ebd318SNicolas Dichtel beginning:
275051ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
275151ebd318SNicolas Dichtel 
275251ebd318SNicolas Dichtel 	/* Parse a Multipath Entry */
275351ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
275451ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
275551ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
275651ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
275751ebd318SNicolas Dichtel 
275851ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
275951ebd318SNicolas Dichtel 		if (attrlen > 0) {
276051ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
276151ebd318SNicolas Dichtel 
276251ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
276351ebd318SNicolas Dichtel 			if (nla) {
276467b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
276551ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
276651ebd318SNicolas Dichtel 			}
276719e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
276819e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
276919e42e45SRoopa Prabhu 			if (nla)
277019e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
277151ebd318SNicolas Dichtel 		}
277251ebd318SNicolas Dichtel 		err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
277351ebd318SNicolas Dichtel 		if (err) {
277451ebd318SNicolas Dichtel 			last_err = err;
277551ebd318SNicolas Dichtel 			/* If we are trying to remove a route, do not stop the
277651ebd318SNicolas Dichtel 			 * loop when ip6_route_del() fails (because next hop is
277751ebd318SNicolas Dichtel 			 * already gone), we should try to remove all next hops.
277851ebd318SNicolas Dichtel 			 */
277951ebd318SNicolas Dichtel 			if (add) {
278051ebd318SNicolas Dichtel 				/* If add fails, we should try to delete all
278151ebd318SNicolas Dichtel 				 * next hops that have been already added.
278251ebd318SNicolas Dichtel 				 */
278351ebd318SNicolas Dichtel 				add = 0;
278435f1b4e9SMichal Kubeček 				remaining = cfg->fc_mp_len - remaining;
278551ebd318SNicolas Dichtel 				goto beginning;
278651ebd318SNicolas Dichtel 			}
278751ebd318SNicolas Dichtel 		}
27881a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
278927596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
279027596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
279127596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
279227596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
279327596472SMichal Kubeček 		 * be added to it.
27941a72418bSNicolas Dichtel 		 */
279527596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
279627596472SMichal Kubeček 						     NLM_F_REPLACE);
279751ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
279851ebd318SNicolas Dichtel 	}
279951ebd318SNicolas Dichtel 
280051ebd318SNicolas Dichtel 	return last_err;
280151ebd318SNicolas Dichtel }
280251ebd318SNicolas Dichtel 
2803661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
28041da177e4SLinus Torvalds {
280586872cb5SThomas Graf 	struct fib6_config cfg;
280686872cb5SThomas Graf 	int err;
28071da177e4SLinus Torvalds 
280886872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
280986872cb5SThomas Graf 	if (err < 0)
281086872cb5SThomas Graf 		return err;
281186872cb5SThomas Graf 
281251ebd318SNicolas Dichtel 	if (cfg.fc_mp)
281351ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 0);
281451ebd318SNicolas Dichtel 	else
281586872cb5SThomas Graf 		return ip6_route_del(&cfg);
28161da177e4SLinus Torvalds }
28171da177e4SLinus Torvalds 
2818661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
28191da177e4SLinus Torvalds {
282086872cb5SThomas Graf 	struct fib6_config cfg;
282186872cb5SThomas Graf 	int err;
28221da177e4SLinus Torvalds 
282386872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
282486872cb5SThomas Graf 	if (err < 0)
282586872cb5SThomas Graf 		return err;
282686872cb5SThomas Graf 
282751ebd318SNicolas Dichtel 	if (cfg.fc_mp)
282851ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 1);
282951ebd318SNicolas Dichtel 	else
283086872cb5SThomas Graf 		return ip6_route_add(&cfg);
28311da177e4SLinus Torvalds }
28321da177e4SLinus Torvalds 
283319e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
2834339bf98fSThomas Graf {
2835339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2836339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2837339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2838339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2839339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2840339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2841339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2842339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2843339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
28446a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2845ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
2846c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
284719e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
284861adedf3SJiri Benc 	       + lwtunnel_get_encap_size(rt->dst.lwtstate);
2849339bf98fSThomas Graf }
2850339bf98fSThomas Graf 
2851191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2852191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
28530d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
285415e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
28557bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
28561da177e4SLinus Torvalds {
28574b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
28581da177e4SLinus Torvalds 	struct rtmsg *rtm;
28591da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2860e3703b3dSThomas Graf 	long expires;
28619e762a4aSPatrick McHardy 	u32 table;
28621da177e4SLinus Torvalds 
28631da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
28641da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
28651da177e4SLinus Torvalds 			/* success since this is not a prefix route */
28661da177e4SLinus Torvalds 			return 1;
28671da177e4SLinus Torvalds 		}
28681da177e4SLinus Torvalds 	}
28691da177e4SLinus Torvalds 
287015e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
287138308473SDavid S. Miller 	if (!nlh)
287226932566SPatrick McHardy 		return -EMSGSIZE;
28732d7202bfSThomas Graf 
28742d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
28751da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
28761da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
28771da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
28781da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2879c71099acSThomas Graf 	if (rt->rt6i_table)
28809e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2881c71099acSThomas Graf 	else
28829e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
28839e762a4aSPatrick McHardy 	rtm->rtm_table = table;
2884c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
2885c78679e8SDavid S. Miller 		goto nla_put_failure;
2886ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
2887ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
2888ef2c7d7bSNicolas Dichtel 		case -EINVAL:
2889ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
2890ef2c7d7bSNicolas Dichtel 			break;
2891ef2c7d7bSNicolas Dichtel 		case -EACCES:
2892ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
2893ef2c7d7bSNicolas Dichtel 			break;
2894b4949ab2SNicolas Dichtel 		case -EAGAIN:
2895b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
2896b4949ab2SNicolas Dichtel 			break;
2897ef2c7d7bSNicolas Dichtel 		default:
28981da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
2899ef2c7d7bSNicolas Dichtel 			break;
2900ef2c7d7bSNicolas Dichtel 		}
2901ef2c7d7bSNicolas Dichtel 	}
2902ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2903ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2904d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
29051da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
29061da177e4SLinus Torvalds 	else
29071da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
29081da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
290935103d11SAndy Gospodarek 	if (!netif_carrier_ok(rt->dst.dev)) {
2910cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
291135103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
291235103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
291335103d11SAndy Gospodarek 	}
29141da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
29151da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
29161da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
29171da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
2918f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
2919f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
29201da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
2921f0396f60SDenis Ovsienko 		else
2922f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
2923f0396f60SDenis Ovsienko 	}
29241da177e4SLinus Torvalds 
29251da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
29261da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
29271da177e4SLinus Torvalds 
29281da177e4SLinus Torvalds 	if (dst) {
2929930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
2930c78679e8SDavid S. Miller 			goto nla_put_failure;
29311da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
29321da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
2933930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
2934c78679e8SDavid S. Miller 			goto nla_put_failure;
29351da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
29361da177e4SLinus Torvalds 	if (src) {
2937930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
2938c78679e8SDavid S. Miller 			goto nla_put_failure;
29391da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
2940c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
2941930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
2942c78679e8SDavid S. Miller 		goto nla_put_failure;
29431da177e4SLinus Torvalds #endif
29447bc570c8SYOSHIFUJI Hideaki 	if (iif) {
29457bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
29467bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
29478229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
29487bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
29497bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
29507bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
29517bc570c8SYOSHIFUJI Hideaki 						return 0;
29527bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
29537bc570c8SYOSHIFUJI Hideaki 				} else {
29547bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
29557bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
29567bc570c8SYOSHIFUJI Hideaki 				}
29577bc570c8SYOSHIFUJI Hideaki 			}
29587bc570c8SYOSHIFUJI Hideaki 		} else
29597bc570c8SYOSHIFUJI Hideaki #endif
2960c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
2961c78679e8SDavid S. Miller 				goto nla_put_failure;
29627bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
29631da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2964c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2965930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2966c78679e8SDavid S. Miller 			goto nla_put_failure;
2967c3968a85SDaniel Walter 	}
2968c3968a85SDaniel Walter 
2969c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2970c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
29714e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
2972930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2973c78679e8SDavid S. Miller 			goto nla_put_failure;
29741da177e4SLinus Torvalds 	}
29752d7202bfSThomas Graf 
29764b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
29774b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
29784b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
29794b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
29802d7202bfSThomas Graf 		goto nla_put_failure;
29812d7202bfSThomas Graf 
2982dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
2983930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
298494f826b8SEric Dumazet 			goto nla_put_failure;
298594f826b8SEric Dumazet 	}
29862d7202bfSThomas Graf 
2987c78679e8SDavid S. Miller 	if (rt->dst.dev &&
2988c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2989c78679e8SDavid S. Miller 		goto nla_put_failure;
2990c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2991c78679e8SDavid S. Miller 		goto nla_put_failure;
29928253947eSLi Wei 
29938253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
299469cdf8f9SYOSHIFUJI Hideaki 
299587a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
2996e3703b3dSThomas Graf 		goto nla_put_failure;
29971da177e4SLinus Torvalds 
2998c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
2999c78ba6d6SLubomir Rintel 		goto nla_put_failure;
3000c78ba6d6SLubomir Rintel 
300161adedf3SJiri Benc 	lwtunnel_fill_encap(skb, rt->dst.lwtstate);
300219e42e45SRoopa Prabhu 
3003053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
3004053c095aSJohannes Berg 	return 0;
30052d7202bfSThomas Graf 
30062d7202bfSThomas Graf nla_put_failure:
300726932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
300826932566SPatrick McHardy 	return -EMSGSIZE;
30091da177e4SLinus Torvalds }
30101da177e4SLinus Torvalds 
30111b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
30121da177e4SLinus Torvalds {
30131da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
30141da177e4SLinus Torvalds 	int prefix;
30151da177e4SLinus Torvalds 
30162d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
30172d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
30181da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
30191da177e4SLinus Torvalds 	} else
30201da177e4SLinus Torvalds 		prefix = 0;
30211da177e4SLinus Torvalds 
3022191cd582SBrian Haley 	return rt6_fill_node(arg->net,
3023191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
302415e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
30257bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
30261da177e4SLinus Torvalds }
30271da177e4SLinus Torvalds 
3028661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
30291da177e4SLinus Torvalds {
30303b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3031ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
30321da177e4SLinus Torvalds 	struct rt6_info *rt;
3033ab364a6fSThomas Graf 	struct sk_buff *skb;
3034ab364a6fSThomas Graf 	struct rtmsg *rtm;
30354c9483b2SDavid S. Miller 	struct flowi6 fl6;
303672331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3037ab364a6fSThomas Graf 
3038ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3039ab364a6fSThomas Graf 	if (err < 0)
3040ab364a6fSThomas Graf 		goto errout;
3041ab364a6fSThomas Graf 
3042ab364a6fSThomas Graf 	err = -EINVAL;
30434c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
3044ab364a6fSThomas Graf 
3045ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3046ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3047ab364a6fSThomas Graf 			goto errout;
3048ab364a6fSThomas Graf 
30494e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3050ab364a6fSThomas Graf 	}
3051ab364a6fSThomas Graf 
3052ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3053ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3054ab364a6fSThomas Graf 			goto errout;
3055ab364a6fSThomas Graf 
30564e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3057ab364a6fSThomas Graf 	}
3058ab364a6fSThomas Graf 
3059ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3060ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3061ab364a6fSThomas Graf 
3062ab364a6fSThomas Graf 	if (tb[RTA_OIF])
306372331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3064ab364a6fSThomas Graf 
30652e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
30662e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
30672e47b291SLorenzo Colitti 
3068ab364a6fSThomas Graf 	if (iif) {
3069ab364a6fSThomas Graf 		struct net_device *dev;
307072331bc0SShmulik Ladkani 		int flags = 0;
307172331bc0SShmulik Ladkani 
30725578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3073ab364a6fSThomas Graf 		if (!dev) {
3074ab364a6fSThomas Graf 			err = -ENODEV;
3075ab364a6fSThomas Graf 			goto errout;
3076ab364a6fSThomas Graf 		}
307772331bc0SShmulik Ladkani 
307872331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
307972331bc0SShmulik Ladkani 
308072331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
308172331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
308272331bc0SShmulik Ladkani 
308372331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
308472331bc0SShmulik Ladkani 							       flags);
308572331bc0SShmulik Ladkani 	} else {
308672331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
308772331bc0SShmulik Ladkani 
308872331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3089ab364a6fSThomas Graf 	}
30901da177e4SLinus Torvalds 
30911da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
309238308473SDavid S. Miller 	if (!skb) {
309394e187c0SAmerigo Wang 		ip6_rt_put(rt);
3094ab364a6fSThomas Graf 		err = -ENOBUFS;
3095ab364a6fSThomas Graf 		goto errout;
3096ab364a6fSThomas Graf 	}
30971da177e4SLinus Torvalds 
30981da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
30991da177e4SLinus Torvalds 	   through good chunk of routing engine.
31001da177e4SLinus Torvalds 	 */
3101459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
31021da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
31031da177e4SLinus Torvalds 
3104d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
31051da177e4SLinus Torvalds 
31064c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
310715e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
31087bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
31091da177e4SLinus Torvalds 	if (err < 0) {
3110ab364a6fSThomas Graf 		kfree_skb(skb);
3111ab364a6fSThomas Graf 		goto errout;
31121da177e4SLinus Torvalds 	}
31131da177e4SLinus Torvalds 
311415e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3115ab364a6fSThomas Graf errout:
31161da177e4SLinus Torvalds 	return err;
31171da177e4SLinus Torvalds }
31181da177e4SLinus Torvalds 
311986872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
31201da177e4SLinus Torvalds {
31211da177e4SLinus Torvalds 	struct sk_buff *skb;
31225578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3123528c4cebSDenis V. Lunev 	u32 seq;
3124528c4cebSDenis V. Lunev 	int err;
31250d51aa80SJamal Hadi Salim 
3126528c4cebSDenis V. Lunev 	err = -ENOBUFS;
312738308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
312886872cb5SThomas Graf 
312919e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
313038308473SDavid S. Miller 	if (!skb)
313121713ebcSThomas Graf 		goto errout;
31321da177e4SLinus Torvalds 
3133191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
313415e47304SEric W. Biederman 				event, info->portid, seq, 0, 0, 0);
313526932566SPatrick McHardy 	if (err < 0) {
313626932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
313726932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
313826932566SPatrick McHardy 		kfree_skb(skb);
313926932566SPatrick McHardy 		goto errout;
314026932566SPatrick McHardy 	}
314115e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
31425578689aSDaniel Lezcano 		    info->nlh, gfp_any());
31431ce85fe4SPablo Neira Ayuso 	return;
314421713ebcSThomas Graf errout:
314521713ebcSThomas Graf 	if (err < 0)
31465578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
31471da177e4SLinus Torvalds }
31481da177e4SLinus Torvalds 
31498ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3150351638e7SJiri Pirko 				unsigned long event, void *ptr)
31518ed67789SDaniel Lezcano {
3152351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3153c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
31548ed67789SDaniel Lezcano 
31558ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3156d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
31578ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
31588ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3159d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
31608ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3161d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
31628ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
31638ed67789SDaniel Lezcano #endif
31648ed67789SDaniel Lezcano 	}
31658ed67789SDaniel Lezcano 
31668ed67789SDaniel Lezcano 	return NOTIFY_OK;
31678ed67789SDaniel Lezcano }
31688ed67789SDaniel Lezcano 
31691da177e4SLinus Torvalds /*
31701da177e4SLinus Torvalds  *	/proc
31711da177e4SLinus Torvalds  */
31721da177e4SLinus Torvalds 
31731da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
31741da177e4SLinus Torvalds 
317533120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
317633120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
317733120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
317833120b30SAlexey Dobriyan 	.read		= seq_read,
317933120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
31808d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
318133120b30SAlexey Dobriyan };
318233120b30SAlexey Dobriyan 
31831da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
31841da177e4SLinus Torvalds {
318569ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
31861da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
318769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
318869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
318969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
319069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
319169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3192fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
319369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
31941da177e4SLinus Torvalds 
31951da177e4SLinus Torvalds 	return 0;
31961da177e4SLinus Torvalds }
31971da177e4SLinus Torvalds 
31981da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
31991da177e4SLinus Torvalds {
3200de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
320169ddb805SDaniel Lezcano }
320269ddb805SDaniel Lezcano 
32039a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
32041da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
32051da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
32061da177e4SLinus Torvalds 	.read	 = seq_read,
32071da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3208b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
32091da177e4SLinus Torvalds };
32101da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
32111da177e4SLinus Torvalds 
32121da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
32131da177e4SLinus Torvalds 
32141da177e4SLinus Torvalds static
3215fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
32161da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
32171da177e4SLinus Torvalds {
3218c486da34SLucian Adrian Grijincu 	struct net *net;
3219c486da34SLucian Adrian Grijincu 	int delay;
3220c486da34SLucian Adrian Grijincu 	if (!write)
3221c486da34SLucian Adrian Grijincu 		return -EINVAL;
3222c486da34SLucian Adrian Grijincu 
3223c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3224c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
32258d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
32262ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
32271da177e4SLinus Torvalds 	return 0;
32281da177e4SLinus Torvalds }
32291da177e4SLinus Torvalds 
3230fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
32311da177e4SLinus Torvalds 	{
32321da177e4SLinus Torvalds 		.procname	=	"flush",
32334990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
32341da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
323589c8b3a1SDave Jones 		.mode		=	0200,
32366d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
32371da177e4SLinus Torvalds 	},
32381da177e4SLinus Torvalds 	{
32391da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
32409a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
32411da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32421da177e4SLinus Torvalds 		.mode		=	0644,
32436d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
32441da177e4SLinus Torvalds 	},
32451da177e4SLinus Torvalds 	{
32461da177e4SLinus Torvalds 		.procname	=	"max_size",
32474990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
32481da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32491da177e4SLinus Torvalds 		.mode		=	0644,
32506d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
32511da177e4SLinus Torvalds 	},
32521da177e4SLinus Torvalds 	{
32531da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
32544990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
32551da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32561da177e4SLinus Torvalds 		.mode		=	0644,
32576d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32581da177e4SLinus Torvalds 	},
32591da177e4SLinus Torvalds 	{
32601da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
32614990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
32621da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32631da177e4SLinus Torvalds 		.mode		=	0644,
32646d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32651da177e4SLinus Torvalds 	},
32661da177e4SLinus Torvalds 	{
32671da177e4SLinus Torvalds 		.procname	=	"gc_interval",
32684990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
32691da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32701da177e4SLinus Torvalds 		.mode		=	0644,
32716d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32721da177e4SLinus Torvalds 	},
32731da177e4SLinus Torvalds 	{
32741da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
32754990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
32761da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32771da177e4SLinus Torvalds 		.mode		=	0644,
3278f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
32791da177e4SLinus Torvalds 	},
32801da177e4SLinus Torvalds 	{
32811da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
32824990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
32831da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32841da177e4SLinus Torvalds 		.mode		=	0644,
32856d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
32861da177e4SLinus Torvalds 	},
32871da177e4SLinus Torvalds 	{
32881da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
32894990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
32901da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32911da177e4SLinus Torvalds 		.mode		=	0644,
3292f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
32931da177e4SLinus Torvalds 	},
32941da177e4SLinus Torvalds 	{
32951da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
32964990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
32971da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
32981da177e4SLinus Torvalds 		.mode		=	0644,
32996d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
33001da177e4SLinus Torvalds 	},
3301f8572d8fSEric W. Biederman 	{ }
33021da177e4SLinus Torvalds };
33031da177e4SLinus Torvalds 
33042c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3305760f2d01SDaniel Lezcano {
3306760f2d01SDaniel Lezcano 	struct ctl_table *table;
3307760f2d01SDaniel Lezcano 
3308760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3309760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3310760f2d01SDaniel Lezcano 			GFP_KERNEL);
33115ee09105SYOSHIFUJI Hideaki 
33125ee09105SYOSHIFUJI Hideaki 	if (table) {
33135ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3314c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
331586393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
33165ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
33175ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
33185ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
33195ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
33205ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
33215ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
33225ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
33239c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3324464dc801SEric W. Biederman 
3325464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3326464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3327464dc801SEric W. Biederman 			table[0].procname = NULL;
33285ee09105SYOSHIFUJI Hideaki 	}
33295ee09105SYOSHIFUJI Hideaki 
3330760f2d01SDaniel Lezcano 	return table;
3331760f2d01SDaniel Lezcano }
33321da177e4SLinus Torvalds #endif
33331da177e4SLinus Torvalds 
33342c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3335cdb18761SDaniel Lezcano {
3336633d424bSPavel Emelyanov 	int ret = -ENOMEM;
33378ed67789SDaniel Lezcano 
333886393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
333986393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3340f2fc6a54SBenjamin Thery 
3341fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3342fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3343fc66f95cSEric Dumazet 
33448ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
33458ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
33468ed67789SDaniel Lezcano 					   GFP_KERNEL);
33478ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3348fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3349d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
33508ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3351d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
335262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
335362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
33548ed67789SDaniel Lezcano 
33558ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
33568ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
33578ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
33588ed67789SDaniel Lezcano 					       GFP_KERNEL);
335968fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
336068fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3361d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
33628ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3363d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
336462fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
336562fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
33668ed67789SDaniel Lezcano 
33678ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
33688ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
33698ed67789SDaniel Lezcano 					       GFP_KERNEL);
337068fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
337168fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3372d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
33738ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3374d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
337562fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
337662fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
33778ed67789SDaniel Lezcano #endif
33788ed67789SDaniel Lezcano 
3379b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3380b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3381b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3382b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3383b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3384b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3385b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3386b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3387b339a47cSPeter Zijlstra 
33886891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
33896891a346SBenjamin Thery 
33908ed67789SDaniel Lezcano 	ret = 0;
33918ed67789SDaniel Lezcano out:
33928ed67789SDaniel Lezcano 	return ret;
3393f2fc6a54SBenjamin Thery 
339468fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
339568fffc67SPeter Zijlstra out_ip6_prohibit_entry:
339668fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
339768fffc67SPeter Zijlstra out_ip6_null_entry:
339868fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
339968fffc67SPeter Zijlstra #endif
3400fc66f95cSEric Dumazet out_ip6_dst_entries:
3401fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3402f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3403f2fc6a54SBenjamin Thery 	goto out;
3404cdb18761SDaniel Lezcano }
3405cdb18761SDaniel Lezcano 
34062c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3407cdb18761SDaniel Lezcano {
34088ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
34098ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
34108ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
34118ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
34128ed67789SDaniel Lezcano #endif
341341bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3414cdb18761SDaniel Lezcano }
3415cdb18761SDaniel Lezcano 
3416d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3417d189634eSThomas Graf {
3418d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3419d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3420d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3421d189634eSThomas Graf #endif
3422d189634eSThomas Graf 	return 0;
3423d189634eSThomas Graf }
3424d189634eSThomas Graf 
3425d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3426d189634eSThomas Graf {
3427d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3428ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3429ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3430d189634eSThomas Graf #endif
3431d189634eSThomas Graf }
3432d189634eSThomas Graf 
3433cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3434cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3435cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3436cdb18761SDaniel Lezcano };
3437cdb18761SDaniel Lezcano 
3438c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3439c3426b47SDavid S. Miller {
3440c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3441c3426b47SDavid S. Miller 
3442c3426b47SDavid S. Miller 	if (!bp)
3443c3426b47SDavid S. Miller 		return -ENOMEM;
3444c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3445c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3446c3426b47SDavid S. Miller 	return 0;
3447c3426b47SDavid S. Miller }
3448c3426b47SDavid S. Miller 
3449c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3450c3426b47SDavid S. Miller {
3451c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3452c3426b47SDavid S. Miller 
3453c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
345456a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3455c3426b47SDavid S. Miller 	kfree(bp);
3456c3426b47SDavid S. Miller }
3457c3426b47SDavid S. Miller 
34582b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3459c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3460c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3461c3426b47SDavid S. Miller };
3462c3426b47SDavid S. Miller 
3463d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3464d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3465d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3466d189634eSThomas Graf };
3467d189634eSThomas Graf 
34688ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
34698ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
34708ed67789SDaniel Lezcano 	.priority = 0,
34718ed67789SDaniel Lezcano };
34728ed67789SDaniel Lezcano 
3473433d49c3SDaniel Lezcano int __init ip6_route_init(void)
34741da177e4SLinus Torvalds {
3475433d49c3SDaniel Lezcano 	int ret;
34768d0b94afSMartin KaFai Lau 	int cpu;
3477433d49c3SDaniel Lezcano 
34789a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
34799a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
34809a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
34819a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
34829a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3483c19a28e1SFernando Carrijo 		goto out;
348414e50e57SDavid S. Miller 
3485fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
34868ed67789SDaniel Lezcano 	if (ret)
3487bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3488bdb3289fSDaniel Lezcano 
3489c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3490c3426b47SDavid S. Miller 	if (ret)
3491e8803b6cSDavid S. Miller 		goto out_dst_entries;
34922a0c451aSThomas Graf 
34937e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
34947e52b33bSDavid S. Miller 	if (ret)
34957e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3496c3426b47SDavid S. Miller 
34975dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
34985dc121e9SArnaud Ebalard 
34998ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
35008ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
35018ed67789SDaniel Lezcano 	 * manually for init_net */
3502d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
35038ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3504bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3505d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
35068ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3507d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
35088ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3509bdb3289fSDaniel Lezcano   #endif
3510e8803b6cSDavid S. Miller 	ret = fib6_init();
3511433d49c3SDaniel Lezcano 	if (ret)
35128ed67789SDaniel Lezcano 		goto out_register_subsys;
3513433d49c3SDaniel Lezcano 
3514433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3515433d49c3SDaniel Lezcano 	if (ret)
3516e8803b6cSDavid S. Miller 		goto out_fib6_init;
3517c35b7e72SDaniel Lezcano 
3518433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3519433d49c3SDaniel Lezcano 	if (ret)
3520433d49c3SDaniel Lezcano 		goto xfrm6_init;
35217e5449c2SDaniel Lezcano 
3522d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3523d189634eSThomas Graf 	if (ret)
3524d189634eSThomas Graf 		goto fib6_rules_init;
3525d189634eSThomas Graf 
3526433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3527c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3528c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3529c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3530d189634eSThomas Graf 		goto out_register_late_subsys;
3531433d49c3SDaniel Lezcano 
35328ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3533cdb18761SDaniel Lezcano 	if (ret)
3534d189634eSThomas Graf 		goto out_register_late_subsys;
35358ed67789SDaniel Lezcano 
35368d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
35378d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
35388d0b94afSMartin KaFai Lau 
35398d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
35408d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
35418d0b94afSMartin KaFai Lau 	}
35428d0b94afSMartin KaFai Lau 
3543433d49c3SDaniel Lezcano out:
3544433d49c3SDaniel Lezcano 	return ret;
3545433d49c3SDaniel Lezcano 
3546d189634eSThomas Graf out_register_late_subsys:
3547d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3548433d49c3SDaniel Lezcano fib6_rules_init:
3549433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3550433d49c3SDaniel Lezcano xfrm6_init:
3551433d49c3SDaniel Lezcano 	xfrm6_fini();
35522a0c451aSThomas Graf out_fib6_init:
35532a0c451aSThomas Graf 	fib6_gc_cleanup();
35548ed67789SDaniel Lezcano out_register_subsys:
35558ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
35567e52b33bSDavid S. Miller out_register_inetpeer:
35577e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3558fc66f95cSEric Dumazet out_dst_entries:
3559fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3560433d49c3SDaniel Lezcano out_kmem_cache:
3561f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3562433d49c3SDaniel Lezcano 	goto out;
35631da177e4SLinus Torvalds }
35641da177e4SLinus Torvalds 
35651da177e4SLinus Torvalds void ip6_route_cleanup(void)
35661da177e4SLinus Torvalds {
35678ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3568d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3569101367c2SThomas Graf 	fib6_rules_cleanup();
35701da177e4SLinus Torvalds 	xfrm6_fini();
35711da177e4SLinus Torvalds 	fib6_gc_cleanup();
3572c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
35738ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
357441bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3575f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
35761da177e4SLinus Torvalds }
3577