xref: /openbmc/linux/net/ipv6/route.c (revision 5973fb1e245086071bf71994c8b54d99526ded03)
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 
407*5973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt)
408*5973fb1eSMartin KaFai Lau {
409*5973fb1eSMartin KaFai Lau 	if (rt->rt6i_flags & RTF_EXPIRES)
410*5973fb1eSMartin KaFai Lau 		return time_after(jiffies, rt->dst.expires);
411*5973fb1eSMartin KaFai Lau 	else
412*5973fb1eSMartin KaFai Lau 		return false;
413*5973fb1eSMartin KaFai Lau }
414*5973fb1eSMartin KaFai Lau 
415a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4161da177e4SLinus Torvalds {
4171716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4181716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
419a50feda5SEric Dumazet 			return true;
4201716a961SGao feng 	} else if (rt->dst.from) {
4213fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
4221716a961SGao feng 	}
423a50feda5SEric Dumazet 	return false;
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds 
42651ebd318SNicolas Dichtel /* Multipath route selection:
42751ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
42851ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
42951ebd318SNicolas Dichtel  */
43051ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
43151ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
43251ebd318SNicolas Dichtel {
433644d0e65STom Herbert 	return get_hash_from_flowi6(fl6) % candidate_count;
43451ebd318SNicolas Dichtel }
43551ebd318SNicolas Dichtel 
43651ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
43752bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
43852bd4c0cSNicolas Dichtel 					     int strict)
43951ebd318SNicolas Dichtel {
44051ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
44151ebd318SNicolas Dichtel 	int route_choosen;
44251ebd318SNicolas Dichtel 
44351ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
44451ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
44551ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
44651ebd318SNicolas Dichtel 	 */
44751ebd318SNicolas Dichtel 	if (route_choosen)
44851ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
44951ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
45051ebd318SNicolas Dichtel 			route_choosen--;
45151ebd318SNicolas Dichtel 			if (route_choosen == 0) {
45252bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
45352bd4c0cSNicolas Dichtel 					break;
45451ebd318SNicolas Dichtel 				match = sibling;
45551ebd318SNicolas Dichtel 				break;
45651ebd318SNicolas Dichtel 			}
45751ebd318SNicolas Dichtel 		}
45851ebd318SNicolas Dichtel 	return match;
45951ebd318SNicolas Dichtel }
46051ebd318SNicolas Dichtel 
4611da177e4SLinus Torvalds /*
462c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4631da177e4SLinus Torvalds  */
4641da177e4SLinus Torvalds 
4658ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4668ed67789SDaniel Lezcano 						    struct rt6_info *rt,
467b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4681da177e4SLinus Torvalds 						    int oif,
469d420895eSYOSHIFUJI Hideaki 						    int flags)
4701da177e4SLinus Torvalds {
4711da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4721da177e4SLinus Torvalds 	struct rt6_info *sprt;
4731da177e4SLinus Torvalds 
474dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
475dd3abc4eSYOSHIFUJI Hideaki 		goto out;
476dd3abc4eSYOSHIFUJI Hideaki 
477d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
478d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
479dd3abc4eSYOSHIFUJI Hideaki 
480dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4811da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4821da177e4SLinus Torvalds 				return sprt;
4831da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
48438308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4851da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
48617fb0b2bSDavid Ahern 					if (flags & RT6_LOOKUP_F_IFACE)
4871da177e4SLinus Torvalds 						continue;
48817fb0b2bSDavid Ahern 					if (local &&
48917fb0b2bSDavid Ahern 					    local->rt6i_idev->dev->ifindex == oif)
4901da177e4SLinus Torvalds 						continue;
4911da177e4SLinus Torvalds 				}
4921da177e4SLinus Torvalds 				local = sprt;
4931da177e4SLinus Torvalds 			}
494dd3abc4eSYOSHIFUJI Hideaki 		} else {
495dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
496dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
497dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
498dd3abc4eSYOSHIFUJI Hideaki 		}
4991da177e4SLinus Torvalds 	}
5001da177e4SLinus Torvalds 
501dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
5021da177e4SLinus Torvalds 		if (local)
5031da177e4SLinus Torvalds 			return local;
5041da177e4SLinus Torvalds 
505d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
5068ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
5071da177e4SLinus Torvalds 	}
508dd3abc4eSYOSHIFUJI Hideaki out:
5091da177e4SLinus Torvalds 	return rt;
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds 
51227097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
513c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
514c2f17e82SHannes Frederic Sowa 	struct work_struct work;
515c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
516c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
517c2f17e82SHannes Frederic Sowa };
518c2f17e82SHannes Frederic Sowa 
519c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
520c2f17e82SHannes Frederic Sowa {
521c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
522c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
523c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
524c2f17e82SHannes Frederic Sowa 
525c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
52638cf595bSJiri Benc 	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, NULL);
527c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
528662f5533SMichael Büsch 	kfree(work);
529c2f17e82SHannes Frederic Sowa }
530c2f17e82SHannes Frederic Sowa 
53127097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
53227097255SYOSHIFUJI Hideaki {
533990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
534f2c31e32SEric Dumazet 	struct neighbour *neigh;
53527097255SYOSHIFUJI Hideaki 	/*
53627097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
53727097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
53827097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
53927097255SYOSHIFUJI Hideaki 	 *
54027097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
54127097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
54227097255SYOSHIFUJI Hideaki 	 */
5432152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
544fdd6681dSAmerigo Wang 		return;
5452152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5462152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5472152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5488d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5498d6c31bfSMartin KaFai Lau 			goto out;
5508d6c31bfSMartin KaFai Lau 
551990edb42SMartin KaFai Lau 		work = NULL;
5522152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
553990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
554990edb42SMartin KaFai Lau 		    time_after(jiffies,
555990edb42SMartin KaFai Lau 			       neigh->updated +
556990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
557c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
558990edb42SMartin KaFai Lau 			if (work)
5597e980569SJiri Benc 				__neigh_set_probe_once(neigh);
560990edb42SMartin KaFai Lau 		}
561c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
562990edb42SMartin KaFai Lau 	} else {
563990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
564990edb42SMartin KaFai Lau 	}
565c2f17e82SHannes Frederic Sowa 
566c2f17e82SHannes Frederic Sowa 	if (work) {
567c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
568c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
569c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
570c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
571c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
572c2f17e82SHannes Frederic Sowa 	}
573990edb42SMartin KaFai Lau 
5748d6c31bfSMartin KaFai Lau out:
5752152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
576f2c31e32SEric Dumazet }
57727097255SYOSHIFUJI Hideaki #else
57827097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
57927097255SYOSHIFUJI Hideaki {
58027097255SYOSHIFUJI Hideaki }
58127097255SYOSHIFUJI Hideaki #endif
58227097255SYOSHIFUJI Hideaki 
5831da177e4SLinus Torvalds /*
584554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5851da177e4SLinus Torvalds  */
586b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5871da177e4SLinus Torvalds {
588d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
589161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
590554cfb7eSYOSHIFUJI Hideaki 		return 2;
591161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
592161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
593161980f4SDavid S. Miller 		return 1;
594554cfb7eSYOSHIFUJI Hideaki 	return 0;
5951da177e4SLinus Torvalds }
5961da177e4SLinus Torvalds 
597afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
5981da177e4SLinus Torvalds {
599f2c31e32SEric Dumazet 	struct neighbour *neigh;
600afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
601f2c31e32SEric Dumazet 
6024d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
6034d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
604afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
605145a3621SYOSHIFUJI Hideaki / 吉藤英明 
606145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
607145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
608145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
609145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
610554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
611afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
612398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
613a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
614afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6157e980569SJiri Benc 		else
6167e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
617398bcbebSYOSHIFUJI Hideaki #endif
618145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
619afc154e9SHannes Frederic Sowa 	} else {
620afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6217e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
622a5a81f0bSPaul Marks 	}
623145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
624145a3621SYOSHIFUJI Hideaki / 吉藤英明 
625a5a81f0bSPaul Marks 	return ret;
6261da177e4SLinus Torvalds }
6271da177e4SLinus Torvalds 
628554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
629554cfb7eSYOSHIFUJI Hideaki 			   int strict)
630554cfb7eSYOSHIFUJI Hideaki {
631a5a81f0bSPaul Marks 	int m;
6324d0c5911SYOSHIFUJI Hideaki 
6334d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
63477d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
635afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
636ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
637ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
638ebacaaa0SYOSHIFUJI Hideaki #endif
639afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
640afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
641afc154e9SHannes Frederic Sowa 		if (n < 0)
642afc154e9SHannes Frederic Sowa 			return n;
643afc154e9SHannes Frederic Sowa 	}
644554cfb7eSYOSHIFUJI Hideaki 	return m;
645554cfb7eSYOSHIFUJI Hideaki }
646554cfb7eSYOSHIFUJI Hideaki 
647f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
648afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
649afc154e9SHannes Frederic Sowa 				   bool *do_rr)
650554cfb7eSYOSHIFUJI Hideaki {
651554cfb7eSYOSHIFUJI Hideaki 	int m;
652afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
65335103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
65435103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
65535103d11SAndy Gospodarek 
65635103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
65735103d11SAndy Gospodarek 	    idev->cnf.ignore_routes_with_linkdown)
65835103d11SAndy Gospodarek 		goto out;
659554cfb7eSYOSHIFUJI Hideaki 
660554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
661f11e6659SDavid S. Miller 		goto out;
662554cfb7eSYOSHIFUJI Hideaki 
663554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6647e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
665afc154e9SHannes Frederic Sowa 		match_do_rr = true;
666afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6677e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
668f11e6659SDavid S. Miller 		goto out;
6691da177e4SLinus Torvalds 	}
670f11e6659SDavid S. Miller 
671afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
672afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
673afc154e9SHannes Frederic Sowa 
6747e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
675afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
676afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
677afc154e9SHannes Frederic Sowa 		*mpri = m;
678afc154e9SHannes Frederic Sowa 		match = rt;
679afc154e9SHannes Frederic Sowa 	}
680f11e6659SDavid S. Miller out:
681f11e6659SDavid S. Miller 	return match;
6821da177e4SLinus Torvalds }
6831da177e4SLinus Torvalds 
684f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
685f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
686afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
687afc154e9SHannes Frederic Sowa 				     bool *do_rr)
688f11e6659SDavid S. Miller {
6899fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
690f11e6659SDavid S. Miller 	int mpri = -1;
691f11e6659SDavid S. Miller 
692f11e6659SDavid S. Miller 	match = NULL;
6939fbdcfafSSteffen Klassert 	cont = NULL;
6949fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
6959fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6969fbdcfafSSteffen Klassert 			cont = rt;
6979fbdcfafSSteffen Klassert 			break;
6989fbdcfafSSteffen Klassert 		}
6999fbdcfafSSteffen Klassert 
700afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7019fbdcfafSSteffen Klassert 	}
7029fbdcfafSSteffen Klassert 
7039fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
7049fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7059fbdcfafSSteffen Klassert 			cont = rt;
7069fbdcfafSSteffen Klassert 			break;
7079fbdcfafSSteffen Klassert 		}
7089fbdcfafSSteffen Klassert 
7099fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7109fbdcfafSSteffen Klassert 	}
7119fbdcfafSSteffen Klassert 
7129fbdcfafSSteffen Klassert 	if (match || !cont)
7139fbdcfafSSteffen Klassert 		return match;
7149fbdcfafSSteffen Klassert 
7159fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
716afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
717f11e6659SDavid S. Miller 
718f11e6659SDavid S. Miller 	return match;
719f11e6659SDavid S. Miller }
720f11e6659SDavid S. Miller 
721f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
722f11e6659SDavid S. Miller {
723f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7248ed67789SDaniel Lezcano 	struct net *net;
725afc154e9SHannes Frederic Sowa 	bool do_rr = false;
726f11e6659SDavid S. Miller 
727f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
728f11e6659SDavid S. Miller 	if (!rt0)
729f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
730f11e6659SDavid S. Miller 
731afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
732afc154e9SHannes Frederic Sowa 			     &do_rr);
733f11e6659SDavid S. Miller 
734afc154e9SHannes Frederic Sowa 	if (do_rr) {
735d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
736f11e6659SDavid S. Miller 
737554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
738f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
739f11e6659SDavid S. Miller 			next = fn->leaf;
740f11e6659SDavid S. Miller 
741f11e6659SDavid S. Miller 		if (next != rt0)
742f11e6659SDavid S. Miller 			fn->rr_ptr = next;
743554cfb7eSYOSHIFUJI Hideaki 	}
744554cfb7eSYOSHIFUJI Hideaki 
745d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
746a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds 
7498b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7508b9df265SMartin KaFai Lau {
7518b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7528b9df265SMartin KaFai Lau }
7538b9df265SMartin KaFai Lau 
75470ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
75570ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
756b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
75770ceb4f5SYOSHIFUJI Hideaki {
758c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
75970ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
76070ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
76170ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7624bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
76370ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
76470ceb4f5SYOSHIFUJI Hideaki 
76570ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
76670ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
76770ceb4f5SYOSHIFUJI Hideaki 	}
76870ceb4f5SYOSHIFUJI Hideaki 
76970ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
77070ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
77170ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
77270ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
77370ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
77470ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
77570ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
77670ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
77770ceb4f5SYOSHIFUJI Hideaki 		}
77870ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
77970ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
78070ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
78170ceb4f5SYOSHIFUJI Hideaki 		}
78270ceb4f5SYOSHIFUJI Hideaki 	}
78370ceb4f5SYOSHIFUJI Hideaki 
78470ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
78570ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
7863933fc95SJens Rosenboom 		return -EINVAL;
78770ceb4f5SYOSHIFUJI Hideaki 
7884bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
78970ceb4f5SYOSHIFUJI Hideaki 
79070ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
79170ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
79270ceb4f5SYOSHIFUJI Hideaki 	else {
79370ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
79470ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
79570ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
79670ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
79770ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
79870ceb4f5SYOSHIFUJI Hideaki 	}
79970ceb4f5SYOSHIFUJI Hideaki 
800f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
801f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
802f104a567SDuan Jiong 	else
803f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
804f104a567SDuan Jiong 					gwaddr, dev->ifindex);
80570ceb4f5SYOSHIFUJI Hideaki 
80670ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
807e0a1ad73SThomas Graf 		ip6_del_rt(rt);
80870ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
80970ceb4f5SYOSHIFUJI Hideaki 	}
81070ceb4f5SYOSHIFUJI Hideaki 
81170ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
812efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
81370ceb4f5SYOSHIFUJI Hideaki 					pref);
81470ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
81570ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
81670ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
81770ceb4f5SYOSHIFUJI Hideaki 
81870ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8191716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8201716a961SGao feng 			rt6_clean_expires(rt);
8211716a961SGao feng 		else
8221716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8231716a961SGao feng 
82494e187c0SAmerigo Wang 		ip6_rt_put(rt);
82570ceb4f5SYOSHIFUJI Hideaki 	}
82670ceb4f5SYOSHIFUJI Hideaki 	return 0;
82770ceb4f5SYOSHIFUJI Hideaki }
82870ceb4f5SYOSHIFUJI Hideaki #endif
82970ceb4f5SYOSHIFUJI Hideaki 
830a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
831a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
832a3c00e46SMartin KaFai Lau {
833a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
834a3c00e46SMartin KaFai Lau 	while (1) {
835a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
836a3c00e46SMartin KaFai Lau 			return NULL;
837a3c00e46SMartin KaFai Lau 		pn = fn->parent;
838a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
839a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
840a3c00e46SMartin KaFai Lau 		else
841a3c00e46SMartin KaFai Lau 			fn = pn;
842a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
843a3c00e46SMartin KaFai Lau 			return fn;
844a3c00e46SMartin KaFai Lau 	}
845a3c00e46SMartin KaFai Lau }
846c71099acSThomas Graf 
8478ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8488ed67789SDaniel Lezcano 					     struct fib6_table *table,
8494c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	struct fib6_node *fn;
8521da177e4SLinus Torvalds 	struct rt6_info *rt;
8531da177e4SLinus Torvalds 
854c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8554c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
856c71099acSThomas Graf restart:
857c71099acSThomas Graf 	rt = fn->leaf;
8584c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
85951ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
86052bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
861a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
862a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
863a3c00e46SMartin KaFai Lau 		if (fn)
864a3c00e46SMartin KaFai Lau 			goto restart;
865a3c00e46SMartin KaFai Lau 	}
866d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
867c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8681da177e4SLinus Torvalds 	return rt;
869c71099acSThomas Graf 
870c71099acSThomas Graf }
871c71099acSThomas Graf 
872ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
873ea6e574eSFlorian Westphal 				    int flags)
874ea6e574eSFlorian Westphal {
875ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
876ea6e574eSFlorian Westphal }
877ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
878ea6e574eSFlorian Westphal 
8799acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8809acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
881c71099acSThomas Graf {
8824c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8834c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8844c9483b2SDavid S. Miller 		.daddr = *daddr,
885c71099acSThomas Graf 	};
886c71099acSThomas Graf 	struct dst_entry *dst;
88777d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
888c71099acSThomas Graf 
889adaa70bbSThomas Graf 	if (saddr) {
8904c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
891adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
892adaa70bbSThomas Graf 	}
893adaa70bbSThomas Graf 
8944c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
895c71099acSThomas Graf 	if (dst->error == 0)
896c71099acSThomas Graf 		return (struct rt6_info *) dst;
897c71099acSThomas Graf 
898c71099acSThomas Graf 	dst_release(dst);
899c71099acSThomas Graf 
9001da177e4SLinus Torvalds 	return NULL;
9011da177e4SLinus Torvalds }
9027159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
9037159039aSYOSHIFUJI Hideaki 
904c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
9051da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
9061da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
9071da177e4SLinus Torvalds    be destroyed.
9081da177e4SLinus Torvalds  */
9091da177e4SLinus Torvalds 
910e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
911e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9121da177e4SLinus Torvalds {
9131da177e4SLinus Torvalds 	int err;
914c71099acSThomas Graf 	struct fib6_table *table;
9151da177e4SLinus Torvalds 
916c71099acSThomas Graf 	table = rt->rt6i_table;
917c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
918e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
919c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	return err;
9221da177e4SLinus Torvalds }
9231da177e4SLinus Torvalds 
92440e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
92540e22e8fSThomas Graf {
926e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
927e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
928e715b6d3SFlorian Westphal 
929e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
93040e22e8fSThomas Graf }
93140e22e8fSThomas Graf 
9328b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
93321efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
934b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9351da177e4SLinus Torvalds {
9361da177e4SLinus Torvalds 	struct rt6_info *rt;
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 	/*
9391da177e4SLinus Torvalds 	 *	Clone the route.
9401da177e4SLinus Torvalds 	 */
9411da177e4SLinus Torvalds 
942d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
94383a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9441da177e4SLinus Torvalds 
945ad706862SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0);
94683a09abdSMartin KaFai Lau 
94783a09abdSMartin KaFai Lau 	if (!rt)
94883a09abdSMartin KaFai Lau 		return NULL;
94983a09abdSMartin KaFai Lau 
95083a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9518b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
95283a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
95383a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
95483a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
95583a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9568b9df265SMartin KaFai Lau 
9578b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
958bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
95921efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
96058c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9611da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9621da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9634e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9641da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9651da177e4SLinus Torvalds 		}
9661da177e4SLinus Torvalds #endif
96795a9a5baSYOSHIFUJI Hideaki 	}
96895a9a5baSYOSHIFUJI Hideaki 
969299d9939SYOSHIFUJI Hideaki 	return rt;
970299d9939SYOSHIFUJI Hideaki }
971299d9939SYOSHIFUJI Hideaki 
972d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
973d52d3997SMartin KaFai Lau {
974d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
975d52d3997SMartin KaFai Lau 
976d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
977ad706862SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags);
978d52d3997SMartin KaFai Lau 
979d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
980d52d3997SMartin KaFai Lau 		return NULL;
981d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
982d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
983d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
984d52d3997SMartin KaFai Lau 	return pcpu_rt;
985d52d3997SMartin KaFai Lau }
986d52d3997SMartin KaFai Lau 
987d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
988d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
989d52d3997SMartin KaFai Lau {
990a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, **p;
991d52d3997SMartin KaFai Lau 
992d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
993d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
994d52d3997SMartin KaFai Lau 
995a73e4195SMartin KaFai Lau 	if (pcpu_rt) {
996a73e4195SMartin KaFai Lau 		dst_hold(&pcpu_rt->dst);
997a73e4195SMartin KaFai Lau 		rt6_dst_from_metrics_check(pcpu_rt);
998a73e4195SMartin KaFai Lau 	}
999a73e4195SMartin KaFai Lau 	return pcpu_rt;
1000a73e4195SMartin KaFai Lau }
1001a73e4195SMartin KaFai Lau 
1002a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
1003a73e4195SMartin KaFai Lau {
10049c7370a1SMartin KaFai Lau 	struct fib6_table *table = rt->rt6i_table;
1005a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
1006d52d3997SMartin KaFai Lau 
1007d52d3997SMartin KaFai Lau 	pcpu_rt = ip6_rt_pcpu_alloc(rt);
1008d52d3997SMartin KaFai Lau 	if (!pcpu_rt) {
1009d52d3997SMartin KaFai Lau 		struct net *net = dev_net(rt->dst.dev);
1010d52d3997SMartin KaFai Lau 
10119c7370a1SMartin KaFai Lau 		dst_hold(&net->ipv6.ip6_null_entry->dst);
10129c7370a1SMartin KaFai Lau 		return net->ipv6.ip6_null_entry;
1013d52d3997SMartin KaFai Lau 	}
1014d52d3997SMartin KaFai Lau 
10159c7370a1SMartin KaFai Lau 	read_lock_bh(&table->tb6_lock);
10169c7370a1SMartin KaFai Lau 	if (rt->rt6i_pcpu) {
1017a73e4195SMartin KaFai Lau 		p = this_cpu_ptr(rt->rt6i_pcpu);
1018d52d3997SMartin KaFai Lau 		prev = cmpxchg(p, NULL, pcpu_rt);
1019d52d3997SMartin KaFai Lau 		if (prev) {
1020d52d3997SMartin KaFai Lau 			/* If someone did it before us, return prev instead */
1021d52d3997SMartin KaFai Lau 			dst_destroy(&pcpu_rt->dst);
1022d52d3997SMartin KaFai Lau 			pcpu_rt = prev;
1023d52d3997SMartin KaFai Lau 		}
10249c7370a1SMartin KaFai Lau 	} else {
10259c7370a1SMartin KaFai Lau 		/* rt has been removed from the fib6 tree
10269c7370a1SMartin KaFai Lau 		 * before we have a chance to acquire the read_lock.
10279c7370a1SMartin KaFai Lau 		 * In this case, don't brother to create a pcpu rt
10289c7370a1SMartin KaFai Lau 		 * since rt is going away anyway.  The next
10299c7370a1SMartin KaFai Lau 		 * dst_check() will trigger a re-lookup.
10309c7370a1SMartin KaFai Lau 		 */
10319c7370a1SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
10329c7370a1SMartin KaFai Lau 		pcpu_rt = rt;
10339c7370a1SMartin KaFai Lau 	}
1034d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1035d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
10369c7370a1SMartin KaFai Lau 	read_unlock_bh(&table->tb6_lock);
1037d52d3997SMartin KaFai Lau 	return pcpu_rt;
1038d52d3997SMartin KaFai Lau }
1039d52d3997SMartin KaFai Lau 
10408ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
10414c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
10421da177e4SLinus Torvalds {
1043367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
104445e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1045c71099acSThomas Graf 	int strict = 0;
10461da177e4SLinus Torvalds 
104777d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1048367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1049367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10501da177e4SLinus Torvalds 
1051c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10521da177e4SLinus Torvalds 
10534c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1054367efcb9SMartin KaFai Lau 	saved_fn = fn;
10551da177e4SLinus Torvalds 
1056ca254490SDavid Ahern 	if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1057ca254490SDavid Ahern 		oif = 0;
1058ca254490SDavid Ahern 
1059a3c00e46SMartin KaFai Lau redo_rt6_select:
1060367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
106152bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1062367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1063a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1064a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1065a3c00e46SMartin KaFai Lau 		if (fn)
1066a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1067367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1068367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1069367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1070367efcb9SMartin KaFai Lau 			fn = saved_fn;
1071367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1072367efcb9SMartin KaFai Lau 		}
1073a3c00e46SMartin KaFai Lau 	}
1074a3c00e46SMartin KaFai Lau 
1075d52d3997SMartin KaFai Lau 
1076d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10773da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1078c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10791da177e4SLinus Torvalds 
1080d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1081d52d3997SMartin KaFai Lau 		return rt;
10823da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10833da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10843da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10853da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10863da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10873da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
10883da59bd9SMartin KaFai Lau 		 */
1089c71099acSThomas Graf 
10903da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
10913da59bd9SMartin KaFai Lau 
1092d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1093d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1094d52d3997SMartin KaFai Lau 
10953da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
10963da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
10973da59bd9SMartin KaFai Lau 
10983da59bd9SMartin KaFai Lau 		if (uncached_rt)
10998d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
11003da59bd9SMartin KaFai Lau 		else
11013da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1102d52d3997SMartin KaFai Lau 
11033da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
11043da59bd9SMartin KaFai Lau 		return uncached_rt;
11053da59bd9SMartin KaFai Lau 
1106d52d3997SMartin KaFai Lau 	} else {
1107d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1108d52d3997SMartin KaFai Lau 
1109d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1110d52d3997SMartin KaFai Lau 
1111d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1112d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1113d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1114d52d3997SMartin KaFai Lau 
11159c7370a1SMartin KaFai Lau 		if (pcpu_rt) {
1116d52d3997SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11179c7370a1SMartin KaFai Lau 		} else {
11189c7370a1SMartin KaFai Lau 			/* We have to do the read_unlock first
11199c7370a1SMartin KaFai Lau 			 * because rt6_make_pcpu_route() may trigger
11209c7370a1SMartin KaFai Lau 			 * ip6_dst_gc() which will take the write_lock.
11219c7370a1SMartin KaFai Lau 			 */
11229c7370a1SMartin KaFai Lau 			dst_hold(&rt->dst);
11239c7370a1SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11249c7370a1SMartin KaFai Lau 			pcpu_rt = rt6_make_pcpu_route(rt);
11259c7370a1SMartin KaFai Lau 			dst_release(&rt->dst);
11269c7370a1SMartin KaFai Lau 		}
1127d52d3997SMartin KaFai Lau 
1128d52d3997SMartin KaFai Lau 		return pcpu_rt;
11299c7370a1SMartin KaFai Lau 
1130d52d3997SMartin KaFai Lau 	}
1131c71099acSThomas Graf }
1132c71099acSThomas Graf 
11338ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11344c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11354acad72dSPavel Emelyanov {
11364c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11374acad72dSPavel Emelyanov }
11384acad72dSPavel Emelyanov 
113972331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
114072331bc0SShmulik Ladkani 						struct net_device *dev,
114172331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
114272331bc0SShmulik Ladkani {
114372331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
114472331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
114572331bc0SShmulik Ladkani 
114672331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
114772331bc0SShmulik Ladkani }
114872331bc0SShmulik Ladkani 
1149c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1150c71099acSThomas Graf {
1151b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1152c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1153adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1154904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
11554c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
1156ca254490SDavid Ahern 		.flowi6_iif = l3mdev_fib_oif(skb->dev),
11574c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11584c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11596502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11604c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11614c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1162c71099acSThomas Graf 	};
1163adaa70bbSThomas Graf 
1164904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
116546fa062aSJiri Benc 	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
1166904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
116706e9d040SJiri Benc 	skb_dst_drop(skb);
116872331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1169c71099acSThomas Graf }
1170c71099acSThomas Graf 
11718ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11724c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1173c71099acSThomas Graf {
11744c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1175c71099acSThomas Graf }
1176c71099acSThomas Graf 
11779c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
11784c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1179c71099acSThomas Graf {
1180ca254490SDavid Ahern 	struct dst_entry *dst;
1181c71099acSThomas Graf 	int flags = 0;
1182d46a9d67SDavid Ahern 	bool any_src;
1183c71099acSThomas Graf 
1184ca254490SDavid Ahern 	dst = l3mdev_rt6_dst_by_oif(net, fl6);
1185ca254490SDavid Ahern 	if (dst)
1186ca254490SDavid Ahern 		return dst;
1187ca254490SDavid Ahern 
11881fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
11894dc27d1cSDavid McCullough 
1190d46a9d67SDavid Ahern 	any_src = ipv6_addr_any(&fl6->saddr);
1191741a11d9SDavid Ahern 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
1192d46a9d67SDavid Ahern 	    (fl6->flowi6_oif && any_src))
119377d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1194c71099acSThomas Graf 
1195d46a9d67SDavid Ahern 	if (!any_src)
1196adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
11970c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
11980c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1199adaa70bbSThomas Graf 
12004c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
12011da177e4SLinus Torvalds }
12027159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
12031da177e4SLinus Torvalds 
12042774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
120514e50e57SDavid S. Miller {
12065c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
120714e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
120814e50e57SDavid S. Miller 
1209f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
121014e50e57SDavid S. Miller 	if (rt) {
12110a1f5962SMartin KaFai Lau 		rt6_info_init(rt);
12120a1f5962SMartin KaFai Lau 
1213d8d1f30bSChangli Gao 		new = &rt->dst;
121414e50e57SDavid S. Miller 		new->__use = 1;
1215352e512cSHerbert Xu 		new->input = dst_discard;
1216ede2059dSEric W. Biederman 		new->output = dst_discard_out;
121714e50e57SDavid S. Miller 
1218defb3519SDavid S. Miller 		dst_copy_metrics(new, &ort->dst);
121914e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
122014e50e57SDavid S. Miller 		if (rt->rt6i_idev)
122114e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
122214e50e57SDavid S. Miller 
12234e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
12240a1f5962SMartin KaFai Lau 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
122514e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
122614e50e57SDavid S. Miller 
122714e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
122814e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
122914e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
123014e50e57SDavid S. Miller #endif
123114e50e57SDavid S. Miller 
123214e50e57SDavid S. Miller 		dst_free(new);
123314e50e57SDavid S. Miller 	}
123414e50e57SDavid S. Miller 
123569ead7afSDavid S. Miller 	dst_release(dst_orig);
123669ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
123714e50e57SDavid S. Miller }
123814e50e57SDavid S. Miller 
12391da177e4SLinus Torvalds /*
12401da177e4SLinus Torvalds  *	Destination cache support functions
12411da177e4SLinus Torvalds  */
12421da177e4SLinus Torvalds 
12434b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12444b32b5adSMartin KaFai Lau {
12454b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12464b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12474b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12484b32b5adSMartin KaFai Lau }
12494b32b5adSMartin KaFai Lau 
12503da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12513da59bd9SMartin KaFai Lau {
12523da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12533da59bd9SMartin KaFai Lau 		return NULL;
12543da59bd9SMartin KaFai Lau 
12553da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12563da59bd9SMartin KaFai Lau 		return NULL;
12573da59bd9SMartin KaFai Lau 
12583da59bd9SMartin KaFai Lau 	return &rt->dst;
12593da59bd9SMartin KaFai Lau }
12603da59bd9SMartin KaFai Lau 
12613da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12623da59bd9SMartin KaFai Lau {
1263*5973fb1eSMartin KaFai Lau 	if (!__rt6_check_expired(rt) &&
1264*5973fb1eSMartin KaFai Lau 	    rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12653da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12663da59bd9SMartin KaFai Lau 		return &rt->dst;
12673da59bd9SMartin KaFai Lau 	else
12683da59bd9SMartin KaFai Lau 		return NULL;
12693da59bd9SMartin KaFai Lau }
12703da59bd9SMartin KaFai Lau 
12711da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12721da177e4SLinus Torvalds {
12731da177e4SLinus Torvalds 	struct rt6_info *rt;
12741da177e4SLinus Torvalds 
12751da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12761da177e4SLinus Torvalds 
12776f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12786f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12796f3118b5SNicolas Dichtel 	 * into this function always.
12806f3118b5SNicolas Dichtel 	 */
1281e3bc10bdSHannes Frederic Sowa 
12824b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
12834b32b5adSMartin KaFai Lau 
1284d52d3997SMartin KaFai Lau 	if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE))
12853da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
12863da59bd9SMartin KaFai Lau 	else
12873da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
12881da177e4SLinus Torvalds }
12891da177e4SLinus Torvalds 
12901da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
12911da177e4SLinus Torvalds {
12921da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 	if (rt) {
129554c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
129654c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1297e0a1ad73SThomas Graf 				ip6_del_rt(rt);
129854c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
12991da177e4SLinus Torvalds 			}
130054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
130154c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
130254c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
130354c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
130454c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
130554c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
13061da177e4SLinus Torvalds }
13071da177e4SLinus Torvalds 
13081da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
13091da177e4SLinus Torvalds {
13101da177e4SLinus Torvalds 	struct rt6_info *rt;
13111da177e4SLinus Torvalds 
13123ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
13131da177e4SLinus Torvalds 
1314adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
13151da177e4SLinus Torvalds 	if (rt) {
13161eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
13171eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
13188e3d5be7SMartin KaFai Lau 			ip6_del_rt(rt);
13191eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
13201da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
13211da177e4SLinus Torvalds 		}
13221da177e4SLinus Torvalds 	}
13231eb4f758SHannes Frederic Sowa }
13241da177e4SLinus Torvalds 
132545e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
132645e4fd26SMartin KaFai Lau {
132745e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
132845e4fd26SMartin KaFai Lau 
132945e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
133045e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
133145e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
133245e4fd26SMartin KaFai Lau }
133345e4fd26SMartin KaFai Lau 
13340d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
13350d3f6d29SMartin KaFai Lau {
13360d3f6d29SMartin KaFai Lau 	return !(rt->rt6i_flags & RTF_CACHE) &&
13370d3f6d29SMartin KaFai Lau 		(rt->rt6i_flags & RTF_PCPU || rt->rt6i_node);
13380d3f6d29SMartin KaFai Lau }
13390d3f6d29SMartin KaFai Lau 
134045e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
134145e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13421da177e4SLinus Torvalds {
13431da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13441da177e4SLinus Torvalds 
134545e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
134645e4fd26SMartin KaFai Lau 		return;
134745e4fd26SMartin KaFai Lau 
134881aded24SDavid S. Miller 	dst_confirm(dst);
134945e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
135045e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
135145e4fd26SMartin KaFai Lau 		return;
135281aded24SDavid S. Miller 
13530d3f6d29SMartin KaFai Lau 	if (!rt6_cache_allowed_for_pmtu(rt6)) {
135445e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
135545e4fd26SMartin KaFai Lau 	} else {
135645e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
135745e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13589d289715SHagen Paul Pfeifer 
135945e4fd26SMartin KaFai Lau 		if (iph) {
136045e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
136145e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
136245e4fd26SMartin KaFai Lau 		} else if (sk) {
136345e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
136445e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
136545e4fd26SMartin KaFai Lau 		} else {
136645e4fd26SMartin KaFai Lau 			return;
13671da177e4SLinus Torvalds 		}
136845e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
136945e4fd26SMartin KaFai Lau 		if (nrt6) {
137045e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
137145e4fd26SMartin KaFai Lau 
137245e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
137345e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
137445e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
137545e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
137645e4fd26SMartin KaFai Lau 			 */
137745e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
137845e4fd26SMartin KaFai Lau 		}
137945e4fd26SMartin KaFai Lau 	}
138045e4fd26SMartin KaFai Lau }
138145e4fd26SMartin KaFai Lau 
138245e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
138345e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
138445e4fd26SMartin KaFai Lau {
138545e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
13861da177e4SLinus Torvalds }
13871da177e4SLinus Torvalds 
138842ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
138942ae66c8SDavid S. Miller 		     int oif, u32 mark)
139081aded24SDavid S. Miller {
139181aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
139281aded24SDavid S. Miller 	struct dst_entry *dst;
139381aded24SDavid S. Miller 	struct flowi6 fl6;
139481aded24SDavid S. Miller 
139581aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
139681aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
13971b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
139881aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
139981aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
14006502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
140181aded24SDavid S. Miller 
140281aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
140381aded24SDavid S. Miller 	if (!dst->error)
140445e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
140581aded24SDavid S. Miller 	dst_release(dst);
140681aded24SDavid S. Miller }
140781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
140881aded24SDavid S. Miller 
140981aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
141081aded24SDavid S. Miller {
141181aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
141281aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
141381aded24SDavid S. Miller }
141481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
141581aded24SDavid S. Miller 
1416b55b76b2SDuan Jiong /* Handle redirects */
1417b55b76b2SDuan Jiong struct ip6rd_flowi {
1418b55b76b2SDuan Jiong 	struct flowi6 fl6;
1419b55b76b2SDuan Jiong 	struct in6_addr gateway;
1420b55b76b2SDuan Jiong };
1421b55b76b2SDuan Jiong 
1422b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1423b55b76b2SDuan Jiong 					     struct fib6_table *table,
1424b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1425b55b76b2SDuan Jiong 					     int flags)
1426b55b76b2SDuan Jiong {
1427b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1428b55b76b2SDuan Jiong 	struct rt6_info *rt;
1429b55b76b2SDuan Jiong 	struct fib6_node *fn;
1430b55b76b2SDuan Jiong 
1431b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1432b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1433b55b76b2SDuan Jiong 	 *
1434b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1435b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1436b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1437b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1438b55b76b2SDuan Jiong 	 * routes.
1439b55b76b2SDuan Jiong 	 */
1440b55b76b2SDuan Jiong 
1441b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1442b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1443b55b76b2SDuan Jiong restart:
1444b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1445b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1446b55b76b2SDuan Jiong 			continue;
1447b55b76b2SDuan Jiong 		if (rt->dst.error)
1448b55b76b2SDuan Jiong 			break;
1449b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1450b55b76b2SDuan Jiong 			continue;
1451b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1452b55b76b2SDuan Jiong 			continue;
1453b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1454b55b76b2SDuan Jiong 			continue;
1455b55b76b2SDuan Jiong 		break;
1456b55b76b2SDuan Jiong 	}
1457b55b76b2SDuan Jiong 
1458b55b76b2SDuan Jiong 	if (!rt)
1459b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1460b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1461b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1462b0a1ba59SMartin KaFai Lau 		goto out;
1463b0a1ba59SMartin KaFai Lau 	}
1464b0a1ba59SMartin KaFai Lau 
1465b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1466a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1467a3c00e46SMartin KaFai Lau 		if (fn)
1468a3c00e46SMartin KaFai Lau 			goto restart;
1469b55b76b2SDuan Jiong 	}
1470a3c00e46SMartin KaFai Lau 
1471b0a1ba59SMartin KaFai Lau out:
1472b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1473b55b76b2SDuan Jiong 
1474b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1475b55b76b2SDuan Jiong 
1476b55b76b2SDuan Jiong 	return rt;
1477b55b76b2SDuan Jiong };
1478b55b76b2SDuan Jiong 
1479b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1480b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1481b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1482b55b76b2SDuan Jiong {
1483b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1484b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1485b55b76b2SDuan Jiong 
1486b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1487b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1488b55b76b2SDuan Jiong 
1489b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1490b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1491b55b76b2SDuan Jiong }
1492b55b76b2SDuan Jiong 
14933a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
14943a5ad2eeSDavid S. Miller {
14953a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
14963a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
14973a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
14983a5ad2eeSDavid S. Miller 
14993a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1500e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
15013a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
15023a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
15033a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
15043a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
15056502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
15063a5ad2eeSDavid S. Miller 
1507b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
15086700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
15093a5ad2eeSDavid S. Miller 	dst_release(dst);
15103a5ad2eeSDavid S. Miller }
15113a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
15123a5ad2eeSDavid S. Miller 
1513c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1514c92a59ecSDuan Jiong 			    u32 mark)
1515c92a59ecSDuan Jiong {
1516c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1517c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1518c92a59ecSDuan Jiong 	struct dst_entry *dst;
1519c92a59ecSDuan Jiong 	struct flowi6 fl6;
1520c92a59ecSDuan Jiong 
1521c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1522e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1523c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1524c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1525c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1526c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1527c92a59ecSDuan Jiong 
1528b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1529c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1530c92a59ecSDuan Jiong 	dst_release(dst);
1531c92a59ecSDuan Jiong }
1532c92a59ecSDuan Jiong 
15333a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
15343a5ad2eeSDavid S. Miller {
15353a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
15363a5ad2eeSDavid S. Miller }
15373a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15383a5ad2eeSDavid S. Miller 
15390dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15401da177e4SLinus Torvalds {
15410dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15420dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15430dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15440dbaee3bSDavid S. Miller 
15451da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15461da177e4SLinus Torvalds 
15475578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15485578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15491da177e4SLinus Torvalds 
15501da177e4SLinus Torvalds 	/*
15511da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15521da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15531da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15541da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15551da177e4SLinus Torvalds 	 */
15561da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15571da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15581da177e4SLinus Torvalds 	return mtu;
15591da177e4SLinus Torvalds }
15601da177e4SLinus Torvalds 
1561ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1562d33e4553SDavid S. Miller {
15634b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
15644b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1565d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1566618f9bc7SSteffen Klassert 
1567618f9bc7SSteffen Klassert 	if (mtu)
156830f78d8eSEric Dumazet 		goto out;
1569618f9bc7SSteffen Klassert 
15704b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
15714b32b5adSMartin KaFai Lau 	if (mtu)
15724b32b5adSMartin KaFai Lau 		goto out;
15734b32b5adSMartin KaFai Lau 
1574618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1575d33e4553SDavid S. Miller 
1576d33e4553SDavid S. Miller 	rcu_read_lock();
1577d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1578d33e4553SDavid S. Miller 	if (idev)
1579d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1580d33e4553SDavid S. Miller 	rcu_read_unlock();
1581d33e4553SDavid S. Miller 
158230f78d8eSEric Dumazet out:
158330f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1584d33e4553SDavid S. Miller }
1585d33e4553SDavid S. Miller 
15863b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
15873b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
15885d0bbeebSThomas Graf 
15893b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
159087a11578SDavid S. Miller 				  struct flowi6 *fl6)
15911da177e4SLinus Torvalds {
159287a11578SDavid S. Miller 	struct dst_entry *dst;
15931da177e4SLinus Torvalds 	struct rt6_info *rt;
15941da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1595c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
15961da177e4SLinus Torvalds 
159738308473SDavid S. Miller 	if (unlikely(!idev))
1598122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
15991da177e4SLinus Torvalds 
1600ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, dev, 0);
160138308473SDavid S. Miller 	if (unlikely(!rt)) {
16021da177e4SLinus Torvalds 		in6_dev_put(idev);
160387a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
16041da177e4SLinus Torvalds 		goto out;
16051da177e4SLinus Torvalds 	}
16061da177e4SLinus Torvalds 
16078e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
16088e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1609d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1610550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
161187a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
16128e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
16138e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
161414edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
16151da177e4SLinus Torvalds 
16163b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1617d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1618d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
16193b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16201da177e4SLinus Torvalds 
16215578689aSDaniel Lezcano 	fib6_force_start_gc(net);
16221da177e4SLinus Torvalds 
162387a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
162487a11578SDavid S. Miller 
16251da177e4SLinus Torvalds out:
162687a11578SDavid S. Miller 	return dst;
16271da177e4SLinus Torvalds }
16281da177e4SLinus Torvalds 
16293d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
16301da177e4SLinus Torvalds {
1631e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
16323d0f24a7SStephen Hemminger 	int more = 0;
16331da177e4SLinus Torvalds 
16343b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16353b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16365d0bbeebSThomas Graf 
16371da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16381da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16391da177e4SLinus Torvalds 			*pprev = dst->next;
16401da177e4SLinus Torvalds 			dst_free(dst);
16411da177e4SLinus Torvalds 		} else {
16421da177e4SLinus Torvalds 			pprev = &dst->next;
16433d0f24a7SStephen Hemminger 			++more;
16441da177e4SLinus Torvalds 		}
16451da177e4SLinus Torvalds 	}
16461da177e4SLinus Torvalds 
16473b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16485d0bbeebSThomas Graf 
16493d0f24a7SStephen Hemminger 	return more;
16501da177e4SLinus Torvalds }
16511da177e4SLinus Torvalds 
16521e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16531e493d19SDavid S. Miller 			    void *arg)
16541e493d19SDavid S. Miller {
16551e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16561e493d19SDavid S. Miller 
16571e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
16581e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
16591e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
16601e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
16611e493d19SDavid S. Miller 		if (func(rt, arg)) {
16621e493d19SDavid S. Miller 			*pprev = dst->next;
16631e493d19SDavid S. Miller 			dst_free(dst);
16641e493d19SDavid S. Miller 		} else {
16651e493d19SDavid S. Miller 			pprev = &dst->next;
16661e493d19SDavid S. Miller 		}
16671e493d19SDavid S. Miller 	}
16681e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
16691e493d19SDavid S. Miller }
16701e493d19SDavid S. Miller 
1671569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
16721da177e4SLinus Torvalds {
167386393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
16747019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
16757019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
16767019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
16777019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
16787019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1679fc66f95cSEric Dumazet 	int entries;
16801da177e4SLinus Torvalds 
1681fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
168249a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1683fc66f95cSEric Dumazet 	    entries <= rt_max_size)
16841da177e4SLinus Torvalds 		goto out;
16851da177e4SLinus Torvalds 
16866891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
168714956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1688fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1689fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
16907019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
16911da177e4SLinus Torvalds out:
16927019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1693fc66f95cSEric Dumazet 	return entries > rt_max_size;
16941da177e4SLinus Torvalds }
16951da177e4SLinus Torvalds 
1696e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1697e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1698e715b6d3SFlorian Westphal {
1699c3a8d947SDaniel Borkmann 	bool ecn_ca = false;
1700e715b6d3SFlorian Westphal 	struct nlattr *nla;
1701e715b6d3SFlorian Westphal 	int remaining;
1702e715b6d3SFlorian Westphal 	u32 *mp;
1703e715b6d3SFlorian Westphal 
170463159f29SIan Morris 	if (!cfg->fc_mx)
1705e715b6d3SFlorian Westphal 		return 0;
1706e715b6d3SFlorian Westphal 
1707e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1708e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1709e715b6d3SFlorian Westphal 		return -ENOMEM;
1710e715b6d3SFlorian Westphal 
1711e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1712e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1713ea697639SDaniel Borkmann 		u32 val;
1714ea697639SDaniel Borkmann 
17151bb14807SDaniel Borkmann 		if (!type)
17161bb14807SDaniel Borkmann 			continue;
1717e715b6d3SFlorian Westphal 		if (unlikely(type > RTAX_MAX))
1718e715b6d3SFlorian Westphal 			goto err;
17191bb14807SDaniel Borkmann 
1720ea697639SDaniel Borkmann 		if (type == RTAX_CC_ALGO) {
1721ea697639SDaniel Borkmann 			char tmp[TCP_CA_NAME_MAX];
1722e715b6d3SFlorian Westphal 
1723ea697639SDaniel Borkmann 			nla_strlcpy(tmp, nla, sizeof(tmp));
1724c3a8d947SDaniel Borkmann 			val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
1725ea697639SDaniel Borkmann 			if (val == TCP_CA_UNSPEC)
1726ea697639SDaniel Borkmann 				goto err;
1727ea697639SDaniel Borkmann 		} else {
1728ea697639SDaniel Borkmann 			val = nla_get_u32(nla);
1729ea697639SDaniel Borkmann 		}
1730b8d3e416SDaniel Borkmann 		if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
1731b8d3e416SDaniel Borkmann 			goto err;
1732ea697639SDaniel Borkmann 
1733ea697639SDaniel Borkmann 		mp[type - 1] = val;
1734e715b6d3SFlorian Westphal 		__set_bit(type - 1, mxc->mx_valid);
1735e715b6d3SFlorian Westphal 	}
1736e715b6d3SFlorian Westphal 
1737c3a8d947SDaniel Borkmann 	if (ecn_ca) {
1738c3a8d947SDaniel Borkmann 		__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
1739c3a8d947SDaniel Borkmann 		mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
1740c3a8d947SDaniel Borkmann 	}
1741e715b6d3SFlorian Westphal 
1742c3a8d947SDaniel Borkmann 	mxc->mx = mp;
1743e715b6d3SFlorian Westphal 	return 0;
1744e715b6d3SFlorian Westphal  err:
1745e715b6d3SFlorian Westphal 	kfree(mp);
1746e715b6d3SFlorian Westphal 	return -EINVAL;
1747e715b6d3SFlorian Westphal }
17481da177e4SLinus Torvalds 
17498c5b83f0SRoopa Prabhu static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
17501da177e4SLinus Torvalds {
17515578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
17521da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
17531da177e4SLinus Torvalds 	struct net_device *dev = NULL;
17541da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1755c71099acSThomas Graf 	struct fib6_table *table;
17561da177e4SLinus Torvalds 	int addr_type;
17578c5b83f0SRoopa Prabhu 	int err = -EINVAL;
17581da177e4SLinus Torvalds 
175986872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
17608c5b83f0SRoopa Prabhu 		goto out;
17611da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
176286872cb5SThomas Graf 	if (cfg->fc_src_len)
17638c5b83f0SRoopa Prabhu 		goto out;
17641da177e4SLinus Torvalds #endif
176586872cb5SThomas Graf 	if (cfg->fc_ifindex) {
17661da177e4SLinus Torvalds 		err = -ENODEV;
17675578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
17681da177e4SLinus Torvalds 		if (!dev)
17691da177e4SLinus Torvalds 			goto out;
17701da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
17711da177e4SLinus Torvalds 		if (!idev)
17721da177e4SLinus Torvalds 			goto out;
17731da177e4SLinus Torvalds 	}
17741da177e4SLinus Torvalds 
177586872cb5SThomas Graf 	if (cfg->fc_metric == 0)
177686872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
17771da177e4SLinus Torvalds 
1778c71099acSThomas Graf 	err = -ENOBUFS;
177938308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1780d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1781d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
178238308473SDavid S. Miller 		if (!table) {
1783f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1784d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1785d71314b4SMatti Vaittinen 		}
1786d71314b4SMatti Vaittinen 	} else {
1787d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1788d71314b4SMatti Vaittinen 	}
178938308473SDavid S. Miller 
179038308473SDavid S. Miller 	if (!table)
1791c71099acSThomas Graf 		goto out;
1792c71099acSThomas Graf 
1793ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, NULL,
1794ad706862SMartin KaFai Lau 			   (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT);
17951da177e4SLinus Torvalds 
179638308473SDavid S. Miller 	if (!rt) {
17971da177e4SLinus Torvalds 		err = -ENOMEM;
17981da177e4SLinus Torvalds 		goto out;
17991da177e4SLinus Torvalds 	}
18001da177e4SLinus Torvalds 
18011716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
18021716a961SGao feng 		rt6_set_expires(rt, jiffies +
18031716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
18041716a961SGao feng 	else
18051716a961SGao feng 		rt6_clean_expires(rt);
18061da177e4SLinus Torvalds 
180786872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
180886872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
180986872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
181086872cb5SThomas Graf 
181186872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
18121da177e4SLinus Torvalds 
18131da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1814d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1815ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1816ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
18171da177e4SLinus Torvalds 	else
1818d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
18191da177e4SLinus Torvalds 
1820d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
18211da177e4SLinus Torvalds 
182219e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
182319e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
182419e42e45SRoopa Prabhu 
182519e42e45SRoopa Prabhu 		err = lwtunnel_build_state(dev, cfg->fc_encap_type,
1826127eb7cdSTom Herbert 					   cfg->fc_encap, AF_INET6, cfg,
1827127eb7cdSTom Herbert 					   &lwtstate);
182819e42e45SRoopa Prabhu 		if (err)
182919e42e45SRoopa Prabhu 			goto out;
183061adedf3SJiri Benc 		rt->dst.lwtstate = lwtstate_get(lwtstate);
183161adedf3SJiri Benc 		if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
183261adedf3SJiri Benc 			rt->dst.lwtstate->orig_output = rt->dst.output;
183361adedf3SJiri Benc 			rt->dst.output = lwtunnel_output;
183419e42e45SRoopa Prabhu 		}
183561adedf3SJiri Benc 		if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
183661adedf3SJiri Benc 			rt->dst.lwtstate->orig_input = rt->dst.input;
183761adedf3SJiri Benc 			rt->dst.input = lwtunnel_input;
183825368623STom Herbert 		}
183925368623STom Herbert 	}
184019e42e45SRoopa Prabhu 
184186872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
184286872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1843afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
184411d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
18451da177e4SLinus Torvalds 
18461da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
184786872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
184886872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
18491da177e4SLinus Torvalds #endif
18501da177e4SLinus Torvalds 
185186872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
18521da177e4SLinus Torvalds 
18531da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
18541da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
18551da177e4SLinus Torvalds 	 */
185686872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
185738308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
185838308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
185938308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
18601da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
18615578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
18621da177e4SLinus Torvalds 			if (dev) {
18631da177e4SLinus Torvalds 				dev_put(dev);
18641da177e4SLinus Torvalds 				in6_dev_put(idev);
18651da177e4SLinus Torvalds 			}
18665578689aSDaniel Lezcano 			dev = net->loopback_dev;
18671da177e4SLinus Torvalds 			dev_hold(dev);
18681da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
18691da177e4SLinus Torvalds 			if (!idev) {
18701da177e4SLinus Torvalds 				err = -ENODEV;
18711da177e4SLinus Torvalds 				goto out;
18721da177e4SLinus Torvalds 			}
18731da177e4SLinus Torvalds 		}
18741da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1875ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1876ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1877ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1878ede2059dSEric W. Biederman 			rt->dst.output = dst_discard_out;
18797150aedeSKamala R 			rt->dst.input = dst_discard;
1880ef2c7d7bSNicolas Dichtel 			break;
1881ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1882ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
18837150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
18847150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1885ef2c7d7bSNicolas Dichtel 			break;
1886b4949ab2SNicolas Dichtel 		case RTN_THROW:
18870315e382SNikola Forró 		case RTN_UNREACHABLE:
1888ef2c7d7bSNicolas Dichtel 		default:
18897150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
18900315e382SNikola Forró 					: (cfg->fc_type == RTN_UNREACHABLE)
18910315e382SNikola Forró 					? -EHOSTUNREACH : -ENETUNREACH;
18927150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
18937150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1894ef2c7d7bSNicolas Dichtel 			break;
1895ef2c7d7bSNicolas Dichtel 		}
18961da177e4SLinus Torvalds 		goto install_route;
18971da177e4SLinus Torvalds 	}
18981da177e4SLinus Torvalds 
189986872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1900b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
19011da177e4SLinus Torvalds 		int gwa_type;
19021da177e4SLinus Torvalds 
190386872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1904330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
190548ed7b26SFlorian Westphal 
190648ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
190748ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
190848ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
190948ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
191048ed7b26SFlorian Westphal 		 */
191148ed7b26SFlorian Westphal 		err = -EINVAL;
1912330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1913330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1914330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
191548ed7b26SFlorian Westphal 			goto out;
191648ed7b26SFlorian Westphal 
19174e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
19201da177e4SLinus Torvalds 			struct rt6_info *grt;
19211da177e4SLinus Torvalds 
19221da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
19231da177e4SLinus Torvalds 			   addresses as nexthop address.
19241da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
19251da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
19261da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
19271da177e4SLinus Torvalds 			   some exceptions. --ANK
19281da177e4SLinus Torvalds 			 */
19291da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
19301da177e4SLinus Torvalds 				goto out;
19311da177e4SLinus Torvalds 
19325578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
19331da177e4SLinus Torvalds 
19341da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
193538308473SDavid S. Miller 			if (!grt)
19361da177e4SLinus Torvalds 				goto out;
19371da177e4SLinus Torvalds 			if (dev) {
1938d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
193994e187c0SAmerigo Wang 					ip6_rt_put(grt);
19401da177e4SLinus Torvalds 					goto out;
19411da177e4SLinus Torvalds 				}
19421da177e4SLinus Torvalds 			} else {
1943d1918542SDavid S. Miller 				dev = grt->dst.dev;
19441da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
19451da177e4SLinus Torvalds 				dev_hold(dev);
19461da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
19471da177e4SLinus Torvalds 			}
19481da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
19491da177e4SLinus Torvalds 				err = 0;
195094e187c0SAmerigo Wang 			ip6_rt_put(grt);
19511da177e4SLinus Torvalds 
19521da177e4SLinus Torvalds 			if (err)
19531da177e4SLinus Torvalds 				goto out;
19541da177e4SLinus Torvalds 		}
19551da177e4SLinus Torvalds 		err = -EINVAL;
195638308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
19571da177e4SLinus Torvalds 			goto out;
19581da177e4SLinus Torvalds 	}
19591da177e4SLinus Torvalds 
19601da177e4SLinus Torvalds 	err = -ENODEV;
196138308473SDavid S. Miller 	if (!dev)
19621da177e4SLinus Torvalds 		goto out;
19631da177e4SLinus Torvalds 
1964c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1965c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1966c3968a85SDaniel Walter 			err = -EINVAL;
1967c3968a85SDaniel Walter 			goto out;
1968c3968a85SDaniel Walter 		}
19694e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1970c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1971c3968a85SDaniel Walter 	} else
1972c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1973c3968a85SDaniel Walter 
197486872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds install_route:
1977d8d1f30bSChangli Gao 	rt->dst.dev = dev;
19781da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1979c71099acSThomas Graf 	rt->rt6i_table = table;
198063152fc0SDaniel Lezcano 
1981c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
198263152fc0SDaniel Lezcano 
19838c5b83f0SRoopa Prabhu 	return rt;
19841da177e4SLinus Torvalds out:
19851da177e4SLinus Torvalds 	if (dev)
19861da177e4SLinus Torvalds 		dev_put(dev);
19871da177e4SLinus Torvalds 	if (idev)
19881da177e4SLinus Torvalds 		in6_dev_put(idev);
19891da177e4SLinus Torvalds 	if (rt)
1990d8d1f30bSChangli Gao 		dst_free(&rt->dst);
19916b9ea5a6SRoopa Prabhu 
19928c5b83f0SRoopa Prabhu 	return ERR_PTR(err);
19936b9ea5a6SRoopa Prabhu }
19946b9ea5a6SRoopa Prabhu 
19956b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg)
19966b9ea5a6SRoopa Prabhu {
19976b9ea5a6SRoopa Prabhu 	struct mx6_config mxc = { .mx = NULL, };
19988c5b83f0SRoopa Prabhu 	struct rt6_info *rt;
19996b9ea5a6SRoopa Prabhu 	int err;
20006b9ea5a6SRoopa Prabhu 
20018c5b83f0SRoopa Prabhu 	rt = ip6_route_info_create(cfg);
20028c5b83f0SRoopa Prabhu 	if (IS_ERR(rt)) {
20038c5b83f0SRoopa Prabhu 		err = PTR_ERR(rt);
20048c5b83f0SRoopa Prabhu 		rt = NULL;
20056b9ea5a6SRoopa Prabhu 		goto out;
20068c5b83f0SRoopa Prabhu 	}
20076b9ea5a6SRoopa Prabhu 
20086b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&mxc, cfg);
20096b9ea5a6SRoopa Prabhu 	if (err)
20106b9ea5a6SRoopa Prabhu 		goto out;
20116b9ea5a6SRoopa Prabhu 
20126b9ea5a6SRoopa Prabhu 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
20136b9ea5a6SRoopa Prabhu 
20146b9ea5a6SRoopa Prabhu 	kfree(mxc.mx);
20156b9ea5a6SRoopa Prabhu 
20166b9ea5a6SRoopa Prabhu 	return err;
20176b9ea5a6SRoopa Prabhu out:
20186b9ea5a6SRoopa Prabhu 	if (rt)
20196b9ea5a6SRoopa Prabhu 		dst_free(&rt->dst);
20206b9ea5a6SRoopa Prabhu 
20211da177e4SLinus Torvalds 	return err;
20221da177e4SLinus Torvalds }
20231da177e4SLinus Torvalds 
202486872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
20251da177e4SLinus Torvalds {
20261da177e4SLinus Torvalds 	int err;
2027c71099acSThomas Graf 	struct fib6_table *table;
2028d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
20291da177e4SLinus Torvalds 
20308e3d5be7SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry ||
20318e3d5be7SMartin KaFai Lau 	    rt->dst.flags & DST_NOCACHE) {
20326825a26cSGao feng 		err = -ENOENT;
20336825a26cSGao feng 		goto out;
20346825a26cSGao feng 	}
20356c813a72SPatrick McHardy 
2036c71099acSThomas Graf 	table = rt->rt6i_table;
2037c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
203886872cb5SThomas Graf 	err = fib6_del(rt, info);
2039c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
20401da177e4SLinus Torvalds 
20416825a26cSGao feng out:
204294e187c0SAmerigo Wang 	ip6_rt_put(rt);
20431da177e4SLinus Torvalds 	return err;
20441da177e4SLinus Torvalds }
20451da177e4SLinus Torvalds 
2046e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
2047e0a1ad73SThomas Graf {
20484d1169c1SDenis V. Lunev 	struct nl_info info = {
2049d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
20504d1169c1SDenis V. Lunev 	};
2051528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
2052e0a1ad73SThomas Graf }
2053e0a1ad73SThomas Graf 
205486872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
20551da177e4SLinus Torvalds {
2056c71099acSThomas Graf 	struct fib6_table *table;
20571da177e4SLinus Torvalds 	struct fib6_node *fn;
20581da177e4SLinus Torvalds 	struct rt6_info *rt;
20591da177e4SLinus Torvalds 	int err = -ESRCH;
20601da177e4SLinus Torvalds 
20615578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
206238308473SDavid S. Miller 	if (!table)
2063c71099acSThomas Graf 		return err;
20641da177e4SLinus Torvalds 
2065c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2066c71099acSThomas Graf 
2067c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
206886872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
206986872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
20701da177e4SLinus Torvalds 
20711da177e4SLinus Torvalds 	if (fn) {
2072d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
20731f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
20741f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
20751f56a01fSMartin KaFai Lau 				continue;
207686872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2077d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2078d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
20791da177e4SLinus Torvalds 				continue;
208086872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
208186872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
20821da177e4SLinus Torvalds 				continue;
208386872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
20841da177e4SLinus Torvalds 				continue;
2085d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2086c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
20871da177e4SLinus Torvalds 
208886872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
20891da177e4SLinus Torvalds 		}
20901da177e4SLinus Torvalds 	}
2091c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
20921da177e4SLinus Torvalds 
20931da177e4SLinus Torvalds 	return err;
20941da177e4SLinus Torvalds }
20951da177e4SLinus Torvalds 
20966700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2097a6279458SYOSHIFUJI Hideaki {
2098a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2099e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2100e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2101e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2102e8599ff4SDavid S. Miller 	struct neighbour *neigh;
210371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
21046e157b6aSDavid S. Miller 	int optlen, on_link;
21056e157b6aSDavid S. Miller 	u8 *lladdr;
2106e8599ff4SDavid S. Miller 
210729a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
210871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2109e8599ff4SDavid S. Miller 
2110e8599ff4SDavid S. Miller 	if (optlen < 0) {
21116e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2112e8599ff4SDavid S. Miller 		return;
2113e8599ff4SDavid S. Miller 	}
2114e8599ff4SDavid S. Miller 
211571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2116e8599ff4SDavid S. Miller 
211771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
21186e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2119e8599ff4SDavid S. Miller 		return;
2120e8599ff4SDavid S. Miller 	}
2121e8599ff4SDavid S. Miller 
21226e157b6aSDavid S. Miller 	on_link = 0;
212371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2124e8599ff4SDavid S. Miller 		on_link = 1;
212571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2126e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
21276e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2128e8599ff4SDavid S. Miller 		return;
2129e8599ff4SDavid S. Miller 	}
2130e8599ff4SDavid S. Miller 
2131e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2132e8599ff4SDavid S. Miller 	if (!in6_dev)
2133e8599ff4SDavid S. Miller 		return;
2134e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2135e8599ff4SDavid S. Miller 		return;
2136e8599ff4SDavid S. Miller 
2137e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2138e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2139e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2140e8599ff4SDavid S. Miller 	 */
2141e8599ff4SDavid S. Miller 
214271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
2143e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2144e8599ff4SDavid S. Miller 		return;
2145e8599ff4SDavid S. Miller 	}
21466e157b6aSDavid S. Miller 
21476e157b6aSDavid S. Miller 	lladdr = NULL;
2148e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2149e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2150e8599ff4SDavid S. Miller 					     skb->dev);
2151e8599ff4SDavid S. Miller 		if (!lladdr) {
2152e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2153e8599ff4SDavid S. Miller 			return;
2154e8599ff4SDavid S. Miller 		}
2155e8599ff4SDavid S. Miller 	}
2156e8599ff4SDavid S. Miller 
21576e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
2158ec13ad1dSMatthias Schiffer 	if (rt->rt6i_flags & RTF_REJECT) {
21596e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
21606e157b6aSDavid S. Miller 		return;
21616e157b6aSDavid S. Miller 	}
21626e157b6aSDavid S. Miller 
21636e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
21646e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
21656e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
21666e157b6aSDavid S. Miller 	 */
21676e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
21686e157b6aSDavid S. Miller 
216971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2170e8599ff4SDavid S. Miller 	if (!neigh)
2171e8599ff4SDavid S. Miller 		return;
2172e8599ff4SDavid S. Miller 
21731da177e4SLinus Torvalds 	/*
21741da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
21751da177e4SLinus Torvalds 	 */
21761da177e4SLinus Torvalds 
21771da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
21781da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
21791da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
21801da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
21811da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
21821da177e4SLinus Torvalds 		     );
21831da177e4SLinus Torvalds 
218483a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
218538308473SDavid S. Miller 	if (!nrt)
21861da177e4SLinus Torvalds 		goto out;
21871da177e4SLinus Torvalds 
21881da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
21891da177e4SLinus Torvalds 	if (on_link)
21901da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
21911da177e4SLinus Torvalds 
21924e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
21931da177e4SLinus Torvalds 
219440e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
21951da177e4SLinus Torvalds 		goto out;
21961da177e4SLinus Torvalds 
2197d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2198d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
219971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
220060592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
22018d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
22028d71740cSTom Tucker 
22031da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
22046e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2205e0a1ad73SThomas Graf 		ip6_del_rt(rt);
22061da177e4SLinus Torvalds 	}
22071da177e4SLinus Torvalds 
22081da177e4SLinus Torvalds out:
2209e8599ff4SDavid S. Miller 	neigh_release(neigh);
22106e157b6aSDavid S. Miller }
22116e157b6aSDavid S. Miller 
22121da177e4SLinus Torvalds /*
22131da177e4SLinus Torvalds  *	Misc support functions
22141da177e4SLinus Torvalds  */
22151da177e4SLinus Torvalds 
22164b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
22174b32b5adSMartin KaFai Lau {
22184b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
22194b32b5adSMartin KaFai Lau 
22204b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
22214b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
22224b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
22234b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
22244b32b5adSMartin KaFai Lau }
22254b32b5adSMartin KaFai Lau 
222683a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
22271da177e4SLinus Torvalds {
2228d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2229d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
223083a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2231d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
22321da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
22331da177e4SLinus Torvalds 	if (rt->rt6i_idev)
22341da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2235d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
22364e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
22371716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
22381716a961SGao feng 	rt6_set_from(rt, ort);
223983a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
22401da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
224183a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
22421da177e4SLinus Torvalds #endif
224383a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2244c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
224561adedf3SJiri Benc 	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
22461da177e4SLinus Torvalds }
22471da177e4SLinus Torvalds 
224870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2249efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2250b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2251b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
225270ceb4f5SYOSHIFUJI Hideaki {
225370ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
225470ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2255c71099acSThomas Graf 	struct fib6_table *table;
225670ceb4f5SYOSHIFUJI Hideaki 
2257efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
225838308473SDavid S. Miller 	if (!table)
2259c71099acSThomas Graf 		return NULL;
2260c71099acSThomas Graf 
22615744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2262c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
226370ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
226470ceb4f5SYOSHIFUJI Hideaki 		goto out;
226570ceb4f5SYOSHIFUJI Hideaki 
2266d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2267d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
226870ceb4f5SYOSHIFUJI Hideaki 			continue;
226970ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
227070ceb4f5SYOSHIFUJI Hideaki 			continue;
227170ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
227270ceb4f5SYOSHIFUJI Hideaki 			continue;
2273d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
227470ceb4f5SYOSHIFUJI Hideaki 		break;
227570ceb4f5SYOSHIFUJI Hideaki 	}
227670ceb4f5SYOSHIFUJI Hideaki out:
22775744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
227870ceb4f5SYOSHIFUJI Hideaki 	return rt;
227970ceb4f5SYOSHIFUJI Hideaki }
228070ceb4f5SYOSHIFUJI Hideaki 
2281efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2282b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2283b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
228495c96174SEric Dumazet 					   unsigned int pref)
228570ceb4f5SYOSHIFUJI Hideaki {
228686872cb5SThomas Graf 	struct fib6_config cfg = {
2287238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
228886872cb5SThomas Graf 		.fc_ifindex	= ifindex,
228986872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
229086872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
229186872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
229215e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2293efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2294efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
229586872cb5SThomas Graf 	};
229670ceb4f5SYOSHIFUJI Hideaki 
2297ca254490SDavid Ahern 	cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO;
22984e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
22994e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
230086872cb5SThomas Graf 
2301e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2302e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
230386872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
230470ceb4f5SYOSHIFUJI Hideaki 
230586872cb5SThomas Graf 	ip6_route_add(&cfg);
230670ceb4f5SYOSHIFUJI Hideaki 
2307efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
230870ceb4f5SYOSHIFUJI Hideaki }
230970ceb4f5SYOSHIFUJI Hideaki #endif
231070ceb4f5SYOSHIFUJI Hideaki 
2311b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
23121da177e4SLinus Torvalds {
23131da177e4SLinus Torvalds 	struct rt6_info *rt;
2314c71099acSThomas Graf 	struct fib6_table *table;
23151da177e4SLinus Torvalds 
2316c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
231738308473SDavid S. Miller 	if (!table)
2318c71099acSThomas Graf 		return NULL;
23191da177e4SLinus Torvalds 
23205744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2321d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2322d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2323045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
23241da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
23251da177e4SLinus Torvalds 			break;
23261da177e4SLinus Torvalds 	}
23271da177e4SLinus Torvalds 	if (rt)
2328d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
23295744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
23301da177e4SLinus Torvalds 	return rt;
23311da177e4SLinus Torvalds }
23321da177e4SLinus Torvalds 
2333b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2334ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2335ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
23361da177e4SLinus Torvalds {
233786872cb5SThomas Graf 	struct fib6_config cfg = {
2338ca254490SDavid Ahern 		.fc_table	= l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
2339238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
234086872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
234186872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
234286872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
234315e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
23445578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2345c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
234686872cb5SThomas Graf 	};
23471da177e4SLinus Torvalds 
23484e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
23491da177e4SLinus Torvalds 
235086872cb5SThomas Graf 	ip6_route_add(&cfg);
23511da177e4SLinus Torvalds 
23521da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
23531da177e4SLinus Torvalds }
23541da177e4SLinus Torvalds 
23557b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
23561da177e4SLinus Torvalds {
23571da177e4SLinus Torvalds 	struct rt6_info *rt;
2358c71099acSThomas Graf 	struct fib6_table *table;
2359c71099acSThomas Graf 
2360c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
23617b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
236238308473SDavid S. Miller 	if (!table)
2363c71099acSThomas Graf 		return;
23641da177e4SLinus Torvalds 
23651da177e4SLinus Torvalds restart:
2366c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2367d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
23683e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
23693e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2370d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2371c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2372e0a1ad73SThomas Graf 			ip6_del_rt(rt);
23731da177e4SLinus Torvalds 			goto restart;
23741da177e4SLinus Torvalds 		}
23751da177e4SLinus Torvalds 	}
2376c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
23771da177e4SLinus Torvalds }
23781da177e4SLinus Torvalds 
23795578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
23805578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
238186872cb5SThomas Graf 				 struct fib6_config *cfg)
238286872cb5SThomas Graf {
238386872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
238486872cb5SThomas Graf 
2385ca254490SDavid Ahern 	cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
2386ca254490SDavid Ahern 			 : RT6_TABLE_MAIN;
238786872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
238886872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
238986872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
239086872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
239186872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
239286872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
239386872cb5SThomas Graf 
23945578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2395f1243c2dSBenjamin Thery 
23964e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
23974e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
23984e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
239986872cb5SThomas Graf }
240086872cb5SThomas Graf 
24015578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
24021da177e4SLinus Torvalds {
240386872cb5SThomas Graf 	struct fib6_config cfg;
24041da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
24051da177e4SLinus Torvalds 	int err;
24061da177e4SLinus Torvalds 
24071da177e4SLinus Torvalds 	switch (cmd) {
24081da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
24091da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2410af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
24111da177e4SLinus Torvalds 			return -EPERM;
24121da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
24131da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
24141da177e4SLinus Torvalds 		if (err)
24151da177e4SLinus Torvalds 			return -EFAULT;
24161da177e4SLinus Torvalds 
24175578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
241886872cb5SThomas Graf 
24191da177e4SLinus Torvalds 		rtnl_lock();
24201da177e4SLinus Torvalds 		switch (cmd) {
24211da177e4SLinus Torvalds 		case SIOCADDRT:
242286872cb5SThomas Graf 			err = ip6_route_add(&cfg);
24231da177e4SLinus Torvalds 			break;
24241da177e4SLinus Torvalds 		case SIOCDELRT:
242586872cb5SThomas Graf 			err = ip6_route_del(&cfg);
24261da177e4SLinus Torvalds 			break;
24271da177e4SLinus Torvalds 		default:
24281da177e4SLinus Torvalds 			err = -EINVAL;
24291da177e4SLinus Torvalds 		}
24301da177e4SLinus Torvalds 		rtnl_unlock();
24311da177e4SLinus Torvalds 
24321da177e4SLinus Torvalds 		return err;
24333ff50b79SStephen Hemminger 	}
24341da177e4SLinus Torvalds 
24351da177e4SLinus Torvalds 	return -EINVAL;
24361da177e4SLinus Torvalds }
24371da177e4SLinus Torvalds 
24381da177e4SLinus Torvalds /*
24391da177e4SLinus Torvalds  *	Drop the packet on the floor
24401da177e4SLinus Torvalds  */
24411da177e4SLinus Torvalds 
2442d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
24431da177e4SLinus Torvalds {
2444612f09e8SYOSHIFUJI Hideaki 	int type;
2445adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2446612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2447612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
24480660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
244945bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
24503bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24513bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2452612f09e8SYOSHIFUJI Hideaki 			break;
2453612f09e8SYOSHIFUJI Hideaki 		}
2454612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2455612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
24563bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24573bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2458612f09e8SYOSHIFUJI Hideaki 		break;
2459612f09e8SYOSHIFUJI Hideaki 	}
24603ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
24611da177e4SLinus Torvalds 	kfree_skb(skb);
24621da177e4SLinus Torvalds 	return 0;
24631da177e4SLinus Torvalds }
24641da177e4SLinus Torvalds 
24659ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
24669ce8ade0SThomas Graf {
2467612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
24689ce8ade0SThomas Graf }
24699ce8ade0SThomas Graf 
2470ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
24711da177e4SLinus Torvalds {
2472adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2473612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
24741da177e4SLinus Torvalds }
24751da177e4SLinus Torvalds 
24769ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
24779ce8ade0SThomas Graf {
2478612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
24799ce8ade0SThomas Graf }
24809ce8ade0SThomas Graf 
2481ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
24829ce8ade0SThomas Graf {
2483adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2484612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
24859ce8ade0SThomas Graf }
24869ce8ade0SThomas Graf 
24871da177e4SLinus Torvalds /*
24881da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
24891da177e4SLinus Torvalds  */
24901da177e4SLinus Torvalds 
24911da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
24921da177e4SLinus Torvalds 				    const struct in6_addr *addr,
24938f031519SDavid S. Miller 				    bool anycast)
24941da177e4SLinus Torvalds {
2495ca254490SDavid Ahern 	u32 tb_id;
2496c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2497a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2498ad706862SMartin KaFai Lau 					    DST_NOCOUNT);
2499a3300ef4SHannes Frederic Sowa 	if (!rt)
25001da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
25011da177e4SLinus Torvalds 
25021da177e4SLinus Torvalds 	in6_dev_hold(idev);
25031da177e4SLinus Torvalds 
250411d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2505d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2506d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
25071da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
25081da177e4SLinus Torvalds 
25091da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
251058c4fb86SYOSHIFUJI Hideaki 	if (anycast)
251158c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
251258c4fb86SYOSHIFUJI Hideaki 	else
25131da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
25141da177e4SLinus Torvalds 
2515550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
25164e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
25171da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
2518ca254490SDavid Ahern 	tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
2519ca254490SDavid Ahern 	rt->rt6i_table = fib6_get_table(net, tb_id);
25208e3d5be7SMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
25211da177e4SLinus Torvalds 
2522d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
25231da177e4SLinus Torvalds 
25241da177e4SLinus Torvalds 	return rt;
25251da177e4SLinus Torvalds }
25261da177e4SLinus Torvalds 
2527c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2528c3968a85SDaniel Walter 			struct rt6_info *rt,
2529b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2530c3968a85SDaniel Walter 			unsigned int prefs,
2531c3968a85SDaniel Walter 			struct in6_addr *saddr)
2532c3968a85SDaniel Walter {
2533e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2534e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2535c3968a85SDaniel Walter 	int err = 0;
2536e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
25374e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2538c3968a85SDaniel Walter 	else
2539c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2540c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2541c3968a85SDaniel Walter 	return err;
2542c3968a85SDaniel Walter }
2543c3968a85SDaniel Walter 
2544c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2545c3968a85SDaniel Walter struct arg_dev_net_ip {
2546c3968a85SDaniel Walter 	struct net_device *dev;
2547c3968a85SDaniel Walter 	struct net *net;
2548c3968a85SDaniel Walter 	struct in6_addr *addr;
2549c3968a85SDaniel Walter };
2550c3968a85SDaniel Walter 
2551c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2552c3968a85SDaniel Walter {
2553c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2554c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2555c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2556c3968a85SDaniel Walter 
2557d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2558c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2559c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2560c3968a85SDaniel Walter 		/* remove prefsrc entry */
2561c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2562c3968a85SDaniel Walter 	}
2563c3968a85SDaniel Walter 	return 0;
2564c3968a85SDaniel Walter }
2565c3968a85SDaniel Walter 
2566c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2567c3968a85SDaniel Walter {
2568c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2569c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2570c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2571c3968a85SDaniel Walter 		.net = net,
2572c3968a85SDaniel Walter 		.addr = &ifp->addr,
2573c3968a85SDaniel Walter 	};
25740c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2575c3968a85SDaniel Walter }
2576c3968a85SDaniel Walter 
2577be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2578be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2579be7a010dSDuan Jiong 
2580be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2581be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2582be7a010dSDuan Jiong {
2583be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2584be7a010dSDuan Jiong 
2585be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2586be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2587be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2588be7a010dSDuan Jiong 		return -1;
2589be7a010dSDuan Jiong 	}
2590be7a010dSDuan Jiong 	return 0;
2591be7a010dSDuan Jiong }
2592be7a010dSDuan Jiong 
2593be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2594be7a010dSDuan Jiong {
2595be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2596be7a010dSDuan Jiong }
2597be7a010dSDuan Jiong 
25988ed67789SDaniel Lezcano struct arg_dev_net {
25998ed67789SDaniel Lezcano 	struct net_device *dev;
26008ed67789SDaniel Lezcano 	struct net *net;
26018ed67789SDaniel Lezcano };
26028ed67789SDaniel Lezcano 
26031da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
26041da177e4SLinus Torvalds {
2605bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2606bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
26078ed67789SDaniel Lezcano 
2608d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2609c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
26101da177e4SLinus Torvalds 		return -1;
2611c159d30cSDavid S. Miller 
26121da177e4SLinus Torvalds 	return 0;
26131da177e4SLinus Torvalds }
26141da177e4SLinus Torvalds 
2615f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
26161da177e4SLinus Torvalds {
26178ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
26188ed67789SDaniel Lezcano 		.dev = dev,
26198ed67789SDaniel Lezcano 		.net = net,
26208ed67789SDaniel Lezcano 	};
26218ed67789SDaniel Lezcano 
26220c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
26231e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
2624e332bc67SEric W. Biederman 	if (dev)
26258d0b94afSMartin KaFai Lau 		rt6_uncached_list_flush_dev(net, dev);
26261da177e4SLinus Torvalds }
26271da177e4SLinus Torvalds 
262895c96174SEric Dumazet struct rt6_mtu_change_arg {
26291da177e4SLinus Torvalds 	struct net_device *dev;
263095c96174SEric Dumazet 	unsigned int mtu;
26311da177e4SLinus Torvalds };
26321da177e4SLinus Torvalds 
26331da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
26341da177e4SLinus Torvalds {
26351da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
26361da177e4SLinus Torvalds 	struct inet6_dev *idev;
26371da177e4SLinus Torvalds 
26381da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
26391da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
26401da177e4SLinus Torvalds 	   We still use this lock to block changes
26411da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
26421da177e4SLinus Torvalds 	*/
26431da177e4SLinus Torvalds 
26441da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
264538308473SDavid S. Miller 	if (!idev)
26461da177e4SLinus Torvalds 		return 0;
26471da177e4SLinus Torvalds 
26481da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
26491da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
26501da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
26511da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
26521da177e4SLinus Torvalds 	 */
26531da177e4SLinus Torvalds 	/*
26541da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
26551da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
26561da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
26571da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
26581da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
26591da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
26601da177e4SLinus Torvalds 	   PMTU discouvery.
26611da177e4SLinus Torvalds 	 */
2662d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
26634b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
26644b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
26654b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
26664b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
26674b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
26684b32b5adSMartin KaFai Lau 			 * been updated.
26694b32b5adSMartin KaFai Lau 			 */
26704b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
26714b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
26724b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2673d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
26744b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2675defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2676566cfd8fSSimon Arlott 		}
26774b32b5adSMartin KaFai Lau 	}
26781da177e4SLinus Torvalds 	return 0;
26791da177e4SLinus Torvalds }
26801da177e4SLinus Torvalds 
268195c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
26821da177e4SLinus Torvalds {
2683c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2684c71099acSThomas Graf 		.dev = dev,
2685c71099acSThomas Graf 		.mtu = mtu,
2686c71099acSThomas Graf 	};
26871da177e4SLinus Torvalds 
26880c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
26891da177e4SLinus Torvalds }
26901da177e4SLinus Torvalds 
2691ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
26925176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
269386872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2694ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
269586872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
269686872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
269751ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2698c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
269919e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
270019e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
270186872cb5SThomas Graf };
270286872cb5SThomas Graf 
270386872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
270486872cb5SThomas Graf 			      struct fib6_config *cfg)
27051da177e4SLinus Torvalds {
270686872cb5SThomas Graf 	struct rtmsg *rtm;
270786872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2708c78ba6d6SLubomir Rintel 	unsigned int pref;
270986872cb5SThomas Graf 	int err;
27101da177e4SLinus Torvalds 
271186872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
271286872cb5SThomas Graf 	if (err < 0)
271386872cb5SThomas Graf 		goto errout;
27141da177e4SLinus Torvalds 
271586872cb5SThomas Graf 	err = -EINVAL;
271686872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
271786872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
271886872cb5SThomas Graf 
271986872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
272086872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
272186872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
272286872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
272386872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2724ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
272586872cb5SThomas Graf 
2726ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2727ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2728b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2729b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
273086872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
273186872cb5SThomas Graf 
2732ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2733ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2734ab79ad14SMaciej Żenczykowski 
27351f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
27361f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
27371f56a01fSMartin KaFai Lau 
273815e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
273986872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
27403b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
274186872cb5SThomas Graf 
274286872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
274367b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
274486872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
27451da177e4SLinus Torvalds 	}
274686872cb5SThomas Graf 
274786872cb5SThomas Graf 	if (tb[RTA_DST]) {
274886872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
274986872cb5SThomas Graf 
275086872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
275186872cb5SThomas Graf 			goto errout;
275286872cb5SThomas Graf 
275386872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
27541da177e4SLinus Torvalds 	}
275586872cb5SThomas Graf 
275686872cb5SThomas Graf 	if (tb[RTA_SRC]) {
275786872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
275886872cb5SThomas Graf 
275986872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
276086872cb5SThomas Graf 			goto errout;
276186872cb5SThomas Graf 
276286872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
27631da177e4SLinus Torvalds 	}
276486872cb5SThomas Graf 
2765c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
276667b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2767c3968a85SDaniel Walter 
276886872cb5SThomas Graf 	if (tb[RTA_OIF])
276986872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
277086872cb5SThomas Graf 
277186872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
277286872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
277386872cb5SThomas Graf 
277486872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
277586872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
277686872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
27771da177e4SLinus Torvalds 	}
277886872cb5SThomas Graf 
277986872cb5SThomas Graf 	if (tb[RTA_TABLE])
278086872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
278186872cb5SThomas Graf 
278251ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
278351ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
278451ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
278551ebd318SNicolas Dichtel 	}
278651ebd318SNicolas Dichtel 
2787c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2788c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2789c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2790c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2791c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2792c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2793c78ba6d6SLubomir Rintel 	}
2794c78ba6d6SLubomir Rintel 
279519e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
279619e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
279719e42e45SRoopa Prabhu 
279819e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP_TYPE])
279919e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
280019e42e45SRoopa Prabhu 
280186872cb5SThomas Graf 	err = 0;
280286872cb5SThomas Graf errout:
280386872cb5SThomas Graf 	return err;
28041da177e4SLinus Torvalds }
28051da177e4SLinus Torvalds 
28066b9ea5a6SRoopa Prabhu struct rt6_nh {
28076b9ea5a6SRoopa Prabhu 	struct rt6_info *rt6_info;
28086b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
28096b9ea5a6SRoopa Prabhu 	struct mx6_config mxc;
28106b9ea5a6SRoopa Prabhu 	struct list_head next;
28116b9ea5a6SRoopa Prabhu };
28126b9ea5a6SRoopa Prabhu 
28136b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
28146b9ea5a6SRoopa Prabhu {
28156b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28166b9ea5a6SRoopa Prabhu 
28176b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28186b9ea5a6SRoopa Prabhu 		pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n",
28196b9ea5a6SRoopa Prabhu 		        &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway,
28206b9ea5a6SRoopa Prabhu 		        nh->r_cfg.fc_ifindex);
28216b9ea5a6SRoopa Prabhu 	}
28226b9ea5a6SRoopa Prabhu }
28236b9ea5a6SRoopa Prabhu 
28246b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list,
28256b9ea5a6SRoopa Prabhu 				 struct rt6_info *rt, struct fib6_config *r_cfg)
28266b9ea5a6SRoopa Prabhu {
28276b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28286b9ea5a6SRoopa Prabhu 	struct rt6_info *rtnh;
28296b9ea5a6SRoopa Prabhu 	int err = -EEXIST;
28306b9ea5a6SRoopa Prabhu 
28316b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28326b9ea5a6SRoopa Prabhu 		/* check if rt6_info already exists */
28336b9ea5a6SRoopa Prabhu 		rtnh = nh->rt6_info;
28346b9ea5a6SRoopa Prabhu 
28356b9ea5a6SRoopa Prabhu 		if (rtnh->dst.dev == rt->dst.dev &&
28366b9ea5a6SRoopa Prabhu 		    rtnh->rt6i_idev == rt->rt6i_idev &&
28376b9ea5a6SRoopa Prabhu 		    ipv6_addr_equal(&rtnh->rt6i_gateway,
28386b9ea5a6SRoopa Prabhu 				    &rt->rt6i_gateway))
28396b9ea5a6SRoopa Prabhu 			return err;
28406b9ea5a6SRoopa Prabhu 	}
28416b9ea5a6SRoopa Prabhu 
28426b9ea5a6SRoopa Prabhu 	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
28436b9ea5a6SRoopa Prabhu 	if (!nh)
28446b9ea5a6SRoopa Prabhu 		return -ENOMEM;
28456b9ea5a6SRoopa Prabhu 	nh->rt6_info = rt;
28466b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&nh->mxc, r_cfg);
28476b9ea5a6SRoopa Prabhu 	if (err) {
28486b9ea5a6SRoopa Prabhu 		kfree(nh);
28496b9ea5a6SRoopa Prabhu 		return err;
28506b9ea5a6SRoopa Prabhu 	}
28516b9ea5a6SRoopa Prabhu 	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
28526b9ea5a6SRoopa Prabhu 	list_add_tail(&nh->next, rt6_nh_list);
28536b9ea5a6SRoopa Prabhu 
28546b9ea5a6SRoopa Prabhu 	return 0;
28556b9ea5a6SRoopa Prabhu }
28566b9ea5a6SRoopa Prabhu 
28576b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg)
285851ebd318SNicolas Dichtel {
285951ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
286051ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
28616b9ea5a6SRoopa Prabhu 	struct rt6_info *rt;
28626b9ea5a6SRoopa Prabhu 	struct rt6_nh *err_nh;
28636b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh, *nh_safe;
286451ebd318SNicolas Dichtel 	int remaining;
286551ebd318SNicolas Dichtel 	int attrlen;
28666b9ea5a6SRoopa Prabhu 	int err = 1;
28676b9ea5a6SRoopa Prabhu 	int nhn = 0;
28686b9ea5a6SRoopa Prabhu 	int replace = (cfg->fc_nlinfo.nlh &&
28696b9ea5a6SRoopa Prabhu 		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
28706b9ea5a6SRoopa Prabhu 	LIST_HEAD(rt6_nh_list);
287151ebd318SNicolas Dichtel 
287235f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
287351ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
287451ebd318SNicolas Dichtel 
28756b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
28766b9ea5a6SRoopa Prabhu 	 * rt6_info structs per nexthop
28776b9ea5a6SRoopa Prabhu 	 */
287851ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
287951ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
288051ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
288151ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
288251ebd318SNicolas Dichtel 
288351ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
288451ebd318SNicolas Dichtel 		if (attrlen > 0) {
288551ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
288651ebd318SNicolas Dichtel 
288751ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
288851ebd318SNicolas Dichtel 			if (nla) {
288967b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
289051ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
289151ebd318SNicolas Dichtel 			}
289219e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
289319e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
289419e42e45SRoopa Prabhu 			if (nla)
289519e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
289651ebd318SNicolas Dichtel 		}
28976b9ea5a6SRoopa Prabhu 
28988c5b83f0SRoopa Prabhu 		rt = ip6_route_info_create(&r_cfg);
28998c5b83f0SRoopa Prabhu 		if (IS_ERR(rt)) {
29008c5b83f0SRoopa Prabhu 			err = PTR_ERR(rt);
29018c5b83f0SRoopa Prabhu 			rt = NULL;
29026b9ea5a6SRoopa Prabhu 			goto cleanup;
29038c5b83f0SRoopa Prabhu 		}
29046b9ea5a6SRoopa Prabhu 
29056b9ea5a6SRoopa Prabhu 		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
290651ebd318SNicolas Dichtel 		if (err) {
29076b9ea5a6SRoopa Prabhu 			dst_free(&rt->dst);
29086b9ea5a6SRoopa Prabhu 			goto cleanup;
290951ebd318SNicolas Dichtel 		}
29106b9ea5a6SRoopa Prabhu 
29116b9ea5a6SRoopa Prabhu 		rtnh = rtnh_next(rtnh, &remaining);
291251ebd318SNicolas Dichtel 	}
29136b9ea5a6SRoopa Prabhu 
29146b9ea5a6SRoopa Prabhu 	err_nh = NULL;
29156b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29166b9ea5a6SRoopa Prabhu 		err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc);
29176b9ea5a6SRoopa Prabhu 		/* nh->rt6_info is used or freed at this point, reset to NULL*/
29186b9ea5a6SRoopa Prabhu 		nh->rt6_info = NULL;
29196b9ea5a6SRoopa Prabhu 		if (err) {
29206b9ea5a6SRoopa Prabhu 			if (replace && nhn)
29216b9ea5a6SRoopa Prabhu 				ip6_print_replace_route_err(&rt6_nh_list);
29226b9ea5a6SRoopa Prabhu 			err_nh = nh;
29236b9ea5a6SRoopa Prabhu 			goto add_errout;
29246b9ea5a6SRoopa Prabhu 		}
29256b9ea5a6SRoopa Prabhu 
29261a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
292727596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
292827596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
292927596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
293027596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
293127596472SMichal Kubeček 		 * be added to it.
29321a72418bSNicolas Dichtel 		 */
293327596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
293427596472SMichal Kubeček 						     NLM_F_REPLACE);
29356b9ea5a6SRoopa Prabhu 		nhn++;
29366b9ea5a6SRoopa Prabhu 	}
29376b9ea5a6SRoopa Prabhu 
29386b9ea5a6SRoopa Prabhu 	goto cleanup;
29396b9ea5a6SRoopa Prabhu 
29406b9ea5a6SRoopa Prabhu add_errout:
29416b9ea5a6SRoopa Prabhu 	/* Delete routes that were already added */
29426b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29436b9ea5a6SRoopa Prabhu 		if (err_nh == nh)
29446b9ea5a6SRoopa Prabhu 			break;
29456b9ea5a6SRoopa Prabhu 		ip6_route_del(&nh->r_cfg);
29466b9ea5a6SRoopa Prabhu 	}
29476b9ea5a6SRoopa Prabhu 
29486b9ea5a6SRoopa Prabhu cleanup:
29496b9ea5a6SRoopa Prabhu 	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
29506b9ea5a6SRoopa Prabhu 		if (nh->rt6_info)
29516b9ea5a6SRoopa Prabhu 			dst_free(&nh->rt6_info->dst);
29526b9ea5a6SRoopa Prabhu 		kfree(nh->mxc.mx);
29536b9ea5a6SRoopa Prabhu 		list_del(&nh->next);
29546b9ea5a6SRoopa Prabhu 		kfree(nh);
29556b9ea5a6SRoopa Prabhu 	}
29566b9ea5a6SRoopa Prabhu 
29576b9ea5a6SRoopa Prabhu 	return err;
29586b9ea5a6SRoopa Prabhu }
29596b9ea5a6SRoopa Prabhu 
29606b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg)
29616b9ea5a6SRoopa Prabhu {
29626b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
29636b9ea5a6SRoopa Prabhu 	struct rtnexthop *rtnh;
29646b9ea5a6SRoopa Prabhu 	int remaining;
29656b9ea5a6SRoopa Prabhu 	int attrlen;
29666b9ea5a6SRoopa Prabhu 	int err = 1, last_err = 0;
29676b9ea5a6SRoopa Prabhu 
29686b9ea5a6SRoopa Prabhu 	remaining = cfg->fc_mp_len;
29696b9ea5a6SRoopa Prabhu 	rtnh = (struct rtnexthop *)cfg->fc_mp;
29706b9ea5a6SRoopa Prabhu 
29716b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry */
29726b9ea5a6SRoopa Prabhu 	while (rtnh_ok(rtnh, remaining)) {
29736b9ea5a6SRoopa Prabhu 		memcpy(&r_cfg, cfg, sizeof(*cfg));
29746b9ea5a6SRoopa Prabhu 		if (rtnh->rtnh_ifindex)
29756b9ea5a6SRoopa Prabhu 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
29766b9ea5a6SRoopa Prabhu 
29776b9ea5a6SRoopa Prabhu 		attrlen = rtnh_attrlen(rtnh);
29786b9ea5a6SRoopa Prabhu 		if (attrlen > 0) {
29796b9ea5a6SRoopa Prabhu 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
29806b9ea5a6SRoopa Prabhu 
29816b9ea5a6SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
29826b9ea5a6SRoopa Prabhu 			if (nla) {
29836b9ea5a6SRoopa Prabhu 				nla_memcpy(&r_cfg.fc_gateway, nla, 16);
29846b9ea5a6SRoopa Prabhu 				r_cfg.fc_flags |= RTF_GATEWAY;
29856b9ea5a6SRoopa Prabhu 			}
29866b9ea5a6SRoopa Prabhu 		}
29876b9ea5a6SRoopa Prabhu 		err = ip6_route_del(&r_cfg);
29886b9ea5a6SRoopa Prabhu 		if (err)
29896b9ea5a6SRoopa Prabhu 			last_err = err;
29906b9ea5a6SRoopa Prabhu 
299151ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
299251ebd318SNicolas Dichtel 	}
299351ebd318SNicolas Dichtel 
299451ebd318SNicolas Dichtel 	return last_err;
299551ebd318SNicolas Dichtel }
299651ebd318SNicolas Dichtel 
2997661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
29981da177e4SLinus Torvalds {
299986872cb5SThomas Graf 	struct fib6_config cfg;
300086872cb5SThomas Graf 	int err;
30011da177e4SLinus Torvalds 
300286872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
300386872cb5SThomas Graf 	if (err < 0)
300486872cb5SThomas Graf 		return err;
300586872cb5SThomas Graf 
300651ebd318SNicolas Dichtel 	if (cfg.fc_mp)
30076b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_del(&cfg);
300851ebd318SNicolas Dichtel 	else
300986872cb5SThomas Graf 		return ip6_route_del(&cfg);
30101da177e4SLinus Torvalds }
30111da177e4SLinus Torvalds 
3012661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
30131da177e4SLinus Torvalds {
301486872cb5SThomas Graf 	struct fib6_config cfg;
301586872cb5SThomas Graf 	int err;
30161da177e4SLinus Torvalds 
301786872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
301886872cb5SThomas Graf 	if (err < 0)
301986872cb5SThomas Graf 		return err;
302086872cb5SThomas Graf 
302151ebd318SNicolas Dichtel 	if (cfg.fc_mp)
30226b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_add(&cfg);
302351ebd318SNicolas Dichtel 	else
302486872cb5SThomas Graf 		return ip6_route_add(&cfg);
30251da177e4SLinus Torvalds }
30261da177e4SLinus Torvalds 
302719e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
3028339bf98fSThomas Graf {
3029339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
3030339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
3031339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
3032339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
3033339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
3034339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
3035339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
3036339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
3037339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
30386a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
3039ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
3040c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
304119e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
304261adedf3SJiri Benc 	       + lwtunnel_get_encap_size(rt->dst.lwtstate);
3043339bf98fSThomas Graf }
3044339bf98fSThomas Graf 
3045191cd582SBrian Haley static int rt6_fill_node(struct net *net,
3046191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
30470d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
304815e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
30497bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
30501da177e4SLinus Torvalds {
30514b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
30521da177e4SLinus Torvalds 	struct rtmsg *rtm;
30531da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
3054e3703b3dSThomas Graf 	long expires;
30559e762a4aSPatrick McHardy 	u32 table;
30561da177e4SLinus Torvalds 
30571da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
30581da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
30591da177e4SLinus Torvalds 			/* success since this is not a prefix route */
30601da177e4SLinus Torvalds 			return 1;
30611da177e4SLinus Torvalds 		}
30621da177e4SLinus Torvalds 	}
30631da177e4SLinus Torvalds 
306415e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
306538308473SDavid S. Miller 	if (!nlh)
306626932566SPatrick McHardy 		return -EMSGSIZE;
30672d7202bfSThomas Graf 
30682d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
30691da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
30701da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
30711da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
30721da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
3073c71099acSThomas Graf 	if (rt->rt6i_table)
30749e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
3075c71099acSThomas Graf 	else
30769e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
30779e762a4aSPatrick McHardy 	rtm->rtm_table = table;
3078c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
3079c78679e8SDavid S. Miller 		goto nla_put_failure;
3080ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
3081ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
3082ef2c7d7bSNicolas Dichtel 		case -EINVAL:
3083ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
3084ef2c7d7bSNicolas Dichtel 			break;
3085ef2c7d7bSNicolas Dichtel 		case -EACCES:
3086ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
3087ef2c7d7bSNicolas Dichtel 			break;
3088b4949ab2SNicolas Dichtel 		case -EAGAIN:
3089b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
3090b4949ab2SNicolas Dichtel 			break;
3091ef2c7d7bSNicolas Dichtel 		default:
30921da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
3093ef2c7d7bSNicolas Dichtel 			break;
3094ef2c7d7bSNicolas Dichtel 		}
3095ef2c7d7bSNicolas Dichtel 	}
3096ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
3097ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
3098d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
30991da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
31001da177e4SLinus Torvalds 	else
31011da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
31021da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
310335103d11SAndy Gospodarek 	if (!netif_carrier_ok(rt->dst.dev)) {
3104cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
310535103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
310635103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
310735103d11SAndy Gospodarek 	}
31081da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
31091da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
31101da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
31111da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
3112f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
3113f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
31141da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
3115f0396f60SDenis Ovsienko 		else
3116f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
3117f0396f60SDenis Ovsienko 	}
31181da177e4SLinus Torvalds 
31191da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
31201da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
31211da177e4SLinus Torvalds 
31221da177e4SLinus Torvalds 	if (dst) {
3123930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
3124c78679e8SDavid S. Miller 			goto nla_put_failure;
31251da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
31261da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
3127930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
3128c78679e8SDavid S. Miller 			goto nla_put_failure;
31291da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
31301da177e4SLinus Torvalds 	if (src) {
3131930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
3132c78679e8SDavid S. Miller 			goto nla_put_failure;
31331da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
3134c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
3135930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
3136c78679e8SDavid S. Miller 		goto nla_put_failure;
31371da177e4SLinus Torvalds #endif
31387bc570c8SYOSHIFUJI Hideaki 	if (iif) {
31397bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
31407bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
31418229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
31427bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
31437bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
31447bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
31457bc570c8SYOSHIFUJI Hideaki 						return 0;
31467bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
31477bc570c8SYOSHIFUJI Hideaki 				} else {
31487bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
31497bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
31507bc570c8SYOSHIFUJI Hideaki 				}
31517bc570c8SYOSHIFUJI Hideaki 			}
31527bc570c8SYOSHIFUJI Hideaki 		} else
31537bc570c8SYOSHIFUJI Hideaki #endif
3154c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
3155c78679e8SDavid S. Miller 				goto nla_put_failure;
31567bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
31571da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
3158c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
3159930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3160c78679e8SDavid S. Miller 			goto nla_put_failure;
3161c3968a85SDaniel Walter 	}
3162c3968a85SDaniel Walter 
3163c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
3164c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
31654e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
3166930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3167c78679e8SDavid S. Miller 			goto nla_put_failure;
31681da177e4SLinus Torvalds 	}
31692d7202bfSThomas Graf 
31704b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
31714b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
31724b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
31734b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
31742d7202bfSThomas Graf 		goto nla_put_failure;
31752d7202bfSThomas Graf 
3176dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
3177930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
317894f826b8SEric Dumazet 			goto nla_put_failure;
317994f826b8SEric Dumazet 	}
31802d7202bfSThomas Graf 
3181c78679e8SDavid S. Miller 	if (rt->dst.dev &&
3182c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3183c78679e8SDavid S. Miller 		goto nla_put_failure;
3184c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
3185c78679e8SDavid S. Miller 		goto nla_put_failure;
31868253947eSLi Wei 
31878253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
318869cdf8f9SYOSHIFUJI Hideaki 
318987a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
3190e3703b3dSThomas Graf 		goto nla_put_failure;
31911da177e4SLinus Torvalds 
3192c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
3193c78ba6d6SLubomir Rintel 		goto nla_put_failure;
3194c78ba6d6SLubomir Rintel 
319561adedf3SJiri Benc 	lwtunnel_fill_encap(skb, rt->dst.lwtstate);
319619e42e45SRoopa Prabhu 
3197053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
3198053c095aSJohannes Berg 	return 0;
31992d7202bfSThomas Graf 
32002d7202bfSThomas Graf nla_put_failure:
320126932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
320226932566SPatrick McHardy 	return -EMSGSIZE;
32031da177e4SLinus Torvalds }
32041da177e4SLinus Torvalds 
32051b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
32061da177e4SLinus Torvalds {
32071da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
32081da177e4SLinus Torvalds 	int prefix;
32091da177e4SLinus Torvalds 
32102d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
32112d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
32121da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
32131da177e4SLinus Torvalds 	} else
32141da177e4SLinus Torvalds 		prefix = 0;
32151da177e4SLinus Torvalds 
3216191cd582SBrian Haley 	return rt6_fill_node(arg->net,
3217191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
321815e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
32197bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
32201da177e4SLinus Torvalds }
32211da177e4SLinus Torvalds 
3222661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
32231da177e4SLinus Torvalds {
32243b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3225ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
32261da177e4SLinus Torvalds 	struct rt6_info *rt;
3227ab364a6fSThomas Graf 	struct sk_buff *skb;
3228ab364a6fSThomas Graf 	struct rtmsg *rtm;
32294c9483b2SDavid S. Miller 	struct flowi6 fl6;
323072331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3231ab364a6fSThomas Graf 
3232ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3233ab364a6fSThomas Graf 	if (err < 0)
3234ab364a6fSThomas Graf 		goto errout;
3235ab364a6fSThomas Graf 
3236ab364a6fSThomas Graf 	err = -EINVAL;
32374c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
3238ab364a6fSThomas Graf 
3239ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3240ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3241ab364a6fSThomas Graf 			goto errout;
3242ab364a6fSThomas Graf 
32434e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3244ab364a6fSThomas Graf 	}
3245ab364a6fSThomas Graf 
3246ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3247ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3248ab364a6fSThomas Graf 			goto errout;
3249ab364a6fSThomas Graf 
32504e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3251ab364a6fSThomas Graf 	}
3252ab364a6fSThomas Graf 
3253ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3254ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3255ab364a6fSThomas Graf 
3256ab364a6fSThomas Graf 	if (tb[RTA_OIF])
325772331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3258ab364a6fSThomas Graf 
32592e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
32602e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
32612e47b291SLorenzo Colitti 
3262ab364a6fSThomas Graf 	if (iif) {
3263ab364a6fSThomas Graf 		struct net_device *dev;
326472331bc0SShmulik Ladkani 		int flags = 0;
326572331bc0SShmulik Ladkani 
32665578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3267ab364a6fSThomas Graf 		if (!dev) {
3268ab364a6fSThomas Graf 			err = -ENODEV;
3269ab364a6fSThomas Graf 			goto errout;
3270ab364a6fSThomas Graf 		}
327172331bc0SShmulik Ladkani 
327272331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
327372331bc0SShmulik Ladkani 
327472331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
327572331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
327672331bc0SShmulik Ladkani 
327772331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
327872331bc0SShmulik Ladkani 							       flags);
327972331bc0SShmulik Ladkani 	} else {
328072331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
328172331bc0SShmulik Ladkani 
3282ca254490SDavid Ahern 		if (netif_index_is_l3_master(net, oif)) {
3283ca254490SDavid Ahern 			fl6.flowi6_flags = FLOWI_FLAG_L3MDEV_SRC |
3284ca254490SDavid Ahern 					   FLOWI_FLAG_SKIP_NH_OIF;
3285ca254490SDavid Ahern 		}
3286ca254490SDavid Ahern 
328772331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3288ab364a6fSThomas Graf 	}
32891da177e4SLinus Torvalds 
32901da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
329138308473SDavid S. Miller 	if (!skb) {
329294e187c0SAmerigo Wang 		ip6_rt_put(rt);
3293ab364a6fSThomas Graf 		err = -ENOBUFS;
3294ab364a6fSThomas Graf 		goto errout;
3295ab364a6fSThomas Graf 	}
32961da177e4SLinus Torvalds 
32971da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
32981da177e4SLinus Torvalds 	   through good chunk of routing engine.
32991da177e4SLinus Torvalds 	 */
3300459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
33011da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
33021da177e4SLinus Torvalds 
3303d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
33041da177e4SLinus Torvalds 
33054c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
330615e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
33077bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
33081da177e4SLinus Torvalds 	if (err < 0) {
3309ab364a6fSThomas Graf 		kfree_skb(skb);
3310ab364a6fSThomas Graf 		goto errout;
33111da177e4SLinus Torvalds 	}
33121da177e4SLinus Torvalds 
331315e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3314ab364a6fSThomas Graf errout:
33151da177e4SLinus Torvalds 	return err;
33161da177e4SLinus Torvalds }
33171da177e4SLinus Torvalds 
331837a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
331937a1d361SRoopa Prabhu 		     unsigned int nlm_flags)
33201da177e4SLinus Torvalds {
33211da177e4SLinus Torvalds 	struct sk_buff *skb;
33225578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3323528c4cebSDenis V. Lunev 	u32 seq;
3324528c4cebSDenis V. Lunev 	int err;
33250d51aa80SJamal Hadi Salim 
3326528c4cebSDenis V. Lunev 	err = -ENOBUFS;
332738308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
332886872cb5SThomas Graf 
332919e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
333038308473SDavid S. Miller 	if (!skb)
333121713ebcSThomas Graf 		goto errout;
33321da177e4SLinus Torvalds 
3333191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
333437a1d361SRoopa Prabhu 				event, info->portid, seq, 0, 0, nlm_flags);
333526932566SPatrick McHardy 	if (err < 0) {
333626932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
333726932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
333826932566SPatrick McHardy 		kfree_skb(skb);
333926932566SPatrick McHardy 		goto errout;
334026932566SPatrick McHardy 	}
334115e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
33425578689aSDaniel Lezcano 		    info->nlh, gfp_any());
33431ce85fe4SPablo Neira Ayuso 	return;
334421713ebcSThomas Graf errout:
334521713ebcSThomas Graf 	if (err < 0)
33465578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
33471da177e4SLinus Torvalds }
33481da177e4SLinus Torvalds 
33498ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3350351638e7SJiri Pirko 				unsigned long event, void *ptr)
33518ed67789SDaniel Lezcano {
3352351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3353c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
33548ed67789SDaniel Lezcano 
33558ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3356d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
33578ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
33588ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3359d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
33608ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3361d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
33628ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
33638ed67789SDaniel Lezcano #endif
33648ed67789SDaniel Lezcano 	}
33658ed67789SDaniel Lezcano 
33668ed67789SDaniel Lezcano 	return NOTIFY_OK;
33678ed67789SDaniel Lezcano }
33688ed67789SDaniel Lezcano 
33691da177e4SLinus Torvalds /*
33701da177e4SLinus Torvalds  *	/proc
33711da177e4SLinus Torvalds  */
33721da177e4SLinus Torvalds 
33731da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
33741da177e4SLinus Torvalds 
337533120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
337633120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
337733120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
337833120b30SAlexey Dobriyan 	.read		= seq_read,
337933120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
33808d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
338133120b30SAlexey Dobriyan };
338233120b30SAlexey Dobriyan 
33831da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
33841da177e4SLinus Torvalds {
338569ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
33861da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
338769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
338869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
338969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
339069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
339169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3392fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
339369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
33941da177e4SLinus Torvalds 
33951da177e4SLinus Torvalds 	return 0;
33961da177e4SLinus Torvalds }
33971da177e4SLinus Torvalds 
33981da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
33991da177e4SLinus Torvalds {
3400de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
340169ddb805SDaniel Lezcano }
340269ddb805SDaniel Lezcano 
34039a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
34041da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
34051da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
34061da177e4SLinus Torvalds 	.read	 = seq_read,
34071da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3408b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
34091da177e4SLinus Torvalds };
34101da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
34111da177e4SLinus Torvalds 
34121da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
34131da177e4SLinus Torvalds 
34141da177e4SLinus Torvalds static
3415fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
34161da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
34171da177e4SLinus Torvalds {
3418c486da34SLucian Adrian Grijincu 	struct net *net;
3419c486da34SLucian Adrian Grijincu 	int delay;
3420c486da34SLucian Adrian Grijincu 	if (!write)
3421c486da34SLucian Adrian Grijincu 		return -EINVAL;
3422c486da34SLucian Adrian Grijincu 
3423c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3424c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
34258d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
34262ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
34271da177e4SLinus Torvalds 	return 0;
34281da177e4SLinus Torvalds }
34291da177e4SLinus Torvalds 
3430fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
34311da177e4SLinus Torvalds 	{
34321da177e4SLinus Torvalds 		.procname	=	"flush",
34334990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
34341da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
343589c8b3a1SDave Jones 		.mode		=	0200,
34366d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
34371da177e4SLinus Torvalds 	},
34381da177e4SLinus Torvalds 	{
34391da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
34409a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
34411da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34421da177e4SLinus Torvalds 		.mode		=	0644,
34436d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34441da177e4SLinus Torvalds 	},
34451da177e4SLinus Torvalds 	{
34461da177e4SLinus Torvalds 		.procname	=	"max_size",
34474990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
34481da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34491da177e4SLinus Torvalds 		.mode		=	0644,
34506d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34511da177e4SLinus Torvalds 	},
34521da177e4SLinus Torvalds 	{
34531da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
34544990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34551da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34561da177e4SLinus Torvalds 		.mode		=	0644,
34576d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34581da177e4SLinus Torvalds 	},
34591da177e4SLinus Torvalds 	{
34601da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
34614990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
34621da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34631da177e4SLinus Torvalds 		.mode		=	0644,
34646d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34651da177e4SLinus Torvalds 	},
34661da177e4SLinus Torvalds 	{
34671da177e4SLinus Torvalds 		.procname	=	"gc_interval",
34684990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
34691da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34701da177e4SLinus Torvalds 		.mode		=	0644,
34716d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34721da177e4SLinus Torvalds 	},
34731da177e4SLinus Torvalds 	{
34741da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
34754990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
34761da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34771da177e4SLinus Torvalds 		.mode		=	0644,
3478f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34791da177e4SLinus Torvalds 	},
34801da177e4SLinus Torvalds 	{
34811da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
34824990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
34831da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34841da177e4SLinus Torvalds 		.mode		=	0644,
34856d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34861da177e4SLinus Torvalds 	},
34871da177e4SLinus Torvalds 	{
34881da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
34894990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
34901da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34911da177e4SLinus Torvalds 		.mode		=	0644,
3492f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34931da177e4SLinus Torvalds 	},
34941da177e4SLinus Torvalds 	{
34951da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
34964990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34971da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34981da177e4SLinus Torvalds 		.mode		=	0644,
34996d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
35001da177e4SLinus Torvalds 	},
3501f8572d8fSEric W. Biederman 	{ }
35021da177e4SLinus Torvalds };
35031da177e4SLinus Torvalds 
35042c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3505760f2d01SDaniel Lezcano {
3506760f2d01SDaniel Lezcano 	struct ctl_table *table;
3507760f2d01SDaniel Lezcano 
3508760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3509760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3510760f2d01SDaniel Lezcano 			GFP_KERNEL);
35115ee09105SYOSHIFUJI Hideaki 
35125ee09105SYOSHIFUJI Hideaki 	if (table) {
35135ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3514c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
351586393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
35165ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
35175ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
35185ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
35195ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
35205ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
35215ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
35225ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
35239c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3524464dc801SEric W. Biederman 
3525464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3526464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3527464dc801SEric W. Biederman 			table[0].procname = NULL;
35285ee09105SYOSHIFUJI Hideaki 	}
35295ee09105SYOSHIFUJI Hideaki 
3530760f2d01SDaniel Lezcano 	return table;
3531760f2d01SDaniel Lezcano }
35321da177e4SLinus Torvalds #endif
35331da177e4SLinus Torvalds 
35342c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3535cdb18761SDaniel Lezcano {
3536633d424bSPavel Emelyanov 	int ret = -ENOMEM;
35378ed67789SDaniel Lezcano 
353886393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
353986393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3540f2fc6a54SBenjamin Thery 
3541fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3542fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3543fc66f95cSEric Dumazet 
35448ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
35458ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
35468ed67789SDaniel Lezcano 					   GFP_KERNEL);
35478ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3548fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3549d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
35508ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3551d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
355262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
355362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35548ed67789SDaniel Lezcano 
35558ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
35568ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
35578ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
35588ed67789SDaniel Lezcano 					       GFP_KERNEL);
355968fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
356068fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3561d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
35628ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3563d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
356462fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
356562fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35668ed67789SDaniel Lezcano 
35678ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
35688ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
35698ed67789SDaniel Lezcano 					       GFP_KERNEL);
357068fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
357168fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3572d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
35738ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3574d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
357562fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
357662fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35778ed67789SDaniel Lezcano #endif
35788ed67789SDaniel Lezcano 
3579b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3580b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3581b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3582b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3583b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3584b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3585b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3586b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3587b339a47cSPeter Zijlstra 
35886891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
35896891a346SBenjamin Thery 
35908ed67789SDaniel Lezcano 	ret = 0;
35918ed67789SDaniel Lezcano out:
35928ed67789SDaniel Lezcano 	return ret;
3593f2fc6a54SBenjamin Thery 
359468fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
359568fffc67SPeter Zijlstra out_ip6_prohibit_entry:
359668fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
359768fffc67SPeter Zijlstra out_ip6_null_entry:
359868fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
359968fffc67SPeter Zijlstra #endif
3600fc66f95cSEric Dumazet out_ip6_dst_entries:
3601fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3602f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3603f2fc6a54SBenjamin Thery 	goto out;
3604cdb18761SDaniel Lezcano }
3605cdb18761SDaniel Lezcano 
36062c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3607cdb18761SDaniel Lezcano {
36088ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
36098ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
36108ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
36118ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
36128ed67789SDaniel Lezcano #endif
361341bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3614cdb18761SDaniel Lezcano }
3615cdb18761SDaniel Lezcano 
3616d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3617d189634eSThomas Graf {
3618d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3619d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3620d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3621d189634eSThomas Graf #endif
3622d189634eSThomas Graf 	return 0;
3623d189634eSThomas Graf }
3624d189634eSThomas Graf 
3625d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3626d189634eSThomas Graf {
3627d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3628ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3629ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3630d189634eSThomas Graf #endif
3631d189634eSThomas Graf }
3632d189634eSThomas Graf 
3633cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3634cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3635cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3636cdb18761SDaniel Lezcano };
3637cdb18761SDaniel Lezcano 
3638c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3639c3426b47SDavid S. Miller {
3640c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3641c3426b47SDavid S. Miller 
3642c3426b47SDavid S. Miller 	if (!bp)
3643c3426b47SDavid S. Miller 		return -ENOMEM;
3644c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3645c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3646c3426b47SDavid S. Miller 	return 0;
3647c3426b47SDavid S. Miller }
3648c3426b47SDavid S. Miller 
3649c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3650c3426b47SDavid S. Miller {
3651c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3652c3426b47SDavid S. Miller 
3653c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
365456a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3655c3426b47SDavid S. Miller 	kfree(bp);
3656c3426b47SDavid S. Miller }
3657c3426b47SDavid S. Miller 
36582b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3659c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3660c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3661c3426b47SDavid S. Miller };
3662c3426b47SDavid S. Miller 
3663d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3664d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3665d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3666d189634eSThomas Graf };
3667d189634eSThomas Graf 
36688ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
36698ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
36708ed67789SDaniel Lezcano 	.priority = 0,
36718ed67789SDaniel Lezcano };
36728ed67789SDaniel Lezcano 
3673433d49c3SDaniel Lezcano int __init ip6_route_init(void)
36741da177e4SLinus Torvalds {
3675433d49c3SDaniel Lezcano 	int ret;
36768d0b94afSMartin KaFai Lau 	int cpu;
3677433d49c3SDaniel Lezcano 
36789a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
36799a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
36809a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
36819a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
36829a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3683c19a28e1SFernando Carrijo 		goto out;
368414e50e57SDavid S. Miller 
3685fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
36868ed67789SDaniel Lezcano 	if (ret)
3687bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3688bdb3289fSDaniel Lezcano 
3689c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3690c3426b47SDavid S. Miller 	if (ret)
3691e8803b6cSDavid S. Miller 		goto out_dst_entries;
36922a0c451aSThomas Graf 
36937e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
36947e52b33bSDavid S. Miller 	if (ret)
36957e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3696c3426b47SDavid S. Miller 
36975dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
36985dc121e9SArnaud Ebalard 
36998ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
37008ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
37018ed67789SDaniel Lezcano 	 * manually for init_net */
3702d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
37038ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3704bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3705d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
37068ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3707d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
37088ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3709bdb3289fSDaniel Lezcano   #endif
3710e8803b6cSDavid S. Miller 	ret = fib6_init();
3711433d49c3SDaniel Lezcano 	if (ret)
37128ed67789SDaniel Lezcano 		goto out_register_subsys;
3713433d49c3SDaniel Lezcano 
3714433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3715433d49c3SDaniel Lezcano 	if (ret)
3716e8803b6cSDavid S. Miller 		goto out_fib6_init;
3717c35b7e72SDaniel Lezcano 
3718433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3719433d49c3SDaniel Lezcano 	if (ret)
3720433d49c3SDaniel Lezcano 		goto xfrm6_init;
37217e5449c2SDaniel Lezcano 
3722d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3723d189634eSThomas Graf 	if (ret)
3724d189634eSThomas Graf 		goto fib6_rules_init;
3725d189634eSThomas Graf 
3726433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3727c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3728c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3729c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3730d189634eSThomas Graf 		goto out_register_late_subsys;
3731433d49c3SDaniel Lezcano 
37328ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3733cdb18761SDaniel Lezcano 	if (ret)
3734d189634eSThomas Graf 		goto out_register_late_subsys;
37358ed67789SDaniel Lezcano 
37368d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
37378d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
37388d0b94afSMartin KaFai Lau 
37398d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
37408d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
37418d0b94afSMartin KaFai Lau 	}
37428d0b94afSMartin KaFai Lau 
3743433d49c3SDaniel Lezcano out:
3744433d49c3SDaniel Lezcano 	return ret;
3745433d49c3SDaniel Lezcano 
3746d189634eSThomas Graf out_register_late_subsys:
3747d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3748433d49c3SDaniel Lezcano fib6_rules_init:
3749433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3750433d49c3SDaniel Lezcano xfrm6_init:
3751433d49c3SDaniel Lezcano 	xfrm6_fini();
37522a0c451aSThomas Graf out_fib6_init:
37532a0c451aSThomas Graf 	fib6_gc_cleanup();
37548ed67789SDaniel Lezcano out_register_subsys:
37558ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
37567e52b33bSDavid S. Miller out_register_inetpeer:
37577e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3758fc66f95cSEric Dumazet out_dst_entries:
3759fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3760433d49c3SDaniel Lezcano out_kmem_cache:
3761f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3762433d49c3SDaniel Lezcano 	goto out;
37631da177e4SLinus Torvalds }
37641da177e4SLinus Torvalds 
37651da177e4SLinus Torvalds void ip6_route_cleanup(void)
37661da177e4SLinus Torvalds {
37678ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3768d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3769101367c2SThomas Graf 	fib6_rules_cleanup();
37701da177e4SLinus Torvalds 	xfrm6_fini();
37711da177e4SLinus Torvalds 	fib6_gc_cleanup();
3772c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
37738ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
377441bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3775f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
37761da177e4SLinus Torvalds }
3777