xref: /openbmc/linux/net/ipv6/route.c (revision 0d3f6d297bfb7af24d0508460fdb3d1ec4903fa3)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Linux INET6 implementation
31da177e4SLinus Torvalds  *	FIB front-end.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
91da177e4SLinus Torvalds  *      modify it under the terms of the GNU General Public License
101da177e4SLinus Torvalds  *      as published by the Free Software Foundation; either version
111da177e4SLinus Torvalds  *      2 of the License, or (at your option) any later version.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds /*	Changes:
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki @USAGI
171da177e4SLinus Torvalds  *		reworked default router selection.
181da177e4SLinus Torvalds  *		- respect outgoing interface
191da177e4SLinus Torvalds  *		- select from (probably) reachable routers (i.e.
201da177e4SLinus Torvalds  *		routers in REACHABLE, STALE, DELAY or PROBE states).
211da177e4SLinus Torvalds  *		- always select the same router if it is (probably)
221da177e4SLinus Torvalds  *		reachable.  otherwise, round-robin the list.
23c0bece9fSYOSHIFUJI Hideaki  *	Ville Nuorvala
24c0bece9fSYOSHIFUJI Hideaki  *		Fixed routing subtrees.
251da177e4SLinus Torvalds  */
261da177e4SLinus Torvalds 
27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt
28f3213831SJoe Perches 
294fc268d2SRandy Dunlap #include <linux/capability.h>
301da177e4SLinus Torvalds #include <linux/errno.h>
31bc3b2d7fSPaul Gortmaker #include <linux/export.h>
321da177e4SLinus Torvalds #include <linux/types.h>
331da177e4SLinus Torvalds #include <linux/times.h>
341da177e4SLinus Torvalds #include <linux/socket.h>
351da177e4SLinus Torvalds #include <linux/sockios.h>
361da177e4SLinus Torvalds #include <linux/net.h>
371da177e4SLinus Torvalds #include <linux/route.h>
381da177e4SLinus Torvalds #include <linux/netdevice.h>
391da177e4SLinus Torvalds #include <linux/in6.h>
407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
411da177e4SLinus Torvalds #include <linux/init.h>
421da177e4SLinus Torvalds #include <linux/if_arp.h>
431da177e4SLinus Torvalds #include <linux/proc_fs.h>
441da177e4SLinus Torvalds #include <linux/seq_file.h>
455b7c931dSDaniel Lezcano #include <linux/nsproxy.h>
465a0e3ad6STejun Heo #include <linux/slab.h>
47457c4cbcSEric W. Biederman #include <net/net_namespace.h>
481da177e4SLinus Torvalds #include <net/snmp.h>
491da177e4SLinus Torvalds #include <net/ipv6.h>
501da177e4SLinus Torvalds #include <net/ip6_fib.h>
511da177e4SLinus Torvalds #include <net/ip6_route.h>
521da177e4SLinus Torvalds #include <net/ndisc.h>
531da177e4SLinus Torvalds #include <net/addrconf.h>
541da177e4SLinus Torvalds #include <net/tcp.h>
551da177e4SLinus Torvalds #include <linux/rtnetlink.h>
561da177e4SLinus Torvalds #include <net/dst.h>
57904af04dSJiri Benc #include <net/dst_metadata.h>
581da177e4SLinus Torvalds #include <net/xfrm.h>
598d71740cSTom Tucker #include <net/netevent.h>
6021713ebcSThomas Graf #include <net/netlink.h>
6151ebd318SNicolas Dichtel #include <net/nexthop.h>
6219e42e45SRoopa Prabhu #include <net/lwtunnel.h>
63904af04dSJiri Benc #include <net/ip_tunnels.h>
64ca254490SDavid Ahern #include <net/l3mdev.h>
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds #include <asm/uaccess.h>
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
691da177e4SLinus Torvalds #include <linux/sysctl.h>
701da177e4SLinus Torvalds #endif
711da177e4SLinus Torvalds 
72afc154e9SHannes Frederic Sowa enum rt6_nud_state {
737e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
747e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
757e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
76afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
77afc154e9SHannes Frederic Sowa };
78afc154e9SHannes Frederic Sowa 
7983a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort);
801da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
810dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
82ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
831da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
841da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
851da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
861da177e4SLinus Torvalds 				       struct net_device *dev, int how);
87569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
90ede2059dSEric W. Biederman static int		ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
917150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
92ede2059dSEric W. Biederman static int		ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
931da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
946700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
956700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
966700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
976700c270SDavid S. Miller 					struct sk_buff *skb);
984b32b5adSMartin KaFai Lau static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
9952bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
1001da177e4SLinus Torvalds 
10170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
102efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
103b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
104b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
10595c96174SEric Dumazet 					   unsigned int pref);
106efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
107b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
108b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
10970ceb4f5SYOSHIFUJI Hideaki #endif
11070ceb4f5SYOSHIFUJI Hideaki 
1118d0b94afSMartin KaFai Lau struct uncached_list {
1128d0b94afSMartin KaFai Lau 	spinlock_t		lock;
1138d0b94afSMartin KaFai Lau 	struct list_head	head;
1148d0b94afSMartin KaFai Lau };
1158d0b94afSMartin KaFai Lau 
1168d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1178d0b94afSMartin KaFai Lau 
1188d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt)
1198d0b94afSMartin KaFai Lau {
1208d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1218d0b94afSMartin KaFai Lau 
1228d0b94afSMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
1238d0b94afSMartin KaFai Lau 	rt->rt6i_uncached_list = ul;
1248d0b94afSMartin KaFai Lau 
1258d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
1268d0b94afSMartin KaFai Lau 	list_add_tail(&rt->rt6i_uncached, &ul->head);
1278d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
1288d0b94afSMartin KaFai Lau }
1298d0b94afSMartin KaFai Lau 
1308d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt)
1318d0b94afSMartin KaFai Lau {
1328d0b94afSMartin KaFai Lau 	if (!list_empty(&rt->rt6i_uncached)) {
1338d0b94afSMartin KaFai Lau 		struct uncached_list *ul = rt->rt6i_uncached_list;
1348d0b94afSMartin KaFai Lau 
1358d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1368d0b94afSMartin KaFai Lau 		list_del(&rt->rt6i_uncached);
1378d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1388d0b94afSMartin KaFai Lau 	}
1398d0b94afSMartin KaFai Lau }
1408d0b94afSMartin KaFai Lau 
1418d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
1428d0b94afSMartin KaFai Lau {
1438d0b94afSMartin KaFai Lau 	struct net_device *loopback_dev = net->loopback_dev;
1448d0b94afSMartin KaFai Lau 	int cpu;
1458d0b94afSMartin KaFai Lau 
146e332bc67SEric W. Biederman 	if (dev == loopback_dev)
147e332bc67SEric W. Biederman 		return;
148e332bc67SEric W. Biederman 
1498d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
1508d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
1518d0b94afSMartin KaFai Lau 		struct rt6_info *rt;
1528d0b94afSMartin KaFai Lau 
1538d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1548d0b94afSMartin KaFai Lau 		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
1558d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
1568d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
1578d0b94afSMartin KaFai Lau 
158e332bc67SEric W. Biederman 			if (rt_idev->dev == dev) {
1598d0b94afSMartin KaFai Lau 				rt->rt6i_idev = in6_dev_get(loopback_dev);
1608d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
1618d0b94afSMartin KaFai Lau 			}
1628d0b94afSMartin KaFai Lau 
163e332bc67SEric W. Biederman 			if (rt_dev == dev) {
1648d0b94afSMartin KaFai Lau 				rt->dst.dev = loopback_dev;
1658d0b94afSMartin KaFai Lau 				dev_hold(rt->dst.dev);
1668d0b94afSMartin KaFai Lau 				dev_put(rt_dev);
1678d0b94afSMartin KaFai Lau 			}
1688d0b94afSMartin KaFai Lau 		}
1698d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1708d0b94afSMartin KaFai Lau 	}
1718d0b94afSMartin KaFai Lau }
1728d0b94afSMartin KaFai Lau 
173d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
174d52d3997SMartin KaFai Lau {
175d52d3997SMartin KaFai Lau 	return dst_metrics_write_ptr(rt->dst.from);
176d52d3997SMartin KaFai Lau }
177d52d3997SMartin KaFai Lau 
17806582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
17906582540SDavid S. Miller {
18006582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
18106582540SDavid S. Miller 
182d52d3997SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU)
183d52d3997SMartin KaFai Lau 		return rt6_pcpu_cow_metrics(rt);
184d52d3997SMartin KaFai Lau 	else if (rt->rt6i_flags & RTF_CACHE)
1854b32b5adSMartin KaFai Lau 		return NULL;
1864b32b5adSMartin KaFai Lau 	else
1873b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
18806582540SDavid S. Miller }
18906582540SDavid S. Miller 
190f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
191f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
192f894cbf8SDavid S. Miller 					     const void *daddr)
19339232973SDavid S. Miller {
19439232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
19539232973SDavid S. Miller 
196a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
19739232973SDavid S. Miller 		return (const void *) p;
198f894cbf8SDavid S. Miller 	else if (skb)
199f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
20039232973SDavid S. Miller 	return daddr;
20139232973SDavid S. Miller }
20239232973SDavid S. Miller 
203f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
204f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
205f894cbf8SDavid S. Miller 					  const void *daddr)
206d3aaeb38SDavid S. Miller {
20739232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
20839232973SDavid S. Miller 	struct neighbour *n;
20939232973SDavid S. Miller 
210f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
2118e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
212f83c7790SDavid S. Miller 	if (n)
213f83c7790SDavid S. Miller 		return n;
214f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
215f83c7790SDavid S. Miller }
216f83c7790SDavid S. Miller 
2179a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2181da177e4SLinus Torvalds 	.family			=	AF_INET6,
2191da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2201da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2211da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2220dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
223ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
22406582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
2251da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2261da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2271da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2281da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2291da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2306e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2319f8955ccSEric W. Biederman 	.local_out		=	__ip6_local_out,
232d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
2331da177e4SLinus Torvalds };
2341da177e4SLinus Torvalds 
235ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
236ec831ea7SRoland Dreier {
237618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
238618f9bc7SSteffen Klassert 
239618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
240ec831ea7SRoland Dreier }
241ec831ea7SRoland Dreier 
2426700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2436700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
24414e50e57SDavid S. Miller {
24514e50e57SDavid S. Miller }
24614e50e57SDavid S. Miller 
2476700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2486700c270SDavid S. Miller 				      struct sk_buff *skb)
249b587ee3bSDavid S. Miller {
250b587ee3bSDavid S. Miller }
251b587ee3bSDavid S. Miller 
25214e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
25314e50e57SDavid S. Miller 	.family			=	AF_INET6,
25414e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
25514e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
256ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
257214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
25814e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
259b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2600a1f5962SMartin KaFai Lau 	.cow_metrics		=	dst_cow_metrics_generic,
261d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
26214e50e57SDavid S. Miller };
26314e50e57SDavid S. Miller 
26462fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
26514edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
26662fa8a84SDavid S. Miller };
26762fa8a84SDavid S. Miller 
268fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2691da177e4SLinus Torvalds 	.dst = {
2701da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2711da177e4SLinus Torvalds 		.__use		= 1,
2722c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2731da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2741da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2751da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2761da177e4SLinus Torvalds 	},
2771da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2784f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2791da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2801da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2811da177e4SLinus Torvalds };
2821da177e4SLinus Torvalds 
283101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
284101367c2SThomas Graf 
285fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
286101367c2SThomas Graf 	.dst = {
287101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
288101367c2SThomas Graf 		.__use		= 1,
2892c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
290101367c2SThomas Graf 		.error		= -EACCES,
2919ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2929ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
293101367c2SThomas Graf 	},
294101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2954f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
296101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
297101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
298101367c2SThomas Graf };
299101367c2SThomas Graf 
300fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
301101367c2SThomas Graf 	.dst = {
302101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
303101367c2SThomas Graf 		.__use		= 1,
3042c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
305101367c2SThomas Graf 		.error		= -EINVAL,
306352e512cSHerbert Xu 		.input		= dst_discard,
307ede2059dSEric W. Biederman 		.output		= dst_discard_out,
308101367c2SThomas Graf 	},
309101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3104f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
311101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
312101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
313101367c2SThomas Graf };
314101367c2SThomas Graf 
315101367c2SThomas Graf #endif
316101367c2SThomas Graf 
317ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt)
318ebfa45f0SMartin KaFai Lau {
319ebfa45f0SMartin KaFai Lau 	struct dst_entry *dst = &rt->dst;
320ebfa45f0SMartin KaFai Lau 
321ebfa45f0SMartin KaFai Lau 	memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
322ebfa45f0SMartin KaFai Lau 	INIT_LIST_HEAD(&rt->rt6i_siblings);
323ebfa45f0SMartin KaFai Lau 	INIT_LIST_HEAD(&rt->rt6i_uncached);
324ebfa45f0SMartin KaFai Lau }
325ebfa45f0SMartin KaFai Lau 
3261da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
327d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net,
328957c665fSDavid S. Miller 					struct net_device *dev,
329ad706862SMartin KaFai Lau 					int flags)
3301da177e4SLinus Torvalds {
33197bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3326f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
333cf911662SDavid S. Miller 
334ebfa45f0SMartin KaFai Lau 	if (rt)
335ebfa45f0SMartin KaFai Lau 		rt6_info_init(rt);
3368104891bSSteffen Klassert 
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,
342ad706862SMartin KaFai Lau 				      int flags)
343d52d3997SMartin KaFai Lau {
344ad706862SMartin KaFai Lau 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
345d52d3997SMartin KaFai Lau 
346d52d3997SMartin KaFai Lau 	if (rt) {
347d52d3997SMartin KaFai Lau 		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
348d52d3997SMartin KaFai Lau 		if (rt->rt6i_pcpu) {
349d52d3997SMartin KaFai Lau 			int cpu;
350d52d3997SMartin KaFai Lau 
351d52d3997SMartin KaFai Lau 			for_each_possible_cpu(cpu) {
352d52d3997SMartin KaFai Lau 				struct rt6_info **p;
353d52d3997SMartin KaFai Lau 
354d52d3997SMartin KaFai Lau 				p = per_cpu_ptr(rt->rt6i_pcpu, cpu);
355d52d3997SMartin KaFai Lau 				/* no one shares rt */
356d52d3997SMartin KaFai Lau 				*p =  NULL;
357d52d3997SMartin KaFai Lau 			}
358d52d3997SMartin KaFai Lau 		} else {
359d52d3997SMartin KaFai Lau 			dst_destroy((struct dst_entry *)rt);
360d52d3997SMartin KaFai Lau 			return NULL;
361d52d3997SMartin KaFai Lau 		}
362d52d3997SMartin KaFai Lau 	}
363d52d3997SMartin KaFai Lau 
364d52d3997SMartin KaFai Lau 	return rt;
365d52d3997SMartin KaFai Lau }
366d52d3997SMartin KaFai Lau 
3671da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3681da177e4SLinus Torvalds {
3691da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
370ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
3718d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3721da177e4SLinus Torvalds 
3738e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
374d52d3997SMartin KaFai Lau 	free_percpu(rt->rt6i_pcpu);
3758d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
3768d0b94afSMartin KaFai Lau 
3778d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
37838308473SDavid S. Miller 	if (idev) {
3791da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3801da177e4SLinus Torvalds 		in6_dev_put(idev);
3811da177e4SLinus Torvalds 	}
3821716a961SGao feng 
383ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
384ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
385b3419363SDavid S. Miller }
386b3419363SDavid S. Miller 
3871da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3881da177e4SLinus Torvalds 			   int how)
3891da177e4SLinus Torvalds {
3901da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3911da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3925a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
393c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3941da177e4SLinus Torvalds 
39597cac082SDavid S. Miller 	if (dev != loopback_dev) {
39697cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3975a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3985a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
39938308473SDavid S. Miller 			if (loopback_idev) {
4001da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
4011da177e4SLinus Torvalds 				in6_dev_put(idev);
4021da177e4SLinus Torvalds 			}
4031da177e4SLinus Torvalds 		}
40497cac082SDavid S. Miller 	}
4051da177e4SLinus Torvalds }
4061da177e4SLinus Torvalds 
407a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4081da177e4SLinus Torvalds {
4091716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4101716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
411a50feda5SEric Dumazet 			return true;
4121716a961SGao feng 	} else if (rt->dst.from) {
4133fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
4141716a961SGao feng 	}
415a50feda5SEric Dumazet 	return false;
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds 
41851ebd318SNicolas Dichtel /* Multipath route selection:
41951ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
42051ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
42151ebd318SNicolas Dichtel  */
42251ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
42351ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
42451ebd318SNicolas Dichtel {
425644d0e65STom Herbert 	return get_hash_from_flowi6(fl6) % candidate_count;
42651ebd318SNicolas Dichtel }
42751ebd318SNicolas Dichtel 
42851ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
42952bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
43052bd4c0cSNicolas Dichtel 					     int strict)
43151ebd318SNicolas Dichtel {
43251ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
43351ebd318SNicolas Dichtel 	int route_choosen;
43451ebd318SNicolas Dichtel 
43551ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
43651ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
43751ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
43851ebd318SNicolas Dichtel 	 */
43951ebd318SNicolas Dichtel 	if (route_choosen)
44051ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
44151ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
44251ebd318SNicolas Dichtel 			route_choosen--;
44351ebd318SNicolas Dichtel 			if (route_choosen == 0) {
44452bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
44552bd4c0cSNicolas Dichtel 					break;
44651ebd318SNicolas Dichtel 				match = sibling;
44751ebd318SNicolas Dichtel 				break;
44851ebd318SNicolas Dichtel 			}
44951ebd318SNicolas Dichtel 		}
45051ebd318SNicolas Dichtel 	return match;
45151ebd318SNicolas Dichtel }
45251ebd318SNicolas Dichtel 
4531da177e4SLinus Torvalds /*
454c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4551da177e4SLinus Torvalds  */
4561da177e4SLinus Torvalds 
4578ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4588ed67789SDaniel Lezcano 						    struct rt6_info *rt,
459b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4601da177e4SLinus Torvalds 						    int oif,
461d420895eSYOSHIFUJI Hideaki 						    int flags)
4621da177e4SLinus Torvalds {
4631da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4641da177e4SLinus Torvalds 	struct rt6_info *sprt;
4651da177e4SLinus Torvalds 
466dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
467dd3abc4eSYOSHIFUJI Hideaki 		goto out;
468dd3abc4eSYOSHIFUJI Hideaki 
469d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
470d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
471dd3abc4eSYOSHIFUJI Hideaki 
472dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4731da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4741da177e4SLinus Torvalds 				return sprt;
4751da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
47638308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4771da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
47817fb0b2bSDavid Ahern 					if (flags & RT6_LOOKUP_F_IFACE)
4791da177e4SLinus Torvalds 						continue;
48017fb0b2bSDavid Ahern 					if (local &&
48117fb0b2bSDavid Ahern 					    local->rt6i_idev->dev->ifindex == oif)
4821da177e4SLinus Torvalds 						continue;
4831da177e4SLinus Torvalds 				}
4841da177e4SLinus Torvalds 				local = sprt;
4851da177e4SLinus Torvalds 			}
486dd3abc4eSYOSHIFUJI Hideaki 		} else {
487dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
488dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
489dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
490dd3abc4eSYOSHIFUJI Hideaki 		}
4911da177e4SLinus Torvalds 	}
4921da177e4SLinus Torvalds 
493dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
4941da177e4SLinus Torvalds 		if (local)
4951da177e4SLinus Torvalds 			return local;
4961da177e4SLinus Torvalds 
497d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
4988ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
4991da177e4SLinus Torvalds 	}
500dd3abc4eSYOSHIFUJI Hideaki out:
5011da177e4SLinus Torvalds 	return rt;
5021da177e4SLinus Torvalds }
5031da177e4SLinus Torvalds 
50427097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
505c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
506c2f17e82SHannes Frederic Sowa 	struct work_struct work;
507c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
508c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
509c2f17e82SHannes Frederic Sowa };
510c2f17e82SHannes Frederic Sowa 
511c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
512c2f17e82SHannes Frederic Sowa {
513c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
514c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
515c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
516c2f17e82SHannes Frederic Sowa 
517c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
51838cf595bSJiri Benc 	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, NULL);
519c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
520662f5533SMichael Büsch 	kfree(work);
521c2f17e82SHannes Frederic Sowa }
522c2f17e82SHannes Frederic Sowa 
52327097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
52427097255SYOSHIFUJI Hideaki {
525990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
526f2c31e32SEric Dumazet 	struct neighbour *neigh;
52727097255SYOSHIFUJI Hideaki 	/*
52827097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
52927097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
53027097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
53127097255SYOSHIFUJI Hideaki 	 *
53227097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
53327097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
53427097255SYOSHIFUJI Hideaki 	 */
5352152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
536fdd6681dSAmerigo Wang 		return;
5372152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5382152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5392152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5408d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5418d6c31bfSMartin KaFai Lau 			goto out;
5428d6c31bfSMartin KaFai Lau 
543990edb42SMartin KaFai Lau 		work = NULL;
5442152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
545990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
546990edb42SMartin KaFai Lau 		    time_after(jiffies,
547990edb42SMartin KaFai Lau 			       neigh->updated +
548990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
549c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
550990edb42SMartin KaFai Lau 			if (work)
5517e980569SJiri Benc 				__neigh_set_probe_once(neigh);
552990edb42SMartin KaFai Lau 		}
553c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
554990edb42SMartin KaFai Lau 	} else {
555990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
556990edb42SMartin KaFai Lau 	}
557c2f17e82SHannes Frederic Sowa 
558c2f17e82SHannes Frederic Sowa 	if (work) {
559c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
560c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
561c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
562c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
563c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
564c2f17e82SHannes Frederic Sowa 	}
565990edb42SMartin KaFai Lau 
5668d6c31bfSMartin KaFai Lau out:
5672152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
568f2c31e32SEric Dumazet }
56927097255SYOSHIFUJI Hideaki #else
57027097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
57127097255SYOSHIFUJI Hideaki {
57227097255SYOSHIFUJI Hideaki }
57327097255SYOSHIFUJI Hideaki #endif
57427097255SYOSHIFUJI Hideaki 
5751da177e4SLinus Torvalds /*
576554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5771da177e4SLinus Torvalds  */
578b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5791da177e4SLinus Torvalds {
580d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
581161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
582554cfb7eSYOSHIFUJI Hideaki 		return 2;
583161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
584161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
585161980f4SDavid S. Miller 		return 1;
586554cfb7eSYOSHIFUJI Hideaki 	return 0;
5871da177e4SLinus Torvalds }
5881da177e4SLinus Torvalds 
589afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
5901da177e4SLinus Torvalds {
591f2c31e32SEric Dumazet 	struct neighbour *neigh;
592afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
593f2c31e32SEric Dumazet 
5944d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
5954d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
596afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
597145a3621SYOSHIFUJI Hideaki / 吉藤英明 
598145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
599145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
600145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
601145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
602554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
603afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
604398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
605a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
606afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6077e980569SJiri Benc 		else
6087e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
609398bcbebSYOSHIFUJI Hideaki #endif
610145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
611afc154e9SHannes Frederic Sowa 	} else {
612afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6137e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
614a5a81f0bSPaul Marks 	}
615145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
616145a3621SYOSHIFUJI Hideaki / 吉藤英明 
617a5a81f0bSPaul Marks 	return ret;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
620554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
621554cfb7eSYOSHIFUJI Hideaki 			   int strict)
622554cfb7eSYOSHIFUJI Hideaki {
623a5a81f0bSPaul Marks 	int m;
6244d0c5911SYOSHIFUJI Hideaki 
6254d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
62677d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
627afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
628ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
629ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
630ebacaaa0SYOSHIFUJI Hideaki #endif
631afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
632afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
633afc154e9SHannes Frederic Sowa 		if (n < 0)
634afc154e9SHannes Frederic Sowa 			return n;
635afc154e9SHannes Frederic Sowa 	}
636554cfb7eSYOSHIFUJI Hideaki 	return m;
637554cfb7eSYOSHIFUJI Hideaki }
638554cfb7eSYOSHIFUJI Hideaki 
639f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
640afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
641afc154e9SHannes Frederic Sowa 				   bool *do_rr)
642554cfb7eSYOSHIFUJI Hideaki {
643554cfb7eSYOSHIFUJI Hideaki 	int m;
644afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
64535103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
64635103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
64735103d11SAndy Gospodarek 
64835103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
64935103d11SAndy Gospodarek 	    idev->cnf.ignore_routes_with_linkdown)
65035103d11SAndy Gospodarek 		goto out;
651554cfb7eSYOSHIFUJI Hideaki 
652554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
653f11e6659SDavid S. Miller 		goto out;
654554cfb7eSYOSHIFUJI Hideaki 
655554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6567e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
657afc154e9SHannes Frederic Sowa 		match_do_rr = true;
658afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6597e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
660f11e6659SDavid S. Miller 		goto out;
6611da177e4SLinus Torvalds 	}
662f11e6659SDavid S. Miller 
663afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
664afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
665afc154e9SHannes Frederic Sowa 
6667e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
667afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
668afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
669afc154e9SHannes Frederic Sowa 		*mpri = m;
670afc154e9SHannes Frederic Sowa 		match = rt;
671afc154e9SHannes Frederic Sowa 	}
672f11e6659SDavid S. Miller out:
673f11e6659SDavid S. Miller 	return match;
6741da177e4SLinus Torvalds }
6751da177e4SLinus Torvalds 
676f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
677f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
678afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
679afc154e9SHannes Frederic Sowa 				     bool *do_rr)
680f11e6659SDavid S. Miller {
6819fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
682f11e6659SDavid S. Miller 	int mpri = -1;
683f11e6659SDavid S. Miller 
684f11e6659SDavid S. Miller 	match = NULL;
6859fbdcfafSSteffen Klassert 	cont = NULL;
6869fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
6879fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6889fbdcfafSSteffen Klassert 			cont = rt;
6899fbdcfafSSteffen Klassert 			break;
6909fbdcfafSSteffen Klassert 		}
6919fbdcfafSSteffen Klassert 
692afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6939fbdcfafSSteffen Klassert 	}
6949fbdcfafSSteffen Klassert 
6959fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
6969fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6979fbdcfafSSteffen Klassert 			cont = rt;
6989fbdcfafSSteffen Klassert 			break;
6999fbdcfafSSteffen Klassert 		}
7009fbdcfafSSteffen Klassert 
7019fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7029fbdcfafSSteffen Klassert 	}
7039fbdcfafSSteffen Klassert 
7049fbdcfafSSteffen Klassert 	if (match || !cont)
7059fbdcfafSSteffen Klassert 		return match;
7069fbdcfafSSteffen Klassert 
7079fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
708afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
709f11e6659SDavid S. Miller 
710f11e6659SDavid S. Miller 	return match;
711f11e6659SDavid S. Miller }
712f11e6659SDavid S. Miller 
713f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
714f11e6659SDavid S. Miller {
715f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7168ed67789SDaniel Lezcano 	struct net *net;
717afc154e9SHannes Frederic Sowa 	bool do_rr = false;
718f11e6659SDavid S. Miller 
719f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
720f11e6659SDavid S. Miller 	if (!rt0)
721f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
722f11e6659SDavid S. Miller 
723afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
724afc154e9SHannes Frederic Sowa 			     &do_rr);
725f11e6659SDavid S. Miller 
726afc154e9SHannes Frederic Sowa 	if (do_rr) {
727d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
728f11e6659SDavid S. Miller 
729554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
730f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
731f11e6659SDavid S. Miller 			next = fn->leaf;
732f11e6659SDavid S. Miller 
733f11e6659SDavid S. Miller 		if (next != rt0)
734f11e6659SDavid S. Miller 			fn->rr_ptr = next;
735554cfb7eSYOSHIFUJI Hideaki 	}
736554cfb7eSYOSHIFUJI Hideaki 
737d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
738a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds 
7418b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7428b9df265SMartin KaFai Lau {
7438b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7448b9df265SMartin KaFai Lau }
7458b9df265SMartin KaFai Lau 
74670ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
74770ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
748b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
74970ceb4f5SYOSHIFUJI Hideaki {
750c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
75170ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
75270ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
75370ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7544bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
75570ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
75670ceb4f5SYOSHIFUJI Hideaki 
75770ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
75870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
75970ceb4f5SYOSHIFUJI Hideaki 	}
76070ceb4f5SYOSHIFUJI Hideaki 
76170ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
76270ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
76370ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
76470ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
76570ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
76670ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
76770ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
76870ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
76970ceb4f5SYOSHIFUJI Hideaki 		}
77070ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
77170ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
77270ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
77370ceb4f5SYOSHIFUJI Hideaki 		}
77470ceb4f5SYOSHIFUJI Hideaki 	}
77570ceb4f5SYOSHIFUJI Hideaki 
77670ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
77770ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
7783933fc95SJens Rosenboom 		return -EINVAL;
77970ceb4f5SYOSHIFUJI Hideaki 
7804bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
78170ceb4f5SYOSHIFUJI Hideaki 
78270ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
78370ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
78470ceb4f5SYOSHIFUJI Hideaki 	else {
78570ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
78670ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
78770ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
78870ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
78970ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
79070ceb4f5SYOSHIFUJI Hideaki 	}
79170ceb4f5SYOSHIFUJI Hideaki 
792f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
793f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
794f104a567SDuan Jiong 	else
795f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
796f104a567SDuan Jiong 					gwaddr, dev->ifindex);
79770ceb4f5SYOSHIFUJI Hideaki 
79870ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
799e0a1ad73SThomas Graf 		ip6_del_rt(rt);
80070ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
80170ceb4f5SYOSHIFUJI Hideaki 	}
80270ceb4f5SYOSHIFUJI Hideaki 
80370ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
804efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
80570ceb4f5SYOSHIFUJI Hideaki 					pref);
80670ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
80770ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
80870ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
80970ceb4f5SYOSHIFUJI Hideaki 
81070ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8111716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8121716a961SGao feng 			rt6_clean_expires(rt);
8131716a961SGao feng 		else
8141716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8151716a961SGao feng 
81694e187c0SAmerigo Wang 		ip6_rt_put(rt);
81770ceb4f5SYOSHIFUJI Hideaki 	}
81870ceb4f5SYOSHIFUJI Hideaki 	return 0;
81970ceb4f5SYOSHIFUJI Hideaki }
82070ceb4f5SYOSHIFUJI Hideaki #endif
82170ceb4f5SYOSHIFUJI Hideaki 
822a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
823a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
824a3c00e46SMartin KaFai Lau {
825a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
826a3c00e46SMartin KaFai Lau 	while (1) {
827a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
828a3c00e46SMartin KaFai Lau 			return NULL;
829a3c00e46SMartin KaFai Lau 		pn = fn->parent;
830a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
831a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
832a3c00e46SMartin KaFai Lau 		else
833a3c00e46SMartin KaFai Lau 			fn = pn;
834a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
835a3c00e46SMartin KaFai Lau 			return fn;
836a3c00e46SMartin KaFai Lau 	}
837a3c00e46SMartin KaFai Lau }
838c71099acSThomas Graf 
8398ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8408ed67789SDaniel Lezcano 					     struct fib6_table *table,
8414c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8421da177e4SLinus Torvalds {
8431da177e4SLinus Torvalds 	struct fib6_node *fn;
8441da177e4SLinus Torvalds 	struct rt6_info *rt;
8451da177e4SLinus Torvalds 
846c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8474c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
848c71099acSThomas Graf restart:
849c71099acSThomas Graf 	rt = fn->leaf;
8504c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
85151ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
85252bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
853a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
854a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
855a3c00e46SMartin KaFai Lau 		if (fn)
856a3c00e46SMartin KaFai Lau 			goto restart;
857a3c00e46SMartin KaFai Lau 	}
858d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
859c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8601da177e4SLinus Torvalds 	return rt;
861c71099acSThomas Graf 
862c71099acSThomas Graf }
863c71099acSThomas Graf 
864ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
865ea6e574eSFlorian Westphal 				    int flags)
866ea6e574eSFlorian Westphal {
867ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
868ea6e574eSFlorian Westphal }
869ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
870ea6e574eSFlorian Westphal 
8719acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8729acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
873c71099acSThomas Graf {
8744c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8754c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8764c9483b2SDavid S. Miller 		.daddr = *daddr,
877c71099acSThomas Graf 	};
878c71099acSThomas Graf 	struct dst_entry *dst;
87977d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
880c71099acSThomas Graf 
881adaa70bbSThomas Graf 	if (saddr) {
8824c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
883adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
884adaa70bbSThomas Graf 	}
885adaa70bbSThomas Graf 
8864c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
887c71099acSThomas Graf 	if (dst->error == 0)
888c71099acSThomas Graf 		return (struct rt6_info *) dst;
889c71099acSThomas Graf 
890c71099acSThomas Graf 	dst_release(dst);
891c71099acSThomas Graf 
8921da177e4SLinus Torvalds 	return NULL;
8931da177e4SLinus Torvalds }
8947159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
8957159039aSYOSHIFUJI Hideaki 
896c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
8971da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
8981da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
8991da177e4SLinus Torvalds    be destroyed.
9001da177e4SLinus Torvalds  */
9011da177e4SLinus Torvalds 
902e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
903e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9041da177e4SLinus Torvalds {
9051da177e4SLinus Torvalds 	int err;
906c71099acSThomas Graf 	struct fib6_table *table;
9071da177e4SLinus Torvalds 
908c71099acSThomas Graf 	table = rt->rt6i_table;
909c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
910e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
911c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 	return err;
9141da177e4SLinus Torvalds }
9151da177e4SLinus Torvalds 
91640e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
91740e22e8fSThomas Graf {
918e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
919e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
920e715b6d3SFlorian Westphal 
921e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
92240e22e8fSThomas Graf }
92340e22e8fSThomas Graf 
9248b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
92521efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
926b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9271da177e4SLinus Torvalds {
9281da177e4SLinus Torvalds 	struct rt6_info *rt;
9291da177e4SLinus Torvalds 
9301da177e4SLinus Torvalds 	/*
9311da177e4SLinus Torvalds 	 *	Clone the route.
9321da177e4SLinus Torvalds 	 */
9331da177e4SLinus Torvalds 
934d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
93583a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9361da177e4SLinus Torvalds 
937ad706862SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0);
93883a09abdSMartin KaFai Lau 
93983a09abdSMartin KaFai Lau 	if (!rt)
94083a09abdSMartin KaFai Lau 		return NULL;
94183a09abdSMartin KaFai Lau 
94283a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9438b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
94483a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
94583a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
94683a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
94783a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9488b9df265SMartin KaFai Lau 
9498b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
950bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
95121efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
95258c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9531da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9541da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9554e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9561da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9571da177e4SLinus Torvalds 		}
9581da177e4SLinus Torvalds #endif
95995a9a5baSYOSHIFUJI Hideaki 	}
96095a9a5baSYOSHIFUJI Hideaki 
961299d9939SYOSHIFUJI Hideaki 	return rt;
962299d9939SYOSHIFUJI Hideaki }
963299d9939SYOSHIFUJI Hideaki 
964d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
965d52d3997SMartin KaFai Lau {
966d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
967d52d3997SMartin KaFai Lau 
968d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
969ad706862SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags);
970d52d3997SMartin KaFai Lau 
971d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
972d52d3997SMartin KaFai Lau 		return NULL;
973d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
974d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
975d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
976d52d3997SMartin KaFai Lau 	return pcpu_rt;
977d52d3997SMartin KaFai Lau }
978d52d3997SMartin KaFai Lau 
979d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
980d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
981d52d3997SMartin KaFai Lau {
982a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, **p;
983d52d3997SMartin KaFai Lau 
984d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
985d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
986d52d3997SMartin KaFai Lau 
987a73e4195SMartin KaFai Lau 	if (pcpu_rt) {
988a73e4195SMartin KaFai Lau 		dst_hold(&pcpu_rt->dst);
989a73e4195SMartin KaFai Lau 		rt6_dst_from_metrics_check(pcpu_rt);
990a73e4195SMartin KaFai Lau 	}
991a73e4195SMartin KaFai Lau 	return pcpu_rt;
992a73e4195SMartin KaFai Lau }
993a73e4195SMartin KaFai Lau 
994a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
995a73e4195SMartin KaFai Lau {
9969c7370a1SMartin KaFai Lau 	struct fib6_table *table = rt->rt6i_table;
997a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
998d52d3997SMartin KaFai Lau 
999d52d3997SMartin KaFai Lau 	pcpu_rt = ip6_rt_pcpu_alloc(rt);
1000d52d3997SMartin KaFai Lau 	if (!pcpu_rt) {
1001d52d3997SMartin KaFai Lau 		struct net *net = dev_net(rt->dst.dev);
1002d52d3997SMartin KaFai Lau 
10039c7370a1SMartin KaFai Lau 		dst_hold(&net->ipv6.ip6_null_entry->dst);
10049c7370a1SMartin KaFai Lau 		return net->ipv6.ip6_null_entry;
1005d52d3997SMartin KaFai Lau 	}
1006d52d3997SMartin KaFai Lau 
10079c7370a1SMartin KaFai Lau 	read_lock_bh(&table->tb6_lock);
10089c7370a1SMartin KaFai Lau 	if (rt->rt6i_pcpu) {
1009a73e4195SMartin KaFai Lau 		p = this_cpu_ptr(rt->rt6i_pcpu);
1010d52d3997SMartin KaFai Lau 		prev = cmpxchg(p, NULL, pcpu_rt);
1011d52d3997SMartin KaFai Lau 		if (prev) {
1012d52d3997SMartin KaFai Lau 			/* If someone did it before us, return prev instead */
1013d52d3997SMartin KaFai Lau 			dst_destroy(&pcpu_rt->dst);
1014d52d3997SMartin KaFai Lau 			pcpu_rt = prev;
1015d52d3997SMartin KaFai Lau 		}
10169c7370a1SMartin KaFai Lau 	} else {
10179c7370a1SMartin KaFai Lau 		/* rt has been removed from the fib6 tree
10189c7370a1SMartin KaFai Lau 		 * before we have a chance to acquire the read_lock.
10199c7370a1SMartin KaFai Lau 		 * In this case, don't brother to create a pcpu rt
10209c7370a1SMartin KaFai Lau 		 * since rt is going away anyway.  The next
10219c7370a1SMartin KaFai Lau 		 * dst_check() will trigger a re-lookup.
10229c7370a1SMartin KaFai Lau 		 */
10239c7370a1SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
10249c7370a1SMartin KaFai Lau 		pcpu_rt = rt;
10259c7370a1SMartin KaFai Lau 	}
1026d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1027d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
10289c7370a1SMartin KaFai Lau 	read_unlock_bh(&table->tb6_lock);
1029d52d3997SMartin KaFai Lau 	return pcpu_rt;
1030d52d3997SMartin KaFai Lau }
1031d52d3997SMartin KaFai Lau 
10328ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
10334c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
10341da177e4SLinus Torvalds {
1035367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
103645e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1037c71099acSThomas Graf 	int strict = 0;
10381da177e4SLinus Torvalds 
103977d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1040367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1041367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10421da177e4SLinus Torvalds 
1043c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10441da177e4SLinus Torvalds 
10454c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1046367efcb9SMartin KaFai Lau 	saved_fn = fn;
10471da177e4SLinus Torvalds 
1048ca254490SDavid Ahern 	if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1049ca254490SDavid Ahern 		oif = 0;
1050ca254490SDavid Ahern 
1051a3c00e46SMartin KaFai Lau redo_rt6_select:
1052367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
105352bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1054367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1055a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1056a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1057a3c00e46SMartin KaFai Lau 		if (fn)
1058a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1059367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1060367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1061367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1062367efcb9SMartin KaFai Lau 			fn = saved_fn;
1063367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1064367efcb9SMartin KaFai Lau 		}
1065a3c00e46SMartin KaFai Lau 	}
1066a3c00e46SMartin KaFai Lau 
1067d52d3997SMartin KaFai Lau 
1068d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10693da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1070c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10711da177e4SLinus Torvalds 
1072d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1073d52d3997SMartin KaFai Lau 		return rt;
10743da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10753da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10763da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10773da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10783da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10793da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
10803da59bd9SMartin KaFai Lau 		 */
1081c71099acSThomas Graf 
10823da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
10833da59bd9SMartin KaFai Lau 
1084d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1085d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1086d52d3997SMartin KaFai Lau 
10873da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
10883da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
10893da59bd9SMartin KaFai Lau 
10903da59bd9SMartin KaFai Lau 		if (uncached_rt)
10918d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
10923da59bd9SMartin KaFai Lau 		else
10933da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1094d52d3997SMartin KaFai Lau 
10953da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
10963da59bd9SMartin KaFai Lau 		return uncached_rt;
10973da59bd9SMartin KaFai Lau 
1098d52d3997SMartin KaFai Lau 	} else {
1099d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1100d52d3997SMartin KaFai Lau 
1101d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1102d52d3997SMartin KaFai Lau 
1103d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1104d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1105d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1106d52d3997SMartin KaFai Lau 
11079c7370a1SMartin KaFai Lau 		if (pcpu_rt) {
1108d52d3997SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11099c7370a1SMartin KaFai Lau 		} else {
11109c7370a1SMartin KaFai Lau 			/* We have to do the read_unlock first
11119c7370a1SMartin KaFai Lau 			 * because rt6_make_pcpu_route() may trigger
11129c7370a1SMartin KaFai Lau 			 * ip6_dst_gc() which will take the write_lock.
11139c7370a1SMartin KaFai Lau 			 */
11149c7370a1SMartin KaFai Lau 			dst_hold(&rt->dst);
11159c7370a1SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11169c7370a1SMartin KaFai Lau 			pcpu_rt = rt6_make_pcpu_route(rt);
11179c7370a1SMartin KaFai Lau 			dst_release(&rt->dst);
11189c7370a1SMartin KaFai Lau 		}
1119d52d3997SMartin KaFai Lau 
1120d52d3997SMartin KaFai Lau 		return pcpu_rt;
11219c7370a1SMartin KaFai Lau 
1122d52d3997SMartin KaFai Lau 	}
1123c71099acSThomas Graf }
1124c71099acSThomas Graf 
11258ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11264c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11274acad72dSPavel Emelyanov {
11284c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11294acad72dSPavel Emelyanov }
11304acad72dSPavel Emelyanov 
113172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
113272331bc0SShmulik Ladkani 						struct net_device *dev,
113372331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
113472331bc0SShmulik Ladkani {
113572331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
113672331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
113772331bc0SShmulik Ladkani 
113872331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
113972331bc0SShmulik Ladkani }
114072331bc0SShmulik Ladkani 
1141c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1142c71099acSThomas Graf {
1143b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1144c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1145adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1146904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
11474c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
1148ca254490SDavid Ahern 		.flowi6_iif = l3mdev_fib_oif(skb->dev),
11494c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11504c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11516502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11524c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11534c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1154c71099acSThomas Graf 	};
1155adaa70bbSThomas Graf 
1156904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
115746fa062aSJiri Benc 	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
1158904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
115906e9d040SJiri Benc 	skb_dst_drop(skb);
116072331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1161c71099acSThomas Graf }
1162c71099acSThomas Graf 
11638ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11644c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1165c71099acSThomas Graf {
11664c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1167c71099acSThomas Graf }
1168c71099acSThomas Graf 
11699c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
11704c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1171c71099acSThomas Graf {
1172ca254490SDavid Ahern 	struct dst_entry *dst;
1173c71099acSThomas Graf 	int flags = 0;
1174d46a9d67SDavid Ahern 	bool any_src;
1175c71099acSThomas Graf 
1176ca254490SDavid Ahern 	dst = l3mdev_rt6_dst_by_oif(net, fl6);
1177ca254490SDavid Ahern 	if (dst)
1178ca254490SDavid Ahern 		return dst;
1179ca254490SDavid Ahern 
11801fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
11814dc27d1cSDavid McCullough 
1182d46a9d67SDavid Ahern 	any_src = ipv6_addr_any(&fl6->saddr);
1183741a11d9SDavid Ahern 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
1184d46a9d67SDavid Ahern 	    (fl6->flowi6_oif && any_src))
118577d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1186c71099acSThomas Graf 
1187d46a9d67SDavid Ahern 	if (!any_src)
1188adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
11890c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
11900c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1191adaa70bbSThomas Graf 
11924c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
11931da177e4SLinus Torvalds }
11947159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
11951da177e4SLinus Torvalds 
11962774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
119714e50e57SDavid S. Miller {
11985c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
119914e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
120014e50e57SDavid S. Miller 
1201f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
120214e50e57SDavid S. Miller 	if (rt) {
12030a1f5962SMartin KaFai Lau 		rt6_info_init(rt);
12040a1f5962SMartin KaFai Lau 
1205d8d1f30bSChangli Gao 		new = &rt->dst;
120614e50e57SDavid S. Miller 		new->__use = 1;
1207352e512cSHerbert Xu 		new->input = dst_discard;
1208ede2059dSEric W. Biederman 		new->output = dst_discard_out;
120914e50e57SDavid S. Miller 
1210defb3519SDavid S. Miller 		dst_copy_metrics(new, &ort->dst);
121114e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
121214e50e57SDavid S. Miller 		if (rt->rt6i_idev)
121314e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
121414e50e57SDavid S. Miller 
12154e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
12160a1f5962SMartin KaFai Lau 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
121714e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
121814e50e57SDavid S. Miller 
121914e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
122014e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
122114e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
122214e50e57SDavid S. Miller #endif
122314e50e57SDavid S. Miller 
122414e50e57SDavid S. Miller 		dst_free(new);
122514e50e57SDavid S. Miller 	}
122614e50e57SDavid S. Miller 
122769ead7afSDavid S. Miller 	dst_release(dst_orig);
122869ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
122914e50e57SDavid S. Miller }
123014e50e57SDavid S. Miller 
12311da177e4SLinus Torvalds /*
12321da177e4SLinus Torvalds  *	Destination cache support functions
12331da177e4SLinus Torvalds  */
12341da177e4SLinus Torvalds 
12354b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12364b32b5adSMartin KaFai Lau {
12374b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12384b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12394b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12404b32b5adSMartin KaFai Lau }
12414b32b5adSMartin KaFai Lau 
12423da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12433da59bd9SMartin KaFai Lau {
12443da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12453da59bd9SMartin KaFai Lau 		return NULL;
12463da59bd9SMartin KaFai Lau 
12473da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12483da59bd9SMartin KaFai Lau 		return NULL;
12493da59bd9SMartin KaFai Lau 
12503da59bd9SMartin KaFai Lau 	return &rt->dst;
12513da59bd9SMartin KaFai Lau }
12523da59bd9SMartin KaFai Lau 
12533da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12543da59bd9SMartin KaFai Lau {
12553da59bd9SMartin KaFai Lau 	if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12563da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12573da59bd9SMartin KaFai Lau 		return &rt->dst;
12583da59bd9SMartin KaFai Lau 	else
12593da59bd9SMartin KaFai Lau 		return NULL;
12603da59bd9SMartin KaFai Lau }
12613da59bd9SMartin KaFai Lau 
12621da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12631da177e4SLinus Torvalds {
12641da177e4SLinus Torvalds 	struct rt6_info *rt;
12651da177e4SLinus Torvalds 
12661da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12671da177e4SLinus Torvalds 
12686f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12696f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12706f3118b5SNicolas Dichtel 	 * into this function always.
12716f3118b5SNicolas Dichtel 	 */
1272e3bc10bdSHannes Frederic Sowa 
12734b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
12744b32b5adSMartin KaFai Lau 
1275d52d3997SMartin KaFai Lau 	if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE))
12763da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
12773da59bd9SMartin KaFai Lau 	else
12783da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
12791da177e4SLinus Torvalds }
12801da177e4SLinus Torvalds 
12811da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
12821da177e4SLinus Torvalds {
12831da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
12841da177e4SLinus Torvalds 
12851da177e4SLinus Torvalds 	if (rt) {
128654c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
128754c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1288e0a1ad73SThomas Graf 				ip6_del_rt(rt);
128954c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
12901da177e4SLinus Torvalds 			}
129154c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
129254c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
129354c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
129454c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
129554c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
129654c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
12971da177e4SLinus Torvalds }
12981da177e4SLinus Torvalds 
12991da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
13001da177e4SLinus Torvalds {
13011da177e4SLinus Torvalds 	struct rt6_info *rt;
13021da177e4SLinus Torvalds 
13033ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
13041da177e4SLinus Torvalds 
1305adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
13061da177e4SLinus Torvalds 	if (rt) {
13071eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
13081eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
13098e3d5be7SMartin KaFai Lau 			ip6_del_rt(rt);
13101eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
13111da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
13121da177e4SLinus Torvalds 		}
13131da177e4SLinus Torvalds 	}
13141eb4f758SHannes Frederic Sowa }
13151da177e4SLinus Torvalds 
131645e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
131745e4fd26SMartin KaFai Lau {
131845e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
131945e4fd26SMartin KaFai Lau 
132045e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
132145e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
132245e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
132345e4fd26SMartin KaFai Lau }
132445e4fd26SMartin KaFai Lau 
1325*0d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
1326*0d3f6d29SMartin KaFai Lau {
1327*0d3f6d29SMartin KaFai Lau 	return !(rt->rt6i_flags & RTF_CACHE) &&
1328*0d3f6d29SMartin KaFai Lau 		(rt->rt6i_flags & RTF_PCPU || rt->rt6i_node);
1329*0d3f6d29SMartin KaFai Lau }
1330*0d3f6d29SMartin KaFai Lau 
133145e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
133245e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13331da177e4SLinus Torvalds {
13341da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13351da177e4SLinus Torvalds 
133645e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
133745e4fd26SMartin KaFai Lau 		return;
133845e4fd26SMartin KaFai Lau 
133981aded24SDavid S. Miller 	dst_confirm(dst);
134045e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
134145e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
134245e4fd26SMartin KaFai Lau 		return;
134381aded24SDavid S. Miller 
1344*0d3f6d29SMartin KaFai Lau 	if (!rt6_cache_allowed_for_pmtu(rt6)) {
134545e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
134645e4fd26SMartin KaFai Lau 	} else {
134745e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
134845e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13499d289715SHagen Paul Pfeifer 
135045e4fd26SMartin KaFai Lau 		if (iph) {
135145e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
135245e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
135345e4fd26SMartin KaFai Lau 		} else if (sk) {
135445e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
135545e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
135645e4fd26SMartin KaFai Lau 		} else {
135745e4fd26SMartin KaFai Lau 			return;
13581da177e4SLinus Torvalds 		}
135945e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
136045e4fd26SMartin KaFai Lau 		if (nrt6) {
136145e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
136245e4fd26SMartin KaFai Lau 
136345e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
136445e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
136545e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
136645e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
136745e4fd26SMartin KaFai Lau 			 */
136845e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
136945e4fd26SMartin KaFai Lau 		}
137045e4fd26SMartin KaFai Lau 	}
137145e4fd26SMartin KaFai Lau }
137245e4fd26SMartin KaFai Lau 
137345e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
137445e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
137545e4fd26SMartin KaFai Lau {
137645e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
13771da177e4SLinus Torvalds }
13781da177e4SLinus Torvalds 
137942ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
138042ae66c8SDavid S. Miller 		     int oif, u32 mark)
138181aded24SDavid S. Miller {
138281aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
138381aded24SDavid S. Miller 	struct dst_entry *dst;
138481aded24SDavid S. Miller 	struct flowi6 fl6;
138581aded24SDavid S. Miller 
138681aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
138781aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
13881b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
138981aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
139081aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
13916502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
139281aded24SDavid S. Miller 
139381aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
139481aded24SDavid S. Miller 	if (!dst->error)
139545e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
139681aded24SDavid S. Miller 	dst_release(dst);
139781aded24SDavid S. Miller }
139881aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
139981aded24SDavid S. Miller 
140081aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
140181aded24SDavid S. Miller {
140281aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
140381aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
140481aded24SDavid S. Miller }
140581aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
140681aded24SDavid S. Miller 
1407b55b76b2SDuan Jiong /* Handle redirects */
1408b55b76b2SDuan Jiong struct ip6rd_flowi {
1409b55b76b2SDuan Jiong 	struct flowi6 fl6;
1410b55b76b2SDuan Jiong 	struct in6_addr gateway;
1411b55b76b2SDuan Jiong };
1412b55b76b2SDuan Jiong 
1413b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1414b55b76b2SDuan Jiong 					     struct fib6_table *table,
1415b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1416b55b76b2SDuan Jiong 					     int flags)
1417b55b76b2SDuan Jiong {
1418b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1419b55b76b2SDuan Jiong 	struct rt6_info *rt;
1420b55b76b2SDuan Jiong 	struct fib6_node *fn;
1421b55b76b2SDuan Jiong 
1422b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1423b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1424b55b76b2SDuan Jiong 	 *
1425b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1426b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1427b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1428b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1429b55b76b2SDuan Jiong 	 * routes.
1430b55b76b2SDuan Jiong 	 */
1431b55b76b2SDuan Jiong 
1432b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1433b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1434b55b76b2SDuan Jiong restart:
1435b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1436b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1437b55b76b2SDuan Jiong 			continue;
1438b55b76b2SDuan Jiong 		if (rt->dst.error)
1439b55b76b2SDuan Jiong 			break;
1440b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1441b55b76b2SDuan Jiong 			continue;
1442b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1443b55b76b2SDuan Jiong 			continue;
1444b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1445b55b76b2SDuan Jiong 			continue;
1446b55b76b2SDuan Jiong 		break;
1447b55b76b2SDuan Jiong 	}
1448b55b76b2SDuan Jiong 
1449b55b76b2SDuan Jiong 	if (!rt)
1450b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1451b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1452b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1453b0a1ba59SMartin KaFai Lau 		goto out;
1454b0a1ba59SMartin KaFai Lau 	}
1455b0a1ba59SMartin KaFai Lau 
1456b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1457a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1458a3c00e46SMartin KaFai Lau 		if (fn)
1459a3c00e46SMartin KaFai Lau 			goto restart;
1460b55b76b2SDuan Jiong 	}
1461a3c00e46SMartin KaFai Lau 
1462b0a1ba59SMartin KaFai Lau out:
1463b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1464b55b76b2SDuan Jiong 
1465b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1466b55b76b2SDuan Jiong 
1467b55b76b2SDuan Jiong 	return rt;
1468b55b76b2SDuan Jiong };
1469b55b76b2SDuan Jiong 
1470b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1471b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1472b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1473b55b76b2SDuan Jiong {
1474b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1475b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1476b55b76b2SDuan Jiong 
1477b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1478b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1479b55b76b2SDuan Jiong 
1480b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1481b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1482b55b76b2SDuan Jiong }
1483b55b76b2SDuan Jiong 
14843a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
14853a5ad2eeSDavid S. Miller {
14863a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
14873a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
14883a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
14893a5ad2eeSDavid S. Miller 
14903a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1491e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
14923a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
14933a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
14943a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
14953a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
14966502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
14973a5ad2eeSDavid S. Miller 
1498b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
14996700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
15003a5ad2eeSDavid S. Miller 	dst_release(dst);
15013a5ad2eeSDavid S. Miller }
15023a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
15033a5ad2eeSDavid S. Miller 
1504c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1505c92a59ecSDuan Jiong 			    u32 mark)
1506c92a59ecSDuan Jiong {
1507c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1508c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1509c92a59ecSDuan Jiong 	struct dst_entry *dst;
1510c92a59ecSDuan Jiong 	struct flowi6 fl6;
1511c92a59ecSDuan Jiong 
1512c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1513e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1514c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1515c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1516c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1517c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1518c92a59ecSDuan Jiong 
1519b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1520c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1521c92a59ecSDuan Jiong 	dst_release(dst);
1522c92a59ecSDuan Jiong }
1523c92a59ecSDuan Jiong 
15243a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
15253a5ad2eeSDavid S. Miller {
15263a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
15273a5ad2eeSDavid S. Miller }
15283a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15293a5ad2eeSDavid S. Miller 
15300dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15311da177e4SLinus Torvalds {
15320dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15330dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15340dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15350dbaee3bSDavid S. Miller 
15361da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15371da177e4SLinus Torvalds 
15385578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15395578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15401da177e4SLinus Torvalds 
15411da177e4SLinus Torvalds 	/*
15421da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15431da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15441da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15451da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15461da177e4SLinus Torvalds 	 */
15471da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15481da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15491da177e4SLinus Torvalds 	return mtu;
15501da177e4SLinus Torvalds }
15511da177e4SLinus Torvalds 
1552ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1553d33e4553SDavid S. Miller {
15544b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
15554b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1556d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1557618f9bc7SSteffen Klassert 
1558618f9bc7SSteffen Klassert 	if (mtu)
155930f78d8eSEric Dumazet 		goto out;
1560618f9bc7SSteffen Klassert 
15614b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
15624b32b5adSMartin KaFai Lau 	if (mtu)
15634b32b5adSMartin KaFai Lau 		goto out;
15644b32b5adSMartin KaFai Lau 
1565618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1566d33e4553SDavid S. Miller 
1567d33e4553SDavid S. Miller 	rcu_read_lock();
1568d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1569d33e4553SDavid S. Miller 	if (idev)
1570d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1571d33e4553SDavid S. Miller 	rcu_read_unlock();
1572d33e4553SDavid S. Miller 
157330f78d8eSEric Dumazet out:
157430f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1575d33e4553SDavid S. Miller }
1576d33e4553SDavid S. Miller 
15773b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
15783b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
15795d0bbeebSThomas Graf 
15803b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
158187a11578SDavid S. Miller 				  struct flowi6 *fl6)
15821da177e4SLinus Torvalds {
158387a11578SDavid S. Miller 	struct dst_entry *dst;
15841da177e4SLinus Torvalds 	struct rt6_info *rt;
15851da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1586c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
15871da177e4SLinus Torvalds 
158838308473SDavid S. Miller 	if (unlikely(!idev))
1589122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
15901da177e4SLinus Torvalds 
1591ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, dev, 0);
159238308473SDavid S. Miller 	if (unlikely(!rt)) {
15931da177e4SLinus Torvalds 		in6_dev_put(idev);
159487a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
15951da177e4SLinus Torvalds 		goto out;
15961da177e4SLinus Torvalds 	}
15971da177e4SLinus Torvalds 
15988e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
15998e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1600d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1601550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
160287a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
16038e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
16048e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
160514edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
16061da177e4SLinus Torvalds 
16073b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1608d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1609d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
16103b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16111da177e4SLinus Torvalds 
16125578689aSDaniel Lezcano 	fib6_force_start_gc(net);
16131da177e4SLinus Torvalds 
161487a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
161587a11578SDavid S. Miller 
16161da177e4SLinus Torvalds out:
161787a11578SDavid S. Miller 	return dst;
16181da177e4SLinus Torvalds }
16191da177e4SLinus Torvalds 
16203d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
16211da177e4SLinus Torvalds {
1622e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
16233d0f24a7SStephen Hemminger 	int more = 0;
16241da177e4SLinus Torvalds 
16253b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16263b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16275d0bbeebSThomas Graf 
16281da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16291da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16301da177e4SLinus Torvalds 			*pprev = dst->next;
16311da177e4SLinus Torvalds 			dst_free(dst);
16321da177e4SLinus Torvalds 		} else {
16331da177e4SLinus Torvalds 			pprev = &dst->next;
16343d0f24a7SStephen Hemminger 			++more;
16351da177e4SLinus Torvalds 		}
16361da177e4SLinus Torvalds 	}
16371da177e4SLinus Torvalds 
16383b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16395d0bbeebSThomas Graf 
16403d0f24a7SStephen Hemminger 	return more;
16411da177e4SLinus Torvalds }
16421da177e4SLinus Torvalds 
16431e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16441e493d19SDavid S. Miller 			    void *arg)
16451e493d19SDavid S. Miller {
16461e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16471e493d19SDavid S. Miller 
16481e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
16491e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
16501e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
16511e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
16521e493d19SDavid S. Miller 		if (func(rt, arg)) {
16531e493d19SDavid S. Miller 			*pprev = dst->next;
16541e493d19SDavid S. Miller 			dst_free(dst);
16551e493d19SDavid S. Miller 		} else {
16561e493d19SDavid S. Miller 			pprev = &dst->next;
16571e493d19SDavid S. Miller 		}
16581e493d19SDavid S. Miller 	}
16591e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
16601e493d19SDavid S. Miller }
16611e493d19SDavid S. Miller 
1662569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
16631da177e4SLinus Torvalds {
166486393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
16657019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
16667019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
16677019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
16687019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
16697019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1670fc66f95cSEric Dumazet 	int entries;
16711da177e4SLinus Torvalds 
1672fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
167349a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1674fc66f95cSEric Dumazet 	    entries <= rt_max_size)
16751da177e4SLinus Torvalds 		goto out;
16761da177e4SLinus Torvalds 
16776891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
167814956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1679fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1680fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
16817019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
16821da177e4SLinus Torvalds out:
16837019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1684fc66f95cSEric Dumazet 	return entries > rt_max_size;
16851da177e4SLinus Torvalds }
16861da177e4SLinus Torvalds 
1687e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1688e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1689e715b6d3SFlorian Westphal {
1690c3a8d947SDaniel Borkmann 	bool ecn_ca = false;
1691e715b6d3SFlorian Westphal 	struct nlattr *nla;
1692e715b6d3SFlorian Westphal 	int remaining;
1693e715b6d3SFlorian Westphal 	u32 *mp;
1694e715b6d3SFlorian Westphal 
169563159f29SIan Morris 	if (!cfg->fc_mx)
1696e715b6d3SFlorian Westphal 		return 0;
1697e715b6d3SFlorian Westphal 
1698e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1699e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1700e715b6d3SFlorian Westphal 		return -ENOMEM;
1701e715b6d3SFlorian Westphal 
1702e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1703e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1704ea697639SDaniel Borkmann 		u32 val;
1705ea697639SDaniel Borkmann 
17061bb14807SDaniel Borkmann 		if (!type)
17071bb14807SDaniel Borkmann 			continue;
1708e715b6d3SFlorian Westphal 		if (unlikely(type > RTAX_MAX))
1709e715b6d3SFlorian Westphal 			goto err;
17101bb14807SDaniel Borkmann 
1711ea697639SDaniel Borkmann 		if (type == RTAX_CC_ALGO) {
1712ea697639SDaniel Borkmann 			char tmp[TCP_CA_NAME_MAX];
1713e715b6d3SFlorian Westphal 
1714ea697639SDaniel Borkmann 			nla_strlcpy(tmp, nla, sizeof(tmp));
1715c3a8d947SDaniel Borkmann 			val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
1716ea697639SDaniel Borkmann 			if (val == TCP_CA_UNSPEC)
1717ea697639SDaniel Borkmann 				goto err;
1718ea697639SDaniel Borkmann 		} else {
1719ea697639SDaniel Borkmann 			val = nla_get_u32(nla);
1720ea697639SDaniel Borkmann 		}
1721b8d3e416SDaniel Borkmann 		if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
1722b8d3e416SDaniel Borkmann 			goto err;
1723ea697639SDaniel Borkmann 
1724ea697639SDaniel Borkmann 		mp[type - 1] = val;
1725e715b6d3SFlorian Westphal 		__set_bit(type - 1, mxc->mx_valid);
1726e715b6d3SFlorian Westphal 	}
1727e715b6d3SFlorian Westphal 
1728c3a8d947SDaniel Borkmann 	if (ecn_ca) {
1729c3a8d947SDaniel Borkmann 		__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
1730c3a8d947SDaniel Borkmann 		mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
1731c3a8d947SDaniel Borkmann 	}
1732e715b6d3SFlorian Westphal 
1733c3a8d947SDaniel Borkmann 	mxc->mx = mp;
1734e715b6d3SFlorian Westphal 	return 0;
1735e715b6d3SFlorian Westphal  err:
1736e715b6d3SFlorian Westphal 	kfree(mp);
1737e715b6d3SFlorian Westphal 	return -EINVAL;
1738e715b6d3SFlorian Westphal }
17391da177e4SLinus Torvalds 
17408c5b83f0SRoopa Prabhu static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
17411da177e4SLinus Torvalds {
17425578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
17431da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
17441da177e4SLinus Torvalds 	struct net_device *dev = NULL;
17451da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1746c71099acSThomas Graf 	struct fib6_table *table;
17471da177e4SLinus Torvalds 	int addr_type;
17488c5b83f0SRoopa Prabhu 	int err = -EINVAL;
17491da177e4SLinus Torvalds 
175086872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
17518c5b83f0SRoopa Prabhu 		goto out;
17521da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
175386872cb5SThomas Graf 	if (cfg->fc_src_len)
17548c5b83f0SRoopa Prabhu 		goto out;
17551da177e4SLinus Torvalds #endif
175686872cb5SThomas Graf 	if (cfg->fc_ifindex) {
17571da177e4SLinus Torvalds 		err = -ENODEV;
17585578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
17591da177e4SLinus Torvalds 		if (!dev)
17601da177e4SLinus Torvalds 			goto out;
17611da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
17621da177e4SLinus Torvalds 		if (!idev)
17631da177e4SLinus Torvalds 			goto out;
17641da177e4SLinus Torvalds 	}
17651da177e4SLinus Torvalds 
176686872cb5SThomas Graf 	if (cfg->fc_metric == 0)
176786872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
17681da177e4SLinus Torvalds 
1769c71099acSThomas Graf 	err = -ENOBUFS;
177038308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1771d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1772d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
177338308473SDavid S. Miller 		if (!table) {
1774f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1775d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1776d71314b4SMatti Vaittinen 		}
1777d71314b4SMatti Vaittinen 	} else {
1778d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1779d71314b4SMatti Vaittinen 	}
178038308473SDavid S. Miller 
178138308473SDavid S. Miller 	if (!table)
1782c71099acSThomas Graf 		goto out;
1783c71099acSThomas Graf 
1784ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, NULL,
1785ad706862SMartin KaFai Lau 			   (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT);
17861da177e4SLinus Torvalds 
178738308473SDavid S. Miller 	if (!rt) {
17881da177e4SLinus Torvalds 		err = -ENOMEM;
17891da177e4SLinus Torvalds 		goto out;
17901da177e4SLinus Torvalds 	}
17911da177e4SLinus Torvalds 
17921716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
17931716a961SGao feng 		rt6_set_expires(rt, jiffies +
17941716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
17951716a961SGao feng 	else
17961716a961SGao feng 		rt6_clean_expires(rt);
17971da177e4SLinus Torvalds 
179886872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
179986872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
180086872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
180186872cb5SThomas Graf 
180286872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
18031da177e4SLinus Torvalds 
18041da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1805d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1806ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1807ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
18081da177e4SLinus Torvalds 	else
1809d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
18101da177e4SLinus Torvalds 
1811d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
18121da177e4SLinus Torvalds 
181319e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
181419e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
181519e42e45SRoopa Prabhu 
181619e42e45SRoopa Prabhu 		err = lwtunnel_build_state(dev, cfg->fc_encap_type,
1817127eb7cdSTom Herbert 					   cfg->fc_encap, AF_INET6, cfg,
1818127eb7cdSTom Herbert 					   &lwtstate);
181919e42e45SRoopa Prabhu 		if (err)
182019e42e45SRoopa Prabhu 			goto out;
182161adedf3SJiri Benc 		rt->dst.lwtstate = lwtstate_get(lwtstate);
182261adedf3SJiri Benc 		if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
182361adedf3SJiri Benc 			rt->dst.lwtstate->orig_output = rt->dst.output;
182461adedf3SJiri Benc 			rt->dst.output = lwtunnel_output;
182519e42e45SRoopa Prabhu 		}
182661adedf3SJiri Benc 		if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
182761adedf3SJiri Benc 			rt->dst.lwtstate->orig_input = rt->dst.input;
182861adedf3SJiri Benc 			rt->dst.input = lwtunnel_input;
182925368623STom Herbert 		}
183025368623STom Herbert 	}
183119e42e45SRoopa Prabhu 
183286872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
183386872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1834afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
183511d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
18361da177e4SLinus Torvalds 
18371da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
183886872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
183986872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
18401da177e4SLinus Torvalds #endif
18411da177e4SLinus Torvalds 
184286872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
18431da177e4SLinus Torvalds 
18441da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
18451da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
18461da177e4SLinus Torvalds 	 */
184786872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
184838308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
184938308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
185038308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
18511da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
18525578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
18531da177e4SLinus Torvalds 			if (dev) {
18541da177e4SLinus Torvalds 				dev_put(dev);
18551da177e4SLinus Torvalds 				in6_dev_put(idev);
18561da177e4SLinus Torvalds 			}
18575578689aSDaniel Lezcano 			dev = net->loopback_dev;
18581da177e4SLinus Torvalds 			dev_hold(dev);
18591da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
18601da177e4SLinus Torvalds 			if (!idev) {
18611da177e4SLinus Torvalds 				err = -ENODEV;
18621da177e4SLinus Torvalds 				goto out;
18631da177e4SLinus Torvalds 			}
18641da177e4SLinus Torvalds 		}
18651da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1866ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1867ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1868ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1869ede2059dSEric W. Biederman 			rt->dst.output = dst_discard_out;
18707150aedeSKamala R 			rt->dst.input = dst_discard;
1871ef2c7d7bSNicolas Dichtel 			break;
1872ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1873ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
18747150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
18757150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1876ef2c7d7bSNicolas Dichtel 			break;
1877b4949ab2SNicolas Dichtel 		case RTN_THROW:
18780315e382SNikola Forró 		case RTN_UNREACHABLE:
1879ef2c7d7bSNicolas Dichtel 		default:
18807150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
18810315e382SNikola Forró 					: (cfg->fc_type == RTN_UNREACHABLE)
18820315e382SNikola Forró 					? -EHOSTUNREACH : -ENETUNREACH;
18837150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
18847150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1885ef2c7d7bSNicolas Dichtel 			break;
1886ef2c7d7bSNicolas Dichtel 		}
18871da177e4SLinus Torvalds 		goto install_route;
18881da177e4SLinus Torvalds 	}
18891da177e4SLinus Torvalds 
189086872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1891b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
18921da177e4SLinus Torvalds 		int gwa_type;
18931da177e4SLinus Torvalds 
189486872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1895330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
189648ed7b26SFlorian Westphal 
189748ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
189848ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
189948ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
190048ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
190148ed7b26SFlorian Westphal 		 */
190248ed7b26SFlorian Westphal 		err = -EINVAL;
1903330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1904330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1905330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
190648ed7b26SFlorian Westphal 			goto out;
190748ed7b26SFlorian Westphal 
19084e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
19091da177e4SLinus Torvalds 
19101da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
19111da177e4SLinus Torvalds 			struct rt6_info *grt;
19121da177e4SLinus Torvalds 
19131da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
19141da177e4SLinus Torvalds 			   addresses as nexthop address.
19151da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
19161da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
19171da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
19181da177e4SLinus Torvalds 			   some exceptions. --ANK
19191da177e4SLinus Torvalds 			 */
19201da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
19211da177e4SLinus Torvalds 				goto out;
19221da177e4SLinus Torvalds 
19235578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
19241da177e4SLinus Torvalds 
19251da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
192638308473SDavid S. Miller 			if (!grt)
19271da177e4SLinus Torvalds 				goto out;
19281da177e4SLinus Torvalds 			if (dev) {
1929d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
193094e187c0SAmerigo Wang 					ip6_rt_put(grt);
19311da177e4SLinus Torvalds 					goto out;
19321da177e4SLinus Torvalds 				}
19331da177e4SLinus Torvalds 			} else {
1934d1918542SDavid S. Miller 				dev = grt->dst.dev;
19351da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
19361da177e4SLinus Torvalds 				dev_hold(dev);
19371da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
19381da177e4SLinus Torvalds 			}
19391da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
19401da177e4SLinus Torvalds 				err = 0;
194194e187c0SAmerigo Wang 			ip6_rt_put(grt);
19421da177e4SLinus Torvalds 
19431da177e4SLinus Torvalds 			if (err)
19441da177e4SLinus Torvalds 				goto out;
19451da177e4SLinus Torvalds 		}
19461da177e4SLinus Torvalds 		err = -EINVAL;
194738308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
19481da177e4SLinus Torvalds 			goto out;
19491da177e4SLinus Torvalds 	}
19501da177e4SLinus Torvalds 
19511da177e4SLinus Torvalds 	err = -ENODEV;
195238308473SDavid S. Miller 	if (!dev)
19531da177e4SLinus Torvalds 		goto out;
19541da177e4SLinus Torvalds 
1955c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1956c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1957c3968a85SDaniel Walter 			err = -EINVAL;
1958c3968a85SDaniel Walter 			goto out;
1959c3968a85SDaniel Walter 		}
19604e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1961c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1962c3968a85SDaniel Walter 	} else
1963c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1964c3968a85SDaniel Walter 
196586872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
19661da177e4SLinus Torvalds 
19671da177e4SLinus Torvalds install_route:
1968d8d1f30bSChangli Gao 	rt->dst.dev = dev;
19691da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1970c71099acSThomas Graf 	rt->rt6i_table = table;
197163152fc0SDaniel Lezcano 
1972c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
197363152fc0SDaniel Lezcano 
19748c5b83f0SRoopa Prabhu 	return rt;
19751da177e4SLinus Torvalds out:
19761da177e4SLinus Torvalds 	if (dev)
19771da177e4SLinus Torvalds 		dev_put(dev);
19781da177e4SLinus Torvalds 	if (idev)
19791da177e4SLinus Torvalds 		in6_dev_put(idev);
19801da177e4SLinus Torvalds 	if (rt)
1981d8d1f30bSChangli Gao 		dst_free(&rt->dst);
19826b9ea5a6SRoopa Prabhu 
19838c5b83f0SRoopa Prabhu 	return ERR_PTR(err);
19846b9ea5a6SRoopa Prabhu }
19856b9ea5a6SRoopa Prabhu 
19866b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg)
19876b9ea5a6SRoopa Prabhu {
19886b9ea5a6SRoopa Prabhu 	struct mx6_config mxc = { .mx = NULL, };
19898c5b83f0SRoopa Prabhu 	struct rt6_info *rt;
19906b9ea5a6SRoopa Prabhu 	int err;
19916b9ea5a6SRoopa Prabhu 
19928c5b83f0SRoopa Prabhu 	rt = ip6_route_info_create(cfg);
19938c5b83f0SRoopa Prabhu 	if (IS_ERR(rt)) {
19948c5b83f0SRoopa Prabhu 		err = PTR_ERR(rt);
19958c5b83f0SRoopa Prabhu 		rt = NULL;
19966b9ea5a6SRoopa Prabhu 		goto out;
19978c5b83f0SRoopa Prabhu 	}
19986b9ea5a6SRoopa Prabhu 
19996b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&mxc, cfg);
20006b9ea5a6SRoopa Prabhu 	if (err)
20016b9ea5a6SRoopa Prabhu 		goto out;
20026b9ea5a6SRoopa Prabhu 
20036b9ea5a6SRoopa Prabhu 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
20046b9ea5a6SRoopa Prabhu 
20056b9ea5a6SRoopa Prabhu 	kfree(mxc.mx);
20066b9ea5a6SRoopa Prabhu 
20076b9ea5a6SRoopa Prabhu 	return err;
20086b9ea5a6SRoopa Prabhu out:
20096b9ea5a6SRoopa Prabhu 	if (rt)
20106b9ea5a6SRoopa Prabhu 		dst_free(&rt->dst);
20116b9ea5a6SRoopa Prabhu 
20121da177e4SLinus Torvalds 	return err;
20131da177e4SLinus Torvalds }
20141da177e4SLinus Torvalds 
201586872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
20161da177e4SLinus Torvalds {
20171da177e4SLinus Torvalds 	int err;
2018c71099acSThomas Graf 	struct fib6_table *table;
2019d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
20201da177e4SLinus Torvalds 
20218e3d5be7SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry ||
20228e3d5be7SMartin KaFai Lau 	    rt->dst.flags & DST_NOCACHE) {
20236825a26cSGao feng 		err = -ENOENT;
20246825a26cSGao feng 		goto out;
20256825a26cSGao feng 	}
20266c813a72SPatrick McHardy 
2027c71099acSThomas Graf 	table = rt->rt6i_table;
2028c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
202986872cb5SThomas Graf 	err = fib6_del(rt, info);
2030c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
20311da177e4SLinus Torvalds 
20326825a26cSGao feng out:
203394e187c0SAmerigo Wang 	ip6_rt_put(rt);
20341da177e4SLinus Torvalds 	return err;
20351da177e4SLinus Torvalds }
20361da177e4SLinus Torvalds 
2037e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
2038e0a1ad73SThomas Graf {
20394d1169c1SDenis V. Lunev 	struct nl_info info = {
2040d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
20414d1169c1SDenis V. Lunev 	};
2042528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
2043e0a1ad73SThomas Graf }
2044e0a1ad73SThomas Graf 
204586872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
20461da177e4SLinus Torvalds {
2047c71099acSThomas Graf 	struct fib6_table *table;
20481da177e4SLinus Torvalds 	struct fib6_node *fn;
20491da177e4SLinus Torvalds 	struct rt6_info *rt;
20501da177e4SLinus Torvalds 	int err = -ESRCH;
20511da177e4SLinus Torvalds 
20525578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
205338308473SDavid S. Miller 	if (!table)
2054c71099acSThomas Graf 		return err;
20551da177e4SLinus Torvalds 
2056c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2057c71099acSThomas Graf 
2058c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
205986872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
206086872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
20611da177e4SLinus Torvalds 
20621da177e4SLinus Torvalds 	if (fn) {
2063d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
20641f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
20651f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
20661f56a01fSMartin KaFai Lau 				continue;
206786872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2068d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2069d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
20701da177e4SLinus Torvalds 				continue;
207186872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
207286872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
20731da177e4SLinus Torvalds 				continue;
207486872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
20751da177e4SLinus Torvalds 				continue;
2076d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2077c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
20781da177e4SLinus Torvalds 
207986872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
20801da177e4SLinus Torvalds 		}
20811da177e4SLinus Torvalds 	}
2082c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
20831da177e4SLinus Torvalds 
20841da177e4SLinus Torvalds 	return err;
20851da177e4SLinus Torvalds }
20861da177e4SLinus Torvalds 
20876700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2088a6279458SYOSHIFUJI Hideaki {
2089a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2090e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2091e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2092e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2093e8599ff4SDavid S. Miller 	struct neighbour *neigh;
209471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
20956e157b6aSDavid S. Miller 	int optlen, on_link;
20966e157b6aSDavid S. Miller 	u8 *lladdr;
2097e8599ff4SDavid S. Miller 
209829a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
209971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2100e8599ff4SDavid S. Miller 
2101e8599ff4SDavid S. Miller 	if (optlen < 0) {
21026e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2103e8599ff4SDavid S. Miller 		return;
2104e8599ff4SDavid S. Miller 	}
2105e8599ff4SDavid S. Miller 
210671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2107e8599ff4SDavid S. Miller 
210871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
21096e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2110e8599ff4SDavid S. Miller 		return;
2111e8599ff4SDavid S. Miller 	}
2112e8599ff4SDavid S. Miller 
21136e157b6aSDavid S. Miller 	on_link = 0;
211471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2115e8599ff4SDavid S. Miller 		on_link = 1;
211671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2117e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
21186e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2119e8599ff4SDavid S. Miller 		return;
2120e8599ff4SDavid S. Miller 	}
2121e8599ff4SDavid S. Miller 
2122e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2123e8599ff4SDavid S. Miller 	if (!in6_dev)
2124e8599ff4SDavid S. Miller 		return;
2125e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2126e8599ff4SDavid S. Miller 		return;
2127e8599ff4SDavid S. Miller 
2128e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2129e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2130e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2131e8599ff4SDavid S. Miller 	 */
2132e8599ff4SDavid S. Miller 
213371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
2134e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2135e8599ff4SDavid S. Miller 		return;
2136e8599ff4SDavid S. Miller 	}
21376e157b6aSDavid S. Miller 
21386e157b6aSDavid S. Miller 	lladdr = NULL;
2139e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2140e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2141e8599ff4SDavid S. Miller 					     skb->dev);
2142e8599ff4SDavid S. Miller 		if (!lladdr) {
2143e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2144e8599ff4SDavid S. Miller 			return;
2145e8599ff4SDavid S. Miller 		}
2146e8599ff4SDavid S. Miller 	}
2147e8599ff4SDavid S. Miller 
21486e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
2149ec13ad1dSMatthias Schiffer 	if (rt->rt6i_flags & RTF_REJECT) {
21506e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
21516e157b6aSDavid S. Miller 		return;
21526e157b6aSDavid S. Miller 	}
21536e157b6aSDavid S. Miller 
21546e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
21556e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
21566e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
21576e157b6aSDavid S. Miller 	 */
21586e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
21596e157b6aSDavid S. Miller 
216071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2161e8599ff4SDavid S. Miller 	if (!neigh)
2162e8599ff4SDavid S. Miller 		return;
2163e8599ff4SDavid S. Miller 
21641da177e4SLinus Torvalds 	/*
21651da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
21661da177e4SLinus Torvalds 	 */
21671da177e4SLinus Torvalds 
21681da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
21691da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
21701da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
21711da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
21721da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
21731da177e4SLinus Torvalds 		     );
21741da177e4SLinus Torvalds 
217583a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
217638308473SDavid S. Miller 	if (!nrt)
21771da177e4SLinus Torvalds 		goto out;
21781da177e4SLinus Torvalds 
21791da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
21801da177e4SLinus Torvalds 	if (on_link)
21811da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
21821da177e4SLinus Torvalds 
21834e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
21841da177e4SLinus Torvalds 
218540e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
21861da177e4SLinus Torvalds 		goto out;
21871da177e4SLinus Torvalds 
2188d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2189d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
219071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
219160592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
21928d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
21938d71740cSTom Tucker 
21941da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
21956e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2196e0a1ad73SThomas Graf 		ip6_del_rt(rt);
21971da177e4SLinus Torvalds 	}
21981da177e4SLinus Torvalds 
21991da177e4SLinus Torvalds out:
2200e8599ff4SDavid S. Miller 	neigh_release(neigh);
22016e157b6aSDavid S. Miller }
22026e157b6aSDavid S. Miller 
22031da177e4SLinus Torvalds /*
22041da177e4SLinus Torvalds  *	Misc support functions
22051da177e4SLinus Torvalds  */
22061da177e4SLinus Torvalds 
22074b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
22084b32b5adSMartin KaFai Lau {
22094b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
22104b32b5adSMartin KaFai Lau 
22114b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
22124b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
22134b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
22144b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
22154b32b5adSMartin KaFai Lau }
22164b32b5adSMartin KaFai Lau 
221783a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
22181da177e4SLinus Torvalds {
2219d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2220d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
222183a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2222d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
22231da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
22241da177e4SLinus Torvalds 	if (rt->rt6i_idev)
22251da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2226d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
22274e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
22281716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
22291716a961SGao feng 	rt6_set_from(rt, ort);
223083a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
22311da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
223283a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
22331da177e4SLinus Torvalds #endif
223483a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2235c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
223661adedf3SJiri Benc 	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
22371da177e4SLinus Torvalds }
22381da177e4SLinus Torvalds 
223970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2240efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2241b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2242b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
224370ceb4f5SYOSHIFUJI Hideaki {
224470ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
224570ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2246c71099acSThomas Graf 	struct fib6_table *table;
224770ceb4f5SYOSHIFUJI Hideaki 
2248efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
224938308473SDavid S. Miller 	if (!table)
2250c71099acSThomas Graf 		return NULL;
2251c71099acSThomas Graf 
22525744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2253c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
225470ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
225570ceb4f5SYOSHIFUJI Hideaki 		goto out;
225670ceb4f5SYOSHIFUJI Hideaki 
2257d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2258d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
225970ceb4f5SYOSHIFUJI Hideaki 			continue;
226070ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
226170ceb4f5SYOSHIFUJI Hideaki 			continue;
226270ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
226370ceb4f5SYOSHIFUJI Hideaki 			continue;
2264d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
226570ceb4f5SYOSHIFUJI Hideaki 		break;
226670ceb4f5SYOSHIFUJI Hideaki 	}
226770ceb4f5SYOSHIFUJI Hideaki out:
22685744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
226970ceb4f5SYOSHIFUJI Hideaki 	return rt;
227070ceb4f5SYOSHIFUJI Hideaki }
227170ceb4f5SYOSHIFUJI Hideaki 
2272efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2273b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2274b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
227595c96174SEric Dumazet 					   unsigned int pref)
227670ceb4f5SYOSHIFUJI Hideaki {
227786872cb5SThomas Graf 	struct fib6_config cfg = {
2278238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
227986872cb5SThomas Graf 		.fc_ifindex	= ifindex,
228086872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
228186872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
228286872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
228315e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2284efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2285efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
228686872cb5SThomas Graf 	};
228770ceb4f5SYOSHIFUJI Hideaki 
2288ca254490SDavid Ahern 	cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO;
22894e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
22904e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
229186872cb5SThomas Graf 
2292e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2293e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
229486872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
229570ceb4f5SYOSHIFUJI Hideaki 
229686872cb5SThomas Graf 	ip6_route_add(&cfg);
229770ceb4f5SYOSHIFUJI Hideaki 
2298efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
229970ceb4f5SYOSHIFUJI Hideaki }
230070ceb4f5SYOSHIFUJI Hideaki #endif
230170ceb4f5SYOSHIFUJI Hideaki 
2302b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
23031da177e4SLinus Torvalds {
23041da177e4SLinus Torvalds 	struct rt6_info *rt;
2305c71099acSThomas Graf 	struct fib6_table *table;
23061da177e4SLinus Torvalds 
2307c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
230838308473SDavid S. Miller 	if (!table)
2309c71099acSThomas Graf 		return NULL;
23101da177e4SLinus Torvalds 
23115744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2312d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2313d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2314045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
23151da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
23161da177e4SLinus Torvalds 			break;
23171da177e4SLinus Torvalds 	}
23181da177e4SLinus Torvalds 	if (rt)
2319d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
23205744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
23211da177e4SLinus Torvalds 	return rt;
23221da177e4SLinus Torvalds }
23231da177e4SLinus Torvalds 
2324b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2325ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2326ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
23271da177e4SLinus Torvalds {
232886872cb5SThomas Graf 	struct fib6_config cfg = {
2329ca254490SDavid Ahern 		.fc_table	= l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
2330238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
233186872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
233286872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
233386872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
233415e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
23355578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2336c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
233786872cb5SThomas Graf 	};
23381da177e4SLinus Torvalds 
23394e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
23401da177e4SLinus Torvalds 
234186872cb5SThomas Graf 	ip6_route_add(&cfg);
23421da177e4SLinus Torvalds 
23431da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
23441da177e4SLinus Torvalds }
23451da177e4SLinus Torvalds 
23467b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
23471da177e4SLinus Torvalds {
23481da177e4SLinus Torvalds 	struct rt6_info *rt;
2349c71099acSThomas Graf 	struct fib6_table *table;
2350c71099acSThomas Graf 
2351c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
23527b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
235338308473SDavid S. Miller 	if (!table)
2354c71099acSThomas Graf 		return;
23551da177e4SLinus Torvalds 
23561da177e4SLinus Torvalds restart:
2357c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2358d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
23593e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
23603e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2361d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2362c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2363e0a1ad73SThomas Graf 			ip6_del_rt(rt);
23641da177e4SLinus Torvalds 			goto restart;
23651da177e4SLinus Torvalds 		}
23661da177e4SLinus Torvalds 	}
2367c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
23681da177e4SLinus Torvalds }
23691da177e4SLinus Torvalds 
23705578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
23715578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
237286872cb5SThomas Graf 				 struct fib6_config *cfg)
237386872cb5SThomas Graf {
237486872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
237586872cb5SThomas Graf 
2376ca254490SDavid Ahern 	cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
2377ca254490SDavid Ahern 			 : RT6_TABLE_MAIN;
237886872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
237986872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
238086872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
238186872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
238286872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
238386872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
238486872cb5SThomas Graf 
23855578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2386f1243c2dSBenjamin Thery 
23874e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
23884e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
23894e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
239086872cb5SThomas Graf }
239186872cb5SThomas Graf 
23925578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
23931da177e4SLinus Torvalds {
239486872cb5SThomas Graf 	struct fib6_config cfg;
23951da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
23961da177e4SLinus Torvalds 	int err;
23971da177e4SLinus Torvalds 
23981da177e4SLinus Torvalds 	switch (cmd) {
23991da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
24001da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2401af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
24021da177e4SLinus Torvalds 			return -EPERM;
24031da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
24041da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
24051da177e4SLinus Torvalds 		if (err)
24061da177e4SLinus Torvalds 			return -EFAULT;
24071da177e4SLinus Torvalds 
24085578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
240986872cb5SThomas Graf 
24101da177e4SLinus Torvalds 		rtnl_lock();
24111da177e4SLinus Torvalds 		switch (cmd) {
24121da177e4SLinus Torvalds 		case SIOCADDRT:
241386872cb5SThomas Graf 			err = ip6_route_add(&cfg);
24141da177e4SLinus Torvalds 			break;
24151da177e4SLinus Torvalds 		case SIOCDELRT:
241686872cb5SThomas Graf 			err = ip6_route_del(&cfg);
24171da177e4SLinus Torvalds 			break;
24181da177e4SLinus Torvalds 		default:
24191da177e4SLinus Torvalds 			err = -EINVAL;
24201da177e4SLinus Torvalds 		}
24211da177e4SLinus Torvalds 		rtnl_unlock();
24221da177e4SLinus Torvalds 
24231da177e4SLinus Torvalds 		return err;
24243ff50b79SStephen Hemminger 	}
24251da177e4SLinus Torvalds 
24261da177e4SLinus Torvalds 	return -EINVAL;
24271da177e4SLinus Torvalds }
24281da177e4SLinus Torvalds 
24291da177e4SLinus Torvalds /*
24301da177e4SLinus Torvalds  *	Drop the packet on the floor
24311da177e4SLinus Torvalds  */
24321da177e4SLinus Torvalds 
2433d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
24341da177e4SLinus Torvalds {
2435612f09e8SYOSHIFUJI Hideaki 	int type;
2436adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2437612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2438612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
24390660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
244045bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
24413bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24423bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2443612f09e8SYOSHIFUJI Hideaki 			break;
2444612f09e8SYOSHIFUJI Hideaki 		}
2445612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2446612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
24473bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24483bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2449612f09e8SYOSHIFUJI Hideaki 		break;
2450612f09e8SYOSHIFUJI Hideaki 	}
24513ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
24521da177e4SLinus Torvalds 	kfree_skb(skb);
24531da177e4SLinus Torvalds 	return 0;
24541da177e4SLinus Torvalds }
24551da177e4SLinus Torvalds 
24569ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
24579ce8ade0SThomas Graf {
2458612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
24599ce8ade0SThomas Graf }
24609ce8ade0SThomas Graf 
2461ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
24621da177e4SLinus Torvalds {
2463adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2464612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
24651da177e4SLinus Torvalds }
24661da177e4SLinus Torvalds 
24679ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
24689ce8ade0SThomas Graf {
2469612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
24709ce8ade0SThomas Graf }
24719ce8ade0SThomas Graf 
2472ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
24739ce8ade0SThomas Graf {
2474adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2475612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
24769ce8ade0SThomas Graf }
24779ce8ade0SThomas Graf 
24781da177e4SLinus Torvalds /*
24791da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
24801da177e4SLinus Torvalds  */
24811da177e4SLinus Torvalds 
24821da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
24831da177e4SLinus Torvalds 				    const struct in6_addr *addr,
24848f031519SDavid S. Miller 				    bool anycast)
24851da177e4SLinus Torvalds {
2486ca254490SDavid Ahern 	u32 tb_id;
2487c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2488a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2489ad706862SMartin KaFai Lau 					    DST_NOCOUNT);
2490a3300ef4SHannes Frederic Sowa 	if (!rt)
24911da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
24921da177e4SLinus Torvalds 
24931da177e4SLinus Torvalds 	in6_dev_hold(idev);
24941da177e4SLinus Torvalds 
249511d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2496d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2497d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
24981da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
24991da177e4SLinus Torvalds 
25001da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
250158c4fb86SYOSHIFUJI Hideaki 	if (anycast)
250258c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
250358c4fb86SYOSHIFUJI Hideaki 	else
25041da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
25051da177e4SLinus Torvalds 
2506550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
25074e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
25081da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
2509ca254490SDavid Ahern 	tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
2510ca254490SDavid Ahern 	rt->rt6i_table = fib6_get_table(net, tb_id);
25118e3d5be7SMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
25121da177e4SLinus Torvalds 
2513d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
25141da177e4SLinus Torvalds 
25151da177e4SLinus Torvalds 	return rt;
25161da177e4SLinus Torvalds }
25171da177e4SLinus Torvalds 
2518c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2519c3968a85SDaniel Walter 			struct rt6_info *rt,
2520b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2521c3968a85SDaniel Walter 			unsigned int prefs,
2522c3968a85SDaniel Walter 			struct in6_addr *saddr)
2523c3968a85SDaniel Walter {
2524e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2525e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2526c3968a85SDaniel Walter 	int err = 0;
2527e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
25284e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2529c3968a85SDaniel Walter 	else
2530c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2531c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2532c3968a85SDaniel Walter 	return err;
2533c3968a85SDaniel Walter }
2534c3968a85SDaniel Walter 
2535c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2536c3968a85SDaniel Walter struct arg_dev_net_ip {
2537c3968a85SDaniel Walter 	struct net_device *dev;
2538c3968a85SDaniel Walter 	struct net *net;
2539c3968a85SDaniel Walter 	struct in6_addr *addr;
2540c3968a85SDaniel Walter };
2541c3968a85SDaniel Walter 
2542c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2543c3968a85SDaniel Walter {
2544c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2545c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2546c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2547c3968a85SDaniel Walter 
2548d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2549c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2550c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2551c3968a85SDaniel Walter 		/* remove prefsrc entry */
2552c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2553c3968a85SDaniel Walter 	}
2554c3968a85SDaniel Walter 	return 0;
2555c3968a85SDaniel Walter }
2556c3968a85SDaniel Walter 
2557c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2558c3968a85SDaniel Walter {
2559c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2560c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2561c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2562c3968a85SDaniel Walter 		.net = net,
2563c3968a85SDaniel Walter 		.addr = &ifp->addr,
2564c3968a85SDaniel Walter 	};
25650c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2566c3968a85SDaniel Walter }
2567c3968a85SDaniel Walter 
2568be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2569be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2570be7a010dSDuan Jiong 
2571be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2572be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2573be7a010dSDuan Jiong {
2574be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2575be7a010dSDuan Jiong 
2576be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2577be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2578be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2579be7a010dSDuan Jiong 		return -1;
2580be7a010dSDuan Jiong 	}
2581be7a010dSDuan Jiong 	return 0;
2582be7a010dSDuan Jiong }
2583be7a010dSDuan Jiong 
2584be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2585be7a010dSDuan Jiong {
2586be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2587be7a010dSDuan Jiong }
2588be7a010dSDuan Jiong 
25898ed67789SDaniel Lezcano struct arg_dev_net {
25908ed67789SDaniel Lezcano 	struct net_device *dev;
25918ed67789SDaniel Lezcano 	struct net *net;
25928ed67789SDaniel Lezcano };
25938ed67789SDaniel Lezcano 
25941da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
25951da177e4SLinus Torvalds {
2596bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2597bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
25988ed67789SDaniel Lezcano 
2599d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2600c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
26011da177e4SLinus Torvalds 		return -1;
2602c159d30cSDavid S. Miller 
26031da177e4SLinus Torvalds 	return 0;
26041da177e4SLinus Torvalds }
26051da177e4SLinus Torvalds 
2606f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
26071da177e4SLinus Torvalds {
26088ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
26098ed67789SDaniel Lezcano 		.dev = dev,
26108ed67789SDaniel Lezcano 		.net = net,
26118ed67789SDaniel Lezcano 	};
26128ed67789SDaniel Lezcano 
26130c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
26141e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
2615e332bc67SEric W. Biederman 	if (dev)
26168d0b94afSMartin KaFai Lau 		rt6_uncached_list_flush_dev(net, dev);
26171da177e4SLinus Torvalds }
26181da177e4SLinus Torvalds 
261995c96174SEric Dumazet struct rt6_mtu_change_arg {
26201da177e4SLinus Torvalds 	struct net_device *dev;
262195c96174SEric Dumazet 	unsigned int mtu;
26221da177e4SLinus Torvalds };
26231da177e4SLinus Torvalds 
26241da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
26251da177e4SLinus Torvalds {
26261da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
26271da177e4SLinus Torvalds 	struct inet6_dev *idev;
26281da177e4SLinus Torvalds 
26291da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
26301da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
26311da177e4SLinus Torvalds 	   We still use this lock to block changes
26321da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
26331da177e4SLinus Torvalds 	*/
26341da177e4SLinus Torvalds 
26351da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
263638308473SDavid S. Miller 	if (!idev)
26371da177e4SLinus Torvalds 		return 0;
26381da177e4SLinus Torvalds 
26391da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
26401da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
26411da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
26421da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
26431da177e4SLinus Torvalds 	 */
26441da177e4SLinus Torvalds 	/*
26451da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
26461da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
26471da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
26481da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
26491da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
26501da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
26511da177e4SLinus Torvalds 	   PMTU discouvery.
26521da177e4SLinus Torvalds 	 */
2653d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
26544b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
26554b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
26564b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
26574b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
26584b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
26594b32b5adSMartin KaFai Lau 			 * been updated.
26604b32b5adSMartin KaFai Lau 			 */
26614b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
26624b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
26634b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2664d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
26654b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2666defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2667566cfd8fSSimon Arlott 		}
26684b32b5adSMartin KaFai Lau 	}
26691da177e4SLinus Torvalds 	return 0;
26701da177e4SLinus Torvalds }
26711da177e4SLinus Torvalds 
267295c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
26731da177e4SLinus Torvalds {
2674c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2675c71099acSThomas Graf 		.dev = dev,
2676c71099acSThomas Graf 		.mtu = mtu,
2677c71099acSThomas Graf 	};
26781da177e4SLinus Torvalds 
26790c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
26801da177e4SLinus Torvalds }
26811da177e4SLinus Torvalds 
2682ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
26835176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
268486872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2685ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
268686872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
268786872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
268851ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2689c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
269019e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
269119e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
269286872cb5SThomas Graf };
269386872cb5SThomas Graf 
269486872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
269586872cb5SThomas Graf 			      struct fib6_config *cfg)
26961da177e4SLinus Torvalds {
269786872cb5SThomas Graf 	struct rtmsg *rtm;
269886872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2699c78ba6d6SLubomir Rintel 	unsigned int pref;
270086872cb5SThomas Graf 	int err;
27011da177e4SLinus Torvalds 
270286872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
270386872cb5SThomas Graf 	if (err < 0)
270486872cb5SThomas Graf 		goto errout;
27051da177e4SLinus Torvalds 
270686872cb5SThomas Graf 	err = -EINVAL;
270786872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
270886872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
270986872cb5SThomas Graf 
271086872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
271186872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
271286872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
271386872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
271486872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2715ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
271686872cb5SThomas Graf 
2717ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2718ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2719b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2720b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
272186872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
272286872cb5SThomas Graf 
2723ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2724ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2725ab79ad14SMaciej Żenczykowski 
27261f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
27271f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
27281f56a01fSMartin KaFai Lau 
272915e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
273086872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
27313b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
273286872cb5SThomas Graf 
273386872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
273467b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
273586872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
27361da177e4SLinus Torvalds 	}
273786872cb5SThomas Graf 
273886872cb5SThomas Graf 	if (tb[RTA_DST]) {
273986872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
274086872cb5SThomas Graf 
274186872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
274286872cb5SThomas Graf 			goto errout;
274386872cb5SThomas Graf 
274486872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
27451da177e4SLinus Torvalds 	}
274686872cb5SThomas Graf 
274786872cb5SThomas Graf 	if (tb[RTA_SRC]) {
274886872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
274986872cb5SThomas Graf 
275086872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
275186872cb5SThomas Graf 			goto errout;
275286872cb5SThomas Graf 
275386872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
27541da177e4SLinus Torvalds 	}
275586872cb5SThomas Graf 
2756c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
275767b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2758c3968a85SDaniel Walter 
275986872cb5SThomas Graf 	if (tb[RTA_OIF])
276086872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
276186872cb5SThomas Graf 
276286872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
276386872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
276486872cb5SThomas Graf 
276586872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
276686872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
276786872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
27681da177e4SLinus Torvalds 	}
276986872cb5SThomas Graf 
277086872cb5SThomas Graf 	if (tb[RTA_TABLE])
277186872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
277286872cb5SThomas Graf 
277351ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
277451ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
277551ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
277651ebd318SNicolas Dichtel 	}
277751ebd318SNicolas Dichtel 
2778c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2779c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2780c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2781c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2782c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2783c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2784c78ba6d6SLubomir Rintel 	}
2785c78ba6d6SLubomir Rintel 
278619e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
278719e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
278819e42e45SRoopa Prabhu 
278919e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP_TYPE])
279019e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
279119e42e45SRoopa Prabhu 
279286872cb5SThomas Graf 	err = 0;
279386872cb5SThomas Graf errout:
279486872cb5SThomas Graf 	return err;
27951da177e4SLinus Torvalds }
27961da177e4SLinus Torvalds 
27976b9ea5a6SRoopa Prabhu struct rt6_nh {
27986b9ea5a6SRoopa Prabhu 	struct rt6_info *rt6_info;
27996b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
28006b9ea5a6SRoopa Prabhu 	struct mx6_config mxc;
28016b9ea5a6SRoopa Prabhu 	struct list_head next;
28026b9ea5a6SRoopa Prabhu };
28036b9ea5a6SRoopa Prabhu 
28046b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
28056b9ea5a6SRoopa Prabhu {
28066b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28076b9ea5a6SRoopa Prabhu 
28086b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28096b9ea5a6SRoopa Prabhu 		pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n",
28106b9ea5a6SRoopa Prabhu 		        &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway,
28116b9ea5a6SRoopa Prabhu 		        nh->r_cfg.fc_ifindex);
28126b9ea5a6SRoopa Prabhu 	}
28136b9ea5a6SRoopa Prabhu }
28146b9ea5a6SRoopa Prabhu 
28156b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list,
28166b9ea5a6SRoopa Prabhu 				 struct rt6_info *rt, struct fib6_config *r_cfg)
28176b9ea5a6SRoopa Prabhu {
28186b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28196b9ea5a6SRoopa Prabhu 	struct rt6_info *rtnh;
28206b9ea5a6SRoopa Prabhu 	int err = -EEXIST;
28216b9ea5a6SRoopa Prabhu 
28226b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28236b9ea5a6SRoopa Prabhu 		/* check if rt6_info already exists */
28246b9ea5a6SRoopa Prabhu 		rtnh = nh->rt6_info;
28256b9ea5a6SRoopa Prabhu 
28266b9ea5a6SRoopa Prabhu 		if (rtnh->dst.dev == rt->dst.dev &&
28276b9ea5a6SRoopa Prabhu 		    rtnh->rt6i_idev == rt->rt6i_idev &&
28286b9ea5a6SRoopa Prabhu 		    ipv6_addr_equal(&rtnh->rt6i_gateway,
28296b9ea5a6SRoopa Prabhu 				    &rt->rt6i_gateway))
28306b9ea5a6SRoopa Prabhu 			return err;
28316b9ea5a6SRoopa Prabhu 	}
28326b9ea5a6SRoopa Prabhu 
28336b9ea5a6SRoopa Prabhu 	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
28346b9ea5a6SRoopa Prabhu 	if (!nh)
28356b9ea5a6SRoopa Prabhu 		return -ENOMEM;
28366b9ea5a6SRoopa Prabhu 	nh->rt6_info = rt;
28376b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&nh->mxc, r_cfg);
28386b9ea5a6SRoopa Prabhu 	if (err) {
28396b9ea5a6SRoopa Prabhu 		kfree(nh);
28406b9ea5a6SRoopa Prabhu 		return err;
28416b9ea5a6SRoopa Prabhu 	}
28426b9ea5a6SRoopa Prabhu 	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
28436b9ea5a6SRoopa Prabhu 	list_add_tail(&nh->next, rt6_nh_list);
28446b9ea5a6SRoopa Prabhu 
28456b9ea5a6SRoopa Prabhu 	return 0;
28466b9ea5a6SRoopa Prabhu }
28476b9ea5a6SRoopa Prabhu 
28486b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg)
284951ebd318SNicolas Dichtel {
285051ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
285151ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
28526b9ea5a6SRoopa Prabhu 	struct rt6_info *rt;
28536b9ea5a6SRoopa Prabhu 	struct rt6_nh *err_nh;
28546b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh, *nh_safe;
285551ebd318SNicolas Dichtel 	int remaining;
285651ebd318SNicolas Dichtel 	int attrlen;
28576b9ea5a6SRoopa Prabhu 	int err = 1;
28586b9ea5a6SRoopa Prabhu 	int nhn = 0;
28596b9ea5a6SRoopa Prabhu 	int replace = (cfg->fc_nlinfo.nlh &&
28606b9ea5a6SRoopa Prabhu 		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
28616b9ea5a6SRoopa Prabhu 	LIST_HEAD(rt6_nh_list);
286251ebd318SNicolas Dichtel 
286335f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
286451ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
286551ebd318SNicolas Dichtel 
28666b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
28676b9ea5a6SRoopa Prabhu 	 * rt6_info structs per nexthop
28686b9ea5a6SRoopa Prabhu 	 */
286951ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
287051ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
287151ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
287251ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
287351ebd318SNicolas Dichtel 
287451ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
287551ebd318SNicolas Dichtel 		if (attrlen > 0) {
287651ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
287751ebd318SNicolas Dichtel 
287851ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
287951ebd318SNicolas Dichtel 			if (nla) {
288067b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
288151ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
288251ebd318SNicolas Dichtel 			}
288319e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
288419e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
288519e42e45SRoopa Prabhu 			if (nla)
288619e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
288751ebd318SNicolas Dichtel 		}
28886b9ea5a6SRoopa Prabhu 
28898c5b83f0SRoopa Prabhu 		rt = ip6_route_info_create(&r_cfg);
28908c5b83f0SRoopa Prabhu 		if (IS_ERR(rt)) {
28918c5b83f0SRoopa Prabhu 			err = PTR_ERR(rt);
28928c5b83f0SRoopa Prabhu 			rt = NULL;
28936b9ea5a6SRoopa Prabhu 			goto cleanup;
28948c5b83f0SRoopa Prabhu 		}
28956b9ea5a6SRoopa Prabhu 
28966b9ea5a6SRoopa Prabhu 		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
289751ebd318SNicolas Dichtel 		if (err) {
28986b9ea5a6SRoopa Prabhu 			dst_free(&rt->dst);
28996b9ea5a6SRoopa Prabhu 			goto cleanup;
290051ebd318SNicolas Dichtel 		}
29016b9ea5a6SRoopa Prabhu 
29026b9ea5a6SRoopa Prabhu 		rtnh = rtnh_next(rtnh, &remaining);
290351ebd318SNicolas Dichtel 	}
29046b9ea5a6SRoopa Prabhu 
29056b9ea5a6SRoopa Prabhu 	err_nh = NULL;
29066b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29076b9ea5a6SRoopa Prabhu 		err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc);
29086b9ea5a6SRoopa Prabhu 		/* nh->rt6_info is used or freed at this point, reset to NULL*/
29096b9ea5a6SRoopa Prabhu 		nh->rt6_info = NULL;
29106b9ea5a6SRoopa Prabhu 		if (err) {
29116b9ea5a6SRoopa Prabhu 			if (replace && nhn)
29126b9ea5a6SRoopa Prabhu 				ip6_print_replace_route_err(&rt6_nh_list);
29136b9ea5a6SRoopa Prabhu 			err_nh = nh;
29146b9ea5a6SRoopa Prabhu 			goto add_errout;
29156b9ea5a6SRoopa Prabhu 		}
29166b9ea5a6SRoopa Prabhu 
29171a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
291827596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
291927596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
292027596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
292127596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
292227596472SMichal Kubeček 		 * be added to it.
29231a72418bSNicolas Dichtel 		 */
292427596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
292527596472SMichal Kubeček 						     NLM_F_REPLACE);
29266b9ea5a6SRoopa Prabhu 		nhn++;
29276b9ea5a6SRoopa Prabhu 	}
29286b9ea5a6SRoopa Prabhu 
29296b9ea5a6SRoopa Prabhu 	goto cleanup;
29306b9ea5a6SRoopa Prabhu 
29316b9ea5a6SRoopa Prabhu add_errout:
29326b9ea5a6SRoopa Prabhu 	/* Delete routes that were already added */
29336b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29346b9ea5a6SRoopa Prabhu 		if (err_nh == nh)
29356b9ea5a6SRoopa Prabhu 			break;
29366b9ea5a6SRoopa Prabhu 		ip6_route_del(&nh->r_cfg);
29376b9ea5a6SRoopa Prabhu 	}
29386b9ea5a6SRoopa Prabhu 
29396b9ea5a6SRoopa Prabhu cleanup:
29406b9ea5a6SRoopa Prabhu 	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
29416b9ea5a6SRoopa Prabhu 		if (nh->rt6_info)
29426b9ea5a6SRoopa Prabhu 			dst_free(&nh->rt6_info->dst);
29436b9ea5a6SRoopa Prabhu 		kfree(nh->mxc.mx);
29446b9ea5a6SRoopa Prabhu 		list_del(&nh->next);
29456b9ea5a6SRoopa Prabhu 		kfree(nh);
29466b9ea5a6SRoopa Prabhu 	}
29476b9ea5a6SRoopa Prabhu 
29486b9ea5a6SRoopa Prabhu 	return err;
29496b9ea5a6SRoopa Prabhu }
29506b9ea5a6SRoopa Prabhu 
29516b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg)
29526b9ea5a6SRoopa Prabhu {
29536b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
29546b9ea5a6SRoopa Prabhu 	struct rtnexthop *rtnh;
29556b9ea5a6SRoopa Prabhu 	int remaining;
29566b9ea5a6SRoopa Prabhu 	int attrlen;
29576b9ea5a6SRoopa Prabhu 	int err = 1, last_err = 0;
29586b9ea5a6SRoopa Prabhu 
29596b9ea5a6SRoopa Prabhu 	remaining = cfg->fc_mp_len;
29606b9ea5a6SRoopa Prabhu 	rtnh = (struct rtnexthop *)cfg->fc_mp;
29616b9ea5a6SRoopa Prabhu 
29626b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry */
29636b9ea5a6SRoopa Prabhu 	while (rtnh_ok(rtnh, remaining)) {
29646b9ea5a6SRoopa Prabhu 		memcpy(&r_cfg, cfg, sizeof(*cfg));
29656b9ea5a6SRoopa Prabhu 		if (rtnh->rtnh_ifindex)
29666b9ea5a6SRoopa Prabhu 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
29676b9ea5a6SRoopa Prabhu 
29686b9ea5a6SRoopa Prabhu 		attrlen = rtnh_attrlen(rtnh);
29696b9ea5a6SRoopa Prabhu 		if (attrlen > 0) {
29706b9ea5a6SRoopa Prabhu 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
29716b9ea5a6SRoopa Prabhu 
29726b9ea5a6SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
29736b9ea5a6SRoopa Prabhu 			if (nla) {
29746b9ea5a6SRoopa Prabhu 				nla_memcpy(&r_cfg.fc_gateway, nla, 16);
29756b9ea5a6SRoopa Prabhu 				r_cfg.fc_flags |= RTF_GATEWAY;
29766b9ea5a6SRoopa Prabhu 			}
29776b9ea5a6SRoopa Prabhu 		}
29786b9ea5a6SRoopa Prabhu 		err = ip6_route_del(&r_cfg);
29796b9ea5a6SRoopa Prabhu 		if (err)
29806b9ea5a6SRoopa Prabhu 			last_err = err;
29816b9ea5a6SRoopa Prabhu 
298251ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
298351ebd318SNicolas Dichtel 	}
298451ebd318SNicolas Dichtel 
298551ebd318SNicolas Dichtel 	return last_err;
298651ebd318SNicolas Dichtel }
298751ebd318SNicolas Dichtel 
2988661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
29891da177e4SLinus Torvalds {
299086872cb5SThomas Graf 	struct fib6_config cfg;
299186872cb5SThomas Graf 	int err;
29921da177e4SLinus Torvalds 
299386872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
299486872cb5SThomas Graf 	if (err < 0)
299586872cb5SThomas Graf 		return err;
299686872cb5SThomas Graf 
299751ebd318SNicolas Dichtel 	if (cfg.fc_mp)
29986b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_del(&cfg);
299951ebd318SNicolas Dichtel 	else
300086872cb5SThomas Graf 		return ip6_route_del(&cfg);
30011da177e4SLinus Torvalds }
30021da177e4SLinus Torvalds 
3003661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
30041da177e4SLinus Torvalds {
300586872cb5SThomas Graf 	struct fib6_config cfg;
300686872cb5SThomas Graf 	int err;
30071da177e4SLinus Torvalds 
300886872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
300986872cb5SThomas Graf 	if (err < 0)
301086872cb5SThomas Graf 		return err;
301186872cb5SThomas Graf 
301251ebd318SNicolas Dichtel 	if (cfg.fc_mp)
30136b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_add(&cfg);
301451ebd318SNicolas Dichtel 	else
301586872cb5SThomas Graf 		return ip6_route_add(&cfg);
30161da177e4SLinus Torvalds }
30171da177e4SLinus Torvalds 
301819e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
3019339bf98fSThomas Graf {
3020339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
3021339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
3022339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
3023339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
3024339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
3025339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
3026339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
3027339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
3028339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
30296a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
3030ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
3031c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
303219e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
303361adedf3SJiri Benc 	       + lwtunnel_get_encap_size(rt->dst.lwtstate);
3034339bf98fSThomas Graf }
3035339bf98fSThomas Graf 
3036191cd582SBrian Haley static int rt6_fill_node(struct net *net,
3037191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
30380d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
303915e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
30407bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
30411da177e4SLinus Torvalds {
30424b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
30431da177e4SLinus Torvalds 	struct rtmsg *rtm;
30441da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
3045e3703b3dSThomas Graf 	long expires;
30469e762a4aSPatrick McHardy 	u32 table;
30471da177e4SLinus Torvalds 
30481da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
30491da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
30501da177e4SLinus Torvalds 			/* success since this is not a prefix route */
30511da177e4SLinus Torvalds 			return 1;
30521da177e4SLinus Torvalds 		}
30531da177e4SLinus Torvalds 	}
30541da177e4SLinus Torvalds 
305515e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
305638308473SDavid S. Miller 	if (!nlh)
305726932566SPatrick McHardy 		return -EMSGSIZE;
30582d7202bfSThomas Graf 
30592d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
30601da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
30611da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
30621da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
30631da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
3064c71099acSThomas Graf 	if (rt->rt6i_table)
30659e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
3066c71099acSThomas Graf 	else
30679e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
30689e762a4aSPatrick McHardy 	rtm->rtm_table = table;
3069c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
3070c78679e8SDavid S. Miller 		goto nla_put_failure;
3071ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
3072ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
3073ef2c7d7bSNicolas Dichtel 		case -EINVAL:
3074ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
3075ef2c7d7bSNicolas Dichtel 			break;
3076ef2c7d7bSNicolas Dichtel 		case -EACCES:
3077ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
3078ef2c7d7bSNicolas Dichtel 			break;
3079b4949ab2SNicolas Dichtel 		case -EAGAIN:
3080b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
3081b4949ab2SNicolas Dichtel 			break;
3082ef2c7d7bSNicolas Dichtel 		default:
30831da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
3084ef2c7d7bSNicolas Dichtel 			break;
3085ef2c7d7bSNicolas Dichtel 		}
3086ef2c7d7bSNicolas Dichtel 	}
3087ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
3088ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
3089d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
30901da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
30911da177e4SLinus Torvalds 	else
30921da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
30931da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
309435103d11SAndy Gospodarek 	if (!netif_carrier_ok(rt->dst.dev)) {
3095cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
309635103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
309735103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
309835103d11SAndy Gospodarek 	}
30991da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
31001da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
31011da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
31021da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
3103f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
3104f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
31051da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
3106f0396f60SDenis Ovsienko 		else
3107f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
3108f0396f60SDenis Ovsienko 	}
31091da177e4SLinus Torvalds 
31101da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
31111da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
31121da177e4SLinus Torvalds 
31131da177e4SLinus Torvalds 	if (dst) {
3114930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
3115c78679e8SDavid S. Miller 			goto nla_put_failure;
31161da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
31171da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
3118930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
3119c78679e8SDavid S. Miller 			goto nla_put_failure;
31201da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
31211da177e4SLinus Torvalds 	if (src) {
3122930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
3123c78679e8SDavid S. Miller 			goto nla_put_failure;
31241da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
3125c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
3126930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
3127c78679e8SDavid S. Miller 		goto nla_put_failure;
31281da177e4SLinus Torvalds #endif
31297bc570c8SYOSHIFUJI Hideaki 	if (iif) {
31307bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
31317bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
31328229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
31337bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
31347bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
31357bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
31367bc570c8SYOSHIFUJI Hideaki 						return 0;
31377bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
31387bc570c8SYOSHIFUJI Hideaki 				} else {
31397bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
31407bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
31417bc570c8SYOSHIFUJI Hideaki 				}
31427bc570c8SYOSHIFUJI Hideaki 			}
31437bc570c8SYOSHIFUJI Hideaki 		} else
31447bc570c8SYOSHIFUJI Hideaki #endif
3145c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
3146c78679e8SDavid S. Miller 				goto nla_put_failure;
31477bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
31481da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
3149c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
3150930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3151c78679e8SDavid S. Miller 			goto nla_put_failure;
3152c3968a85SDaniel Walter 	}
3153c3968a85SDaniel Walter 
3154c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
3155c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
31564e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
3157930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3158c78679e8SDavid S. Miller 			goto nla_put_failure;
31591da177e4SLinus Torvalds 	}
31602d7202bfSThomas Graf 
31614b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
31624b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
31634b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
31644b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
31652d7202bfSThomas Graf 		goto nla_put_failure;
31662d7202bfSThomas Graf 
3167dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
3168930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
316994f826b8SEric Dumazet 			goto nla_put_failure;
317094f826b8SEric Dumazet 	}
31712d7202bfSThomas Graf 
3172c78679e8SDavid S. Miller 	if (rt->dst.dev &&
3173c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3174c78679e8SDavid S. Miller 		goto nla_put_failure;
3175c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
3176c78679e8SDavid S. Miller 		goto nla_put_failure;
31778253947eSLi Wei 
31788253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
317969cdf8f9SYOSHIFUJI Hideaki 
318087a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
3181e3703b3dSThomas Graf 		goto nla_put_failure;
31821da177e4SLinus Torvalds 
3183c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
3184c78ba6d6SLubomir Rintel 		goto nla_put_failure;
3185c78ba6d6SLubomir Rintel 
318661adedf3SJiri Benc 	lwtunnel_fill_encap(skb, rt->dst.lwtstate);
318719e42e45SRoopa Prabhu 
3188053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
3189053c095aSJohannes Berg 	return 0;
31902d7202bfSThomas Graf 
31912d7202bfSThomas Graf nla_put_failure:
319226932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
319326932566SPatrick McHardy 	return -EMSGSIZE;
31941da177e4SLinus Torvalds }
31951da177e4SLinus Torvalds 
31961b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
31971da177e4SLinus Torvalds {
31981da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
31991da177e4SLinus Torvalds 	int prefix;
32001da177e4SLinus Torvalds 
32012d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
32022d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
32031da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
32041da177e4SLinus Torvalds 	} else
32051da177e4SLinus Torvalds 		prefix = 0;
32061da177e4SLinus Torvalds 
3207191cd582SBrian Haley 	return rt6_fill_node(arg->net,
3208191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
320915e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
32107bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
32111da177e4SLinus Torvalds }
32121da177e4SLinus Torvalds 
3213661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
32141da177e4SLinus Torvalds {
32153b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3216ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
32171da177e4SLinus Torvalds 	struct rt6_info *rt;
3218ab364a6fSThomas Graf 	struct sk_buff *skb;
3219ab364a6fSThomas Graf 	struct rtmsg *rtm;
32204c9483b2SDavid S. Miller 	struct flowi6 fl6;
322172331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3222ab364a6fSThomas Graf 
3223ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3224ab364a6fSThomas Graf 	if (err < 0)
3225ab364a6fSThomas Graf 		goto errout;
3226ab364a6fSThomas Graf 
3227ab364a6fSThomas Graf 	err = -EINVAL;
32284c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
3229ab364a6fSThomas Graf 
3230ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3231ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3232ab364a6fSThomas Graf 			goto errout;
3233ab364a6fSThomas Graf 
32344e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3235ab364a6fSThomas Graf 	}
3236ab364a6fSThomas Graf 
3237ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3238ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3239ab364a6fSThomas Graf 			goto errout;
3240ab364a6fSThomas Graf 
32414e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3242ab364a6fSThomas Graf 	}
3243ab364a6fSThomas Graf 
3244ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3245ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3246ab364a6fSThomas Graf 
3247ab364a6fSThomas Graf 	if (tb[RTA_OIF])
324872331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3249ab364a6fSThomas Graf 
32502e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
32512e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
32522e47b291SLorenzo Colitti 
3253ab364a6fSThomas Graf 	if (iif) {
3254ab364a6fSThomas Graf 		struct net_device *dev;
325572331bc0SShmulik Ladkani 		int flags = 0;
325672331bc0SShmulik Ladkani 
32575578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3258ab364a6fSThomas Graf 		if (!dev) {
3259ab364a6fSThomas Graf 			err = -ENODEV;
3260ab364a6fSThomas Graf 			goto errout;
3261ab364a6fSThomas Graf 		}
326272331bc0SShmulik Ladkani 
326372331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
326472331bc0SShmulik Ladkani 
326572331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
326672331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
326772331bc0SShmulik Ladkani 
326872331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
326972331bc0SShmulik Ladkani 							       flags);
327072331bc0SShmulik Ladkani 	} else {
327172331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
327272331bc0SShmulik Ladkani 
3273ca254490SDavid Ahern 		if (netif_index_is_l3_master(net, oif)) {
3274ca254490SDavid Ahern 			fl6.flowi6_flags = FLOWI_FLAG_L3MDEV_SRC |
3275ca254490SDavid Ahern 					   FLOWI_FLAG_SKIP_NH_OIF;
3276ca254490SDavid Ahern 		}
3277ca254490SDavid Ahern 
327872331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3279ab364a6fSThomas Graf 	}
32801da177e4SLinus Torvalds 
32811da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
328238308473SDavid S. Miller 	if (!skb) {
328394e187c0SAmerigo Wang 		ip6_rt_put(rt);
3284ab364a6fSThomas Graf 		err = -ENOBUFS;
3285ab364a6fSThomas Graf 		goto errout;
3286ab364a6fSThomas Graf 	}
32871da177e4SLinus Torvalds 
32881da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
32891da177e4SLinus Torvalds 	   through good chunk of routing engine.
32901da177e4SLinus Torvalds 	 */
3291459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
32921da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
32931da177e4SLinus Torvalds 
3294d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
32951da177e4SLinus Torvalds 
32964c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
329715e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
32987bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
32991da177e4SLinus Torvalds 	if (err < 0) {
3300ab364a6fSThomas Graf 		kfree_skb(skb);
3301ab364a6fSThomas Graf 		goto errout;
33021da177e4SLinus Torvalds 	}
33031da177e4SLinus Torvalds 
330415e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3305ab364a6fSThomas Graf errout:
33061da177e4SLinus Torvalds 	return err;
33071da177e4SLinus Torvalds }
33081da177e4SLinus Torvalds 
330937a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
331037a1d361SRoopa Prabhu 		     unsigned int nlm_flags)
33111da177e4SLinus Torvalds {
33121da177e4SLinus Torvalds 	struct sk_buff *skb;
33135578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3314528c4cebSDenis V. Lunev 	u32 seq;
3315528c4cebSDenis V. Lunev 	int err;
33160d51aa80SJamal Hadi Salim 
3317528c4cebSDenis V. Lunev 	err = -ENOBUFS;
331838308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
331986872cb5SThomas Graf 
332019e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
332138308473SDavid S. Miller 	if (!skb)
332221713ebcSThomas Graf 		goto errout;
33231da177e4SLinus Torvalds 
3324191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
332537a1d361SRoopa Prabhu 				event, info->portid, seq, 0, 0, nlm_flags);
332626932566SPatrick McHardy 	if (err < 0) {
332726932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
332826932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
332926932566SPatrick McHardy 		kfree_skb(skb);
333026932566SPatrick McHardy 		goto errout;
333126932566SPatrick McHardy 	}
333215e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
33335578689aSDaniel Lezcano 		    info->nlh, gfp_any());
33341ce85fe4SPablo Neira Ayuso 	return;
333521713ebcSThomas Graf errout:
333621713ebcSThomas Graf 	if (err < 0)
33375578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
33381da177e4SLinus Torvalds }
33391da177e4SLinus Torvalds 
33408ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3341351638e7SJiri Pirko 				unsigned long event, void *ptr)
33428ed67789SDaniel Lezcano {
3343351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3344c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
33458ed67789SDaniel Lezcano 
33468ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3347d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
33488ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
33498ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3350d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
33518ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3352d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
33538ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
33548ed67789SDaniel Lezcano #endif
33558ed67789SDaniel Lezcano 	}
33568ed67789SDaniel Lezcano 
33578ed67789SDaniel Lezcano 	return NOTIFY_OK;
33588ed67789SDaniel Lezcano }
33598ed67789SDaniel Lezcano 
33601da177e4SLinus Torvalds /*
33611da177e4SLinus Torvalds  *	/proc
33621da177e4SLinus Torvalds  */
33631da177e4SLinus Torvalds 
33641da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
33651da177e4SLinus Torvalds 
336633120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
336733120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
336833120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
336933120b30SAlexey Dobriyan 	.read		= seq_read,
337033120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
33718d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
337233120b30SAlexey Dobriyan };
337333120b30SAlexey Dobriyan 
33741da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
33751da177e4SLinus Torvalds {
337669ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
33771da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
337869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
337969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
338069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
338169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
338269ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3383fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
338469ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
33851da177e4SLinus Torvalds 
33861da177e4SLinus Torvalds 	return 0;
33871da177e4SLinus Torvalds }
33881da177e4SLinus Torvalds 
33891da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
33901da177e4SLinus Torvalds {
3391de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
339269ddb805SDaniel Lezcano }
339369ddb805SDaniel Lezcano 
33949a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
33951da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
33961da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
33971da177e4SLinus Torvalds 	.read	 = seq_read,
33981da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3399b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
34001da177e4SLinus Torvalds };
34011da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
34021da177e4SLinus Torvalds 
34031da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
34041da177e4SLinus Torvalds 
34051da177e4SLinus Torvalds static
3406fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
34071da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
34081da177e4SLinus Torvalds {
3409c486da34SLucian Adrian Grijincu 	struct net *net;
3410c486da34SLucian Adrian Grijincu 	int delay;
3411c486da34SLucian Adrian Grijincu 	if (!write)
3412c486da34SLucian Adrian Grijincu 		return -EINVAL;
3413c486da34SLucian Adrian Grijincu 
3414c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3415c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
34168d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
34172ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
34181da177e4SLinus Torvalds 	return 0;
34191da177e4SLinus Torvalds }
34201da177e4SLinus Torvalds 
3421fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
34221da177e4SLinus Torvalds 	{
34231da177e4SLinus Torvalds 		.procname	=	"flush",
34244990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
34251da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
342689c8b3a1SDave Jones 		.mode		=	0200,
34276d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
34281da177e4SLinus Torvalds 	},
34291da177e4SLinus Torvalds 	{
34301da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
34319a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
34321da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34331da177e4SLinus Torvalds 		.mode		=	0644,
34346d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34351da177e4SLinus Torvalds 	},
34361da177e4SLinus Torvalds 	{
34371da177e4SLinus Torvalds 		.procname	=	"max_size",
34384990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
34391da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34401da177e4SLinus Torvalds 		.mode		=	0644,
34416d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34421da177e4SLinus Torvalds 	},
34431da177e4SLinus Torvalds 	{
34441da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
34454990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34461da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34471da177e4SLinus Torvalds 		.mode		=	0644,
34486d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34491da177e4SLinus Torvalds 	},
34501da177e4SLinus Torvalds 	{
34511da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
34524990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
34531da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34541da177e4SLinus Torvalds 		.mode		=	0644,
34556d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34561da177e4SLinus Torvalds 	},
34571da177e4SLinus Torvalds 	{
34581da177e4SLinus Torvalds 		.procname	=	"gc_interval",
34594990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
34601da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34611da177e4SLinus Torvalds 		.mode		=	0644,
34626d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34631da177e4SLinus Torvalds 	},
34641da177e4SLinus Torvalds 	{
34651da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
34664990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
34671da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34681da177e4SLinus Torvalds 		.mode		=	0644,
3469f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34701da177e4SLinus Torvalds 	},
34711da177e4SLinus Torvalds 	{
34721da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
34734990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
34741da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34751da177e4SLinus Torvalds 		.mode		=	0644,
34766d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34771da177e4SLinus Torvalds 	},
34781da177e4SLinus Torvalds 	{
34791da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
34804990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
34811da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34821da177e4SLinus Torvalds 		.mode		=	0644,
3483f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34841da177e4SLinus Torvalds 	},
34851da177e4SLinus Torvalds 	{
34861da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
34874990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34881da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34891da177e4SLinus Torvalds 		.mode		=	0644,
34906d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
34911da177e4SLinus Torvalds 	},
3492f8572d8fSEric W. Biederman 	{ }
34931da177e4SLinus Torvalds };
34941da177e4SLinus Torvalds 
34952c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3496760f2d01SDaniel Lezcano {
3497760f2d01SDaniel Lezcano 	struct ctl_table *table;
3498760f2d01SDaniel Lezcano 
3499760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3500760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3501760f2d01SDaniel Lezcano 			GFP_KERNEL);
35025ee09105SYOSHIFUJI Hideaki 
35035ee09105SYOSHIFUJI Hideaki 	if (table) {
35045ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3505c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
350686393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
35075ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
35085ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
35095ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
35105ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
35115ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
35125ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
35135ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
35149c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3515464dc801SEric W. Biederman 
3516464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3517464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3518464dc801SEric W. Biederman 			table[0].procname = NULL;
35195ee09105SYOSHIFUJI Hideaki 	}
35205ee09105SYOSHIFUJI Hideaki 
3521760f2d01SDaniel Lezcano 	return table;
3522760f2d01SDaniel Lezcano }
35231da177e4SLinus Torvalds #endif
35241da177e4SLinus Torvalds 
35252c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3526cdb18761SDaniel Lezcano {
3527633d424bSPavel Emelyanov 	int ret = -ENOMEM;
35288ed67789SDaniel Lezcano 
352986393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
353086393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3531f2fc6a54SBenjamin Thery 
3532fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3533fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3534fc66f95cSEric Dumazet 
35358ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
35368ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
35378ed67789SDaniel Lezcano 					   GFP_KERNEL);
35388ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3539fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3540d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
35418ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3542d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
354362fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
354462fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35458ed67789SDaniel Lezcano 
35468ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
35478ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
35488ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
35498ed67789SDaniel Lezcano 					       GFP_KERNEL);
355068fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
355168fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3552d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
35538ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3554d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
355562fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
355662fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35578ed67789SDaniel Lezcano 
35588ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
35598ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
35608ed67789SDaniel Lezcano 					       GFP_KERNEL);
356168fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
356268fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3563d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
35648ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3565d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
356662fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
356762fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35688ed67789SDaniel Lezcano #endif
35698ed67789SDaniel Lezcano 
3570b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3571b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3572b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3573b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3574b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3575b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3576b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3577b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3578b339a47cSPeter Zijlstra 
35796891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
35806891a346SBenjamin Thery 
35818ed67789SDaniel Lezcano 	ret = 0;
35828ed67789SDaniel Lezcano out:
35838ed67789SDaniel Lezcano 	return ret;
3584f2fc6a54SBenjamin Thery 
358568fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
358668fffc67SPeter Zijlstra out_ip6_prohibit_entry:
358768fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
358868fffc67SPeter Zijlstra out_ip6_null_entry:
358968fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
359068fffc67SPeter Zijlstra #endif
3591fc66f95cSEric Dumazet out_ip6_dst_entries:
3592fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3593f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3594f2fc6a54SBenjamin Thery 	goto out;
3595cdb18761SDaniel Lezcano }
3596cdb18761SDaniel Lezcano 
35972c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3598cdb18761SDaniel Lezcano {
35998ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
36008ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
36018ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
36028ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
36038ed67789SDaniel Lezcano #endif
360441bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3605cdb18761SDaniel Lezcano }
3606cdb18761SDaniel Lezcano 
3607d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3608d189634eSThomas Graf {
3609d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3610d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3611d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3612d189634eSThomas Graf #endif
3613d189634eSThomas Graf 	return 0;
3614d189634eSThomas Graf }
3615d189634eSThomas Graf 
3616d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3617d189634eSThomas Graf {
3618d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3619ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3620ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3621d189634eSThomas Graf #endif
3622d189634eSThomas Graf }
3623d189634eSThomas Graf 
3624cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3625cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3626cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3627cdb18761SDaniel Lezcano };
3628cdb18761SDaniel Lezcano 
3629c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3630c3426b47SDavid S. Miller {
3631c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3632c3426b47SDavid S. Miller 
3633c3426b47SDavid S. Miller 	if (!bp)
3634c3426b47SDavid S. Miller 		return -ENOMEM;
3635c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3636c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3637c3426b47SDavid S. Miller 	return 0;
3638c3426b47SDavid S. Miller }
3639c3426b47SDavid S. Miller 
3640c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3641c3426b47SDavid S. Miller {
3642c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3643c3426b47SDavid S. Miller 
3644c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
364556a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3646c3426b47SDavid S. Miller 	kfree(bp);
3647c3426b47SDavid S. Miller }
3648c3426b47SDavid S. Miller 
36492b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3650c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3651c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3652c3426b47SDavid S. Miller };
3653c3426b47SDavid S. Miller 
3654d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3655d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3656d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3657d189634eSThomas Graf };
3658d189634eSThomas Graf 
36598ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
36608ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
36618ed67789SDaniel Lezcano 	.priority = 0,
36628ed67789SDaniel Lezcano };
36638ed67789SDaniel Lezcano 
3664433d49c3SDaniel Lezcano int __init ip6_route_init(void)
36651da177e4SLinus Torvalds {
3666433d49c3SDaniel Lezcano 	int ret;
36678d0b94afSMartin KaFai Lau 	int cpu;
3668433d49c3SDaniel Lezcano 
36699a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
36709a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
36719a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
36729a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
36739a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3674c19a28e1SFernando Carrijo 		goto out;
367514e50e57SDavid S. Miller 
3676fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
36778ed67789SDaniel Lezcano 	if (ret)
3678bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3679bdb3289fSDaniel Lezcano 
3680c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3681c3426b47SDavid S. Miller 	if (ret)
3682e8803b6cSDavid S. Miller 		goto out_dst_entries;
36832a0c451aSThomas Graf 
36847e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
36857e52b33bSDavid S. Miller 	if (ret)
36867e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3687c3426b47SDavid S. Miller 
36885dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
36895dc121e9SArnaud Ebalard 
36908ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
36918ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
36928ed67789SDaniel Lezcano 	 * manually for init_net */
3693d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
36948ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3695bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3696d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
36978ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3698d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
36998ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3700bdb3289fSDaniel Lezcano   #endif
3701e8803b6cSDavid S. Miller 	ret = fib6_init();
3702433d49c3SDaniel Lezcano 	if (ret)
37038ed67789SDaniel Lezcano 		goto out_register_subsys;
3704433d49c3SDaniel Lezcano 
3705433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3706433d49c3SDaniel Lezcano 	if (ret)
3707e8803b6cSDavid S. Miller 		goto out_fib6_init;
3708c35b7e72SDaniel Lezcano 
3709433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3710433d49c3SDaniel Lezcano 	if (ret)
3711433d49c3SDaniel Lezcano 		goto xfrm6_init;
37127e5449c2SDaniel Lezcano 
3713d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3714d189634eSThomas Graf 	if (ret)
3715d189634eSThomas Graf 		goto fib6_rules_init;
3716d189634eSThomas Graf 
3717433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3718c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3719c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3720c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3721d189634eSThomas Graf 		goto out_register_late_subsys;
3722433d49c3SDaniel Lezcano 
37238ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3724cdb18761SDaniel Lezcano 	if (ret)
3725d189634eSThomas Graf 		goto out_register_late_subsys;
37268ed67789SDaniel Lezcano 
37278d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
37288d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
37298d0b94afSMartin KaFai Lau 
37308d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
37318d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
37328d0b94afSMartin KaFai Lau 	}
37338d0b94afSMartin KaFai Lau 
3734433d49c3SDaniel Lezcano out:
3735433d49c3SDaniel Lezcano 	return ret;
3736433d49c3SDaniel Lezcano 
3737d189634eSThomas Graf out_register_late_subsys:
3738d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3739433d49c3SDaniel Lezcano fib6_rules_init:
3740433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3741433d49c3SDaniel Lezcano xfrm6_init:
3742433d49c3SDaniel Lezcano 	xfrm6_fini();
37432a0c451aSThomas Graf out_fib6_init:
37442a0c451aSThomas Graf 	fib6_gc_cleanup();
37458ed67789SDaniel Lezcano out_register_subsys:
37468ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
37477e52b33bSDavid S. Miller out_register_inetpeer:
37487e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3749fc66f95cSEric Dumazet out_dst_entries:
3750fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3751433d49c3SDaniel Lezcano out_kmem_cache:
3752f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3753433d49c3SDaniel Lezcano 	goto out;
37541da177e4SLinus Torvalds }
37551da177e4SLinus Torvalds 
37561da177e4SLinus Torvalds void ip6_route_cleanup(void)
37571da177e4SLinus Torvalds {
37588ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3759d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3760101367c2SThomas Graf 	fib6_rules_cleanup();
37611da177e4SLinus Torvalds 	xfrm6_fini();
37621da177e4SLinus Torvalds 	fib6_gc_cleanup();
3763c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
37648ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
376541bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3766f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
37671da177e4SLinus Torvalds }
3768