xref: /openbmc/linux/net/ipv6/route.c (revision 38b7097b55b6cf30adc5ac07cb1055683224393e)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Linux INET6 implementation
31da177e4SLinus Torvalds  *	FIB front-end.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
91da177e4SLinus Torvalds  *      modify it under the terms of the GNU General Public License
101da177e4SLinus Torvalds  *      as published by the Free Software Foundation; either version
111da177e4SLinus Torvalds  *      2 of the License, or (at your option) any later version.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds /*	Changes:
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki @USAGI
171da177e4SLinus Torvalds  *		reworked default router selection.
181da177e4SLinus Torvalds  *		- respect outgoing interface
191da177e4SLinus Torvalds  *		- select from (probably) reachable routers (i.e.
201da177e4SLinus Torvalds  *		routers in REACHABLE, STALE, DELAY or PROBE states).
211da177e4SLinus Torvalds  *		- always select the same router if it is (probably)
221da177e4SLinus Torvalds  *		reachable.  otherwise, round-robin the list.
23c0bece9fSYOSHIFUJI Hideaki  *	Ville Nuorvala
24c0bece9fSYOSHIFUJI Hideaki  *		Fixed routing subtrees.
251da177e4SLinus Torvalds  */
261da177e4SLinus Torvalds 
27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt
28f3213831SJoe Perches 
294fc268d2SRandy Dunlap #include <linux/capability.h>
301da177e4SLinus Torvalds #include <linux/errno.h>
31bc3b2d7fSPaul Gortmaker #include <linux/export.h>
321da177e4SLinus Torvalds #include <linux/types.h>
331da177e4SLinus Torvalds #include <linux/times.h>
341da177e4SLinus Torvalds #include <linux/socket.h>
351da177e4SLinus Torvalds #include <linux/sockios.h>
361da177e4SLinus Torvalds #include <linux/net.h>
371da177e4SLinus Torvalds #include <linux/route.h>
381da177e4SLinus Torvalds #include <linux/netdevice.h>
391da177e4SLinus Torvalds #include <linux/in6.h>
407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
411da177e4SLinus Torvalds #include <linux/init.h>
421da177e4SLinus Torvalds #include <linux/if_arp.h>
431da177e4SLinus Torvalds #include <linux/proc_fs.h>
441da177e4SLinus Torvalds #include <linux/seq_file.h>
455b7c931dSDaniel Lezcano #include <linux/nsproxy.h>
465a0e3ad6STejun Heo #include <linux/slab.h>
47457c4cbcSEric W. Biederman #include <net/net_namespace.h>
481da177e4SLinus Torvalds #include <net/snmp.h>
491da177e4SLinus Torvalds #include <net/ipv6.h>
501da177e4SLinus Torvalds #include <net/ip6_fib.h>
511da177e4SLinus Torvalds #include <net/ip6_route.h>
521da177e4SLinus Torvalds #include <net/ndisc.h>
531da177e4SLinus Torvalds #include <net/addrconf.h>
541da177e4SLinus Torvalds #include <net/tcp.h>
551da177e4SLinus Torvalds #include <linux/rtnetlink.h>
561da177e4SLinus Torvalds #include <net/dst.h>
57904af04dSJiri Benc #include <net/dst_metadata.h>
581da177e4SLinus Torvalds #include <net/xfrm.h>
598d71740cSTom Tucker #include <net/netevent.h>
6021713ebcSThomas Graf #include <net/netlink.h>
6151ebd318SNicolas Dichtel #include <net/nexthop.h>
6219e42e45SRoopa Prabhu #include <net/lwtunnel.h>
63904af04dSJiri Benc #include <net/ip_tunnels.h>
64ca254490SDavid Ahern #include <net/l3mdev.h>
65b811580dSDavid Ahern #include <trace/events/fib6.h>
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds #include <asm/uaccess.h>
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
701da177e4SLinus Torvalds #include <linux/sysctl.h>
711da177e4SLinus Torvalds #endif
721da177e4SLinus Torvalds 
73afc154e9SHannes Frederic Sowa enum rt6_nud_state {
747e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
757e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
767e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
77afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
78afc154e9SHannes Frederic Sowa };
79afc154e9SHannes Frederic Sowa 
8083a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort);
811da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
820dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
83ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
841da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
851da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
861da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
871da177e4SLinus Torvalds 				       struct net_device *dev, int how);
88569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
91ede2059dSEric W. Biederman static int		ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
927150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
93ede2059dSEric W. Biederman static int		ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
941da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
956700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
966700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
976700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
986700c270SDavid S. Miller 					struct sk_buff *skb);
994b32b5adSMartin KaFai Lau static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
10052bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
1011da177e4SLinus Torvalds 
10270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
104b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
105b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
10695c96174SEric Dumazet 					   unsigned int pref);
107efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
108b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
109b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
11070ceb4f5SYOSHIFUJI Hideaki #endif
11170ceb4f5SYOSHIFUJI Hideaki 
1128d0b94afSMartin KaFai Lau struct uncached_list {
1138d0b94afSMartin KaFai Lau 	spinlock_t		lock;
1148d0b94afSMartin KaFai Lau 	struct list_head	head;
1158d0b94afSMartin KaFai Lau };
1168d0b94afSMartin KaFai Lau 
1178d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1188d0b94afSMartin KaFai Lau 
1198d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt)
1208d0b94afSMartin KaFai Lau {
1218d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1228d0b94afSMartin KaFai Lau 
1238d0b94afSMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
1248d0b94afSMartin KaFai Lau 	rt->rt6i_uncached_list = ul;
1258d0b94afSMartin KaFai Lau 
1268d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
1278d0b94afSMartin KaFai Lau 	list_add_tail(&rt->rt6i_uncached, &ul->head);
1288d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
1298d0b94afSMartin KaFai Lau }
1308d0b94afSMartin KaFai Lau 
1318d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt)
1328d0b94afSMartin KaFai Lau {
1338d0b94afSMartin KaFai Lau 	if (!list_empty(&rt->rt6i_uncached)) {
1348d0b94afSMartin KaFai Lau 		struct uncached_list *ul = rt->rt6i_uncached_list;
1358d0b94afSMartin KaFai Lau 
1368d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1378d0b94afSMartin KaFai Lau 		list_del(&rt->rt6i_uncached);
1388d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1398d0b94afSMartin KaFai Lau 	}
1408d0b94afSMartin KaFai Lau }
1418d0b94afSMartin KaFai Lau 
1428d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
1438d0b94afSMartin KaFai Lau {
1448d0b94afSMartin KaFai Lau 	struct net_device *loopback_dev = net->loopback_dev;
1458d0b94afSMartin KaFai Lau 	int cpu;
1468d0b94afSMartin KaFai Lau 
147e332bc67SEric W. Biederman 	if (dev == loopback_dev)
148e332bc67SEric W. Biederman 		return;
149e332bc67SEric W. Biederman 
1508d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
1518d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
1528d0b94afSMartin KaFai Lau 		struct rt6_info *rt;
1538d0b94afSMartin KaFai Lau 
1548d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1558d0b94afSMartin KaFai Lau 		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
1568d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
1578d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
1588d0b94afSMartin KaFai Lau 
159e332bc67SEric W. Biederman 			if (rt_idev->dev == dev) {
1608d0b94afSMartin KaFai Lau 				rt->rt6i_idev = in6_dev_get(loopback_dev);
1618d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
1628d0b94afSMartin KaFai Lau 			}
1638d0b94afSMartin KaFai Lau 
164e332bc67SEric W. Biederman 			if (rt_dev == dev) {
1658d0b94afSMartin KaFai Lau 				rt->dst.dev = loopback_dev;
1668d0b94afSMartin KaFai Lau 				dev_hold(rt->dst.dev);
1678d0b94afSMartin KaFai Lau 				dev_put(rt_dev);
1688d0b94afSMartin KaFai Lau 			}
1698d0b94afSMartin KaFai Lau 		}
1708d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1718d0b94afSMartin KaFai Lau 	}
1728d0b94afSMartin KaFai Lau }
1738d0b94afSMartin KaFai Lau 
174d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
175d52d3997SMartin KaFai Lau {
176d52d3997SMartin KaFai Lau 	return dst_metrics_write_ptr(rt->dst.from);
177d52d3997SMartin KaFai Lau }
178d52d3997SMartin KaFai Lau 
17906582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
18006582540SDavid S. Miller {
18106582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
18206582540SDavid S. Miller 
183d52d3997SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU)
184d52d3997SMartin KaFai Lau 		return rt6_pcpu_cow_metrics(rt);
185d52d3997SMartin KaFai Lau 	else if (rt->rt6i_flags & RTF_CACHE)
1864b32b5adSMartin KaFai Lau 		return NULL;
1874b32b5adSMartin KaFai Lau 	else
1883b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
18906582540SDavid S. Miller }
19006582540SDavid S. Miller 
191f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
192f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
193f894cbf8SDavid S. Miller 					     const void *daddr)
19439232973SDavid S. Miller {
19539232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
19639232973SDavid S. Miller 
197a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
19839232973SDavid S. Miller 		return (const void *) p;
199f894cbf8SDavid S. Miller 	else if (skb)
200f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
20139232973SDavid S. Miller 	return daddr;
20239232973SDavid S. Miller }
20339232973SDavid S. Miller 
204f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
205f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
206f894cbf8SDavid S. Miller 					  const void *daddr)
207d3aaeb38SDavid S. Miller {
20839232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
20939232973SDavid S. Miller 	struct neighbour *n;
21039232973SDavid S. Miller 
211f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
2128e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
213f83c7790SDavid S. Miller 	if (n)
214f83c7790SDavid S. Miller 		return n;
215f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
216f83c7790SDavid S. Miller }
217f83c7790SDavid S. Miller 
2189a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2191da177e4SLinus Torvalds 	.family			=	AF_INET6,
2201da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2211da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2221da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2230dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
224ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
22506582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
2261da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2271da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2281da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2291da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2301da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2316e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2329f8955ccSEric W. Biederman 	.local_out		=	__ip6_local_out,
233d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
2341da177e4SLinus Torvalds };
2351da177e4SLinus Torvalds 
236ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
237ec831ea7SRoland Dreier {
238618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
239618f9bc7SSteffen Klassert 
240618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
241ec831ea7SRoland Dreier }
242ec831ea7SRoland Dreier 
2436700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2446700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
24514e50e57SDavid S. Miller {
24614e50e57SDavid S. Miller }
24714e50e57SDavid S. Miller 
2486700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2496700c270SDavid S. Miller 				      struct sk_buff *skb)
250b587ee3bSDavid S. Miller {
251b587ee3bSDavid S. Miller }
252b587ee3bSDavid S. Miller 
25314e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
25414e50e57SDavid S. Miller 	.family			=	AF_INET6,
25514e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
25614e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
257ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
258214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
25914e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
260b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2610a1f5962SMartin KaFai Lau 	.cow_metrics		=	dst_cow_metrics_generic,
262d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
26314e50e57SDavid S. Miller };
26414e50e57SDavid S. Miller 
26562fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
26614edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
26762fa8a84SDavid S. Miller };
26862fa8a84SDavid S. Miller 
269fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2701da177e4SLinus Torvalds 	.dst = {
2711da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2721da177e4SLinus Torvalds 		.__use		= 1,
2732c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2741da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2751da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2761da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2771da177e4SLinus Torvalds 	},
2781da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2794f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2801da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2811da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2821da177e4SLinus Torvalds };
2831da177e4SLinus Torvalds 
284101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
285101367c2SThomas Graf 
286fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
287101367c2SThomas Graf 	.dst = {
288101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
289101367c2SThomas Graf 		.__use		= 1,
2902c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
291101367c2SThomas Graf 		.error		= -EACCES,
2929ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2939ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
294101367c2SThomas Graf 	},
295101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2964f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
297101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
298101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
299101367c2SThomas Graf };
300101367c2SThomas Graf 
301fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
302101367c2SThomas Graf 	.dst = {
303101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
304101367c2SThomas Graf 		.__use		= 1,
3052c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
306101367c2SThomas Graf 		.error		= -EINVAL,
307352e512cSHerbert Xu 		.input		= dst_discard,
308ede2059dSEric W. Biederman 		.output		= dst_discard_out,
309101367c2SThomas Graf 	},
310101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3114f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
312101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
313101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
314101367c2SThomas Graf };
315101367c2SThomas Graf 
316101367c2SThomas Graf #endif
317101367c2SThomas Graf 
318ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt)
319ebfa45f0SMartin KaFai Lau {
320ebfa45f0SMartin KaFai Lau 	struct dst_entry *dst = &rt->dst;
321ebfa45f0SMartin KaFai Lau 
322ebfa45f0SMartin KaFai Lau 	memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
323ebfa45f0SMartin KaFai Lau 	INIT_LIST_HEAD(&rt->rt6i_siblings);
324ebfa45f0SMartin KaFai Lau 	INIT_LIST_HEAD(&rt->rt6i_uncached);
325ebfa45f0SMartin KaFai Lau }
326ebfa45f0SMartin KaFai Lau 
3271da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
328d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net,
329957c665fSDavid S. Miller 					struct net_device *dev,
330ad706862SMartin KaFai Lau 					int flags)
3311da177e4SLinus Torvalds {
33297bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3336f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
334cf911662SDavid S. Miller 
335ebfa45f0SMartin KaFai Lau 	if (rt)
336ebfa45f0SMartin KaFai Lau 		rt6_info_init(rt);
3378104891bSSteffen Klassert 
338cf911662SDavid S. Miller 	return rt;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
3419ab179d8SDavid Ahern struct rt6_info *ip6_dst_alloc(struct net *net,
342d52d3997SMartin KaFai Lau 			       struct net_device *dev,
343ad706862SMartin KaFai Lau 			       int flags)
344d52d3997SMartin KaFai Lau {
345ad706862SMartin KaFai Lau 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
346d52d3997SMartin KaFai Lau 
347d52d3997SMartin KaFai Lau 	if (rt) {
348d52d3997SMartin KaFai Lau 		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
349d52d3997SMartin KaFai Lau 		if (rt->rt6i_pcpu) {
350d52d3997SMartin KaFai Lau 			int cpu;
351d52d3997SMartin KaFai Lau 
352d52d3997SMartin KaFai Lau 			for_each_possible_cpu(cpu) {
353d52d3997SMartin KaFai Lau 				struct rt6_info **p;
354d52d3997SMartin KaFai Lau 
355d52d3997SMartin KaFai Lau 				p = per_cpu_ptr(rt->rt6i_pcpu, cpu);
356d52d3997SMartin KaFai Lau 				/* no one shares rt */
357d52d3997SMartin KaFai Lau 				*p =  NULL;
358d52d3997SMartin KaFai Lau 			}
359d52d3997SMartin KaFai Lau 		} else {
360d52d3997SMartin KaFai Lau 			dst_destroy((struct dst_entry *)rt);
361d52d3997SMartin KaFai Lau 			return NULL;
362d52d3997SMartin KaFai Lau 		}
363d52d3997SMartin KaFai Lau 	}
364d52d3997SMartin KaFai Lau 
365d52d3997SMartin KaFai Lau 	return rt;
366d52d3997SMartin KaFai Lau }
3679ab179d8SDavid Ahern EXPORT_SYMBOL(ip6_dst_alloc);
368d52d3997SMartin KaFai Lau 
3691da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3701da177e4SLinus Torvalds {
3711da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
372ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
3738d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3741da177e4SLinus Torvalds 
3758e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
376d52d3997SMartin KaFai Lau 	free_percpu(rt->rt6i_pcpu);
3778d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
3788d0b94afSMartin KaFai Lau 
3798d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
38038308473SDavid S. Miller 	if (idev) {
3811da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3821da177e4SLinus Torvalds 		in6_dev_put(idev);
3831da177e4SLinus Torvalds 	}
3841716a961SGao feng 
385ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
386ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
387b3419363SDavid S. Miller }
388b3419363SDavid S. Miller 
3891da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3901da177e4SLinus Torvalds 			   int how)
3911da177e4SLinus Torvalds {
3921da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3931da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3945a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
395c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3961da177e4SLinus Torvalds 
39797cac082SDavid S. Miller 	if (dev != loopback_dev) {
39897cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3995a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
4005a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
40138308473SDavid S. Miller 			if (loopback_idev) {
4021da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
4031da177e4SLinus Torvalds 				in6_dev_put(idev);
4041da177e4SLinus Torvalds 			}
4051da177e4SLinus Torvalds 		}
40697cac082SDavid S. Miller 	}
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds 
4095973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt)
4105973fb1eSMartin KaFai Lau {
4115973fb1eSMartin KaFai Lau 	if (rt->rt6i_flags & RTF_EXPIRES)
4125973fb1eSMartin KaFai Lau 		return time_after(jiffies, rt->dst.expires);
4135973fb1eSMartin KaFai Lau 	else
4145973fb1eSMartin KaFai Lau 		return false;
4155973fb1eSMartin KaFai Lau }
4165973fb1eSMartin KaFai Lau 
417a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4181da177e4SLinus Torvalds {
4191716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4201716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
421a50feda5SEric Dumazet 			return true;
4221716a961SGao feng 	} else if (rt->dst.from) {
4233fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
4241716a961SGao feng 	}
425a50feda5SEric Dumazet 	return false;
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds 
42851ebd318SNicolas Dichtel /* Multipath route selection:
42951ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
43051ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
43151ebd318SNicolas Dichtel  */
43251ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
43351ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
43451ebd318SNicolas Dichtel {
435644d0e65STom Herbert 	return get_hash_from_flowi6(fl6) % candidate_count;
43651ebd318SNicolas Dichtel }
43751ebd318SNicolas Dichtel 
43851ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
43952bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
44052bd4c0cSNicolas Dichtel 					     int strict)
44151ebd318SNicolas Dichtel {
44251ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
44351ebd318SNicolas Dichtel 	int route_choosen;
44451ebd318SNicolas Dichtel 
44551ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
44651ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
44751ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
44851ebd318SNicolas Dichtel 	 */
44951ebd318SNicolas Dichtel 	if (route_choosen)
45051ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
45151ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
45251ebd318SNicolas Dichtel 			route_choosen--;
45351ebd318SNicolas Dichtel 			if (route_choosen == 0) {
45452bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
45552bd4c0cSNicolas Dichtel 					break;
45651ebd318SNicolas Dichtel 				match = sibling;
45751ebd318SNicolas Dichtel 				break;
45851ebd318SNicolas Dichtel 			}
45951ebd318SNicolas Dichtel 		}
46051ebd318SNicolas Dichtel 	return match;
46151ebd318SNicolas Dichtel }
46251ebd318SNicolas Dichtel 
4631da177e4SLinus Torvalds /*
464c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4651da177e4SLinus Torvalds  */
4661da177e4SLinus Torvalds 
4678ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4688ed67789SDaniel Lezcano 						    struct rt6_info *rt,
469b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4701da177e4SLinus Torvalds 						    int oif,
471d420895eSYOSHIFUJI Hideaki 						    int flags)
4721da177e4SLinus Torvalds {
4731da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4741da177e4SLinus Torvalds 	struct rt6_info *sprt;
4751da177e4SLinus Torvalds 
476dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
477dd3abc4eSYOSHIFUJI Hideaki 		goto out;
478dd3abc4eSYOSHIFUJI Hideaki 
479d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
480d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
481dd3abc4eSYOSHIFUJI Hideaki 
482dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4831da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4841da177e4SLinus Torvalds 				return sprt;
4851da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
48638308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4871da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
48817fb0b2bSDavid Ahern 					if (flags & RT6_LOOKUP_F_IFACE)
4891da177e4SLinus Torvalds 						continue;
49017fb0b2bSDavid Ahern 					if (local &&
49117fb0b2bSDavid Ahern 					    local->rt6i_idev->dev->ifindex == oif)
4921da177e4SLinus Torvalds 						continue;
4931da177e4SLinus Torvalds 				}
4941da177e4SLinus Torvalds 				local = sprt;
4951da177e4SLinus Torvalds 			}
496dd3abc4eSYOSHIFUJI Hideaki 		} else {
497dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
498dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
499dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
500dd3abc4eSYOSHIFUJI Hideaki 		}
5011da177e4SLinus Torvalds 	}
5021da177e4SLinus Torvalds 
503dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
5041da177e4SLinus Torvalds 		if (local)
5051da177e4SLinus Torvalds 			return local;
5061da177e4SLinus Torvalds 
507d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
5088ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
5091da177e4SLinus Torvalds 	}
510dd3abc4eSYOSHIFUJI Hideaki out:
5111da177e4SLinus Torvalds 	return rt;
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds 
51427097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
515c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
516c2f17e82SHannes Frederic Sowa 	struct work_struct work;
517c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
518c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
519c2f17e82SHannes Frederic Sowa };
520c2f17e82SHannes Frederic Sowa 
521c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
522c2f17e82SHannes Frederic Sowa {
523c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
524c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
525c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
526c2f17e82SHannes Frederic Sowa 
527c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
528304d888bSNicolas Dichtel 	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL);
529c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
530662f5533SMichael Büsch 	kfree(work);
531c2f17e82SHannes Frederic Sowa }
532c2f17e82SHannes Frederic Sowa 
53327097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
53427097255SYOSHIFUJI Hideaki {
535990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
536f2c31e32SEric Dumazet 	struct neighbour *neigh;
53727097255SYOSHIFUJI Hideaki 	/*
53827097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
53927097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
54027097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
54127097255SYOSHIFUJI Hideaki 	 *
54227097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
54327097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
54427097255SYOSHIFUJI Hideaki 	 */
5452152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
546fdd6681dSAmerigo Wang 		return;
5472152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5482152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5492152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5508d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5518d6c31bfSMartin KaFai Lau 			goto out;
5528d6c31bfSMartin KaFai Lau 
553990edb42SMartin KaFai Lau 		work = NULL;
5542152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
555990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
556990edb42SMartin KaFai Lau 		    time_after(jiffies,
557990edb42SMartin KaFai Lau 			       neigh->updated +
558990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
559c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
560990edb42SMartin KaFai Lau 			if (work)
5617e980569SJiri Benc 				__neigh_set_probe_once(neigh);
562990edb42SMartin KaFai Lau 		}
563c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
564990edb42SMartin KaFai Lau 	} else {
565990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
566990edb42SMartin KaFai Lau 	}
567c2f17e82SHannes Frederic Sowa 
568c2f17e82SHannes Frederic Sowa 	if (work) {
569c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
570c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
571c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
572c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
573c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
574c2f17e82SHannes Frederic Sowa 	}
575990edb42SMartin KaFai Lau 
5768d6c31bfSMartin KaFai Lau out:
5772152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
578f2c31e32SEric Dumazet }
57927097255SYOSHIFUJI Hideaki #else
58027097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
58127097255SYOSHIFUJI Hideaki {
58227097255SYOSHIFUJI Hideaki }
58327097255SYOSHIFUJI Hideaki #endif
58427097255SYOSHIFUJI Hideaki 
5851da177e4SLinus Torvalds /*
586554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5871da177e4SLinus Torvalds  */
588b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5891da177e4SLinus Torvalds {
590d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
591161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
592554cfb7eSYOSHIFUJI Hideaki 		return 2;
593161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
594161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
595161980f4SDavid S. Miller 		return 1;
596554cfb7eSYOSHIFUJI Hideaki 	return 0;
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
599afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
6001da177e4SLinus Torvalds {
601f2c31e32SEric Dumazet 	struct neighbour *neigh;
602afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
603f2c31e32SEric Dumazet 
6044d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
6054d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
606afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
607145a3621SYOSHIFUJI Hideaki / 吉藤英明 
608145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
609145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
610145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
611145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
612554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
613afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
614398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
615a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
616afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6177e980569SJiri Benc 		else
6187e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
619398bcbebSYOSHIFUJI Hideaki #endif
620145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
621afc154e9SHannes Frederic Sowa 	} else {
622afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6237e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
624a5a81f0bSPaul Marks 	}
625145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
626145a3621SYOSHIFUJI Hideaki / 吉藤英明 
627a5a81f0bSPaul Marks 	return ret;
6281da177e4SLinus Torvalds }
6291da177e4SLinus Torvalds 
630554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
631554cfb7eSYOSHIFUJI Hideaki 			   int strict)
632554cfb7eSYOSHIFUJI Hideaki {
633a5a81f0bSPaul Marks 	int m;
6344d0c5911SYOSHIFUJI Hideaki 
6354d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
63677d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
637afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
638ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
639ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
640ebacaaa0SYOSHIFUJI Hideaki #endif
641afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
642afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
643afc154e9SHannes Frederic Sowa 		if (n < 0)
644afc154e9SHannes Frederic Sowa 			return n;
645afc154e9SHannes Frederic Sowa 	}
646554cfb7eSYOSHIFUJI Hideaki 	return m;
647554cfb7eSYOSHIFUJI Hideaki }
648554cfb7eSYOSHIFUJI Hideaki 
649f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
650afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
651afc154e9SHannes Frederic Sowa 				   bool *do_rr)
652554cfb7eSYOSHIFUJI Hideaki {
653554cfb7eSYOSHIFUJI Hideaki 	int m;
654afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
65535103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
65635103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
65735103d11SAndy Gospodarek 
65835103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
65935103d11SAndy Gospodarek 	    idev->cnf.ignore_routes_with_linkdown)
66035103d11SAndy Gospodarek 		goto out;
661554cfb7eSYOSHIFUJI Hideaki 
662554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
663f11e6659SDavid S. Miller 		goto out;
664554cfb7eSYOSHIFUJI Hideaki 
665554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6667e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
667afc154e9SHannes Frederic Sowa 		match_do_rr = true;
668afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6697e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
670f11e6659SDavid S. Miller 		goto out;
6711da177e4SLinus Torvalds 	}
672f11e6659SDavid S. Miller 
673afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
674afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
675afc154e9SHannes Frederic Sowa 
6767e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
677afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
678afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
679afc154e9SHannes Frederic Sowa 		*mpri = m;
680afc154e9SHannes Frederic Sowa 		match = rt;
681afc154e9SHannes Frederic Sowa 	}
682f11e6659SDavid S. Miller out:
683f11e6659SDavid S. Miller 	return match;
6841da177e4SLinus Torvalds }
6851da177e4SLinus Torvalds 
686f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
687f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
688afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
689afc154e9SHannes Frederic Sowa 				     bool *do_rr)
690f11e6659SDavid S. Miller {
6919fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
692f11e6659SDavid S. Miller 	int mpri = -1;
693f11e6659SDavid S. Miller 
694f11e6659SDavid S. Miller 	match = NULL;
6959fbdcfafSSteffen Klassert 	cont = NULL;
6969fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
6979fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6989fbdcfafSSteffen Klassert 			cont = rt;
6999fbdcfafSSteffen Klassert 			break;
7009fbdcfafSSteffen Klassert 		}
7019fbdcfafSSteffen Klassert 
702afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7039fbdcfafSSteffen Klassert 	}
7049fbdcfafSSteffen Klassert 
7059fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
7069fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7079fbdcfafSSteffen Klassert 			cont = rt;
7089fbdcfafSSteffen Klassert 			break;
7099fbdcfafSSteffen Klassert 		}
7109fbdcfafSSteffen Klassert 
7119fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7129fbdcfafSSteffen Klassert 	}
7139fbdcfafSSteffen Klassert 
7149fbdcfafSSteffen Klassert 	if (match || !cont)
7159fbdcfafSSteffen Klassert 		return match;
7169fbdcfafSSteffen Klassert 
7179fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
718afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
719f11e6659SDavid S. Miller 
720f11e6659SDavid S. Miller 	return match;
721f11e6659SDavid S. Miller }
722f11e6659SDavid S. Miller 
723f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
724f11e6659SDavid S. Miller {
725f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7268ed67789SDaniel Lezcano 	struct net *net;
727afc154e9SHannes Frederic Sowa 	bool do_rr = false;
728f11e6659SDavid S. Miller 
729f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
730f11e6659SDavid S. Miller 	if (!rt0)
731f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
732f11e6659SDavid S. Miller 
733afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
734afc154e9SHannes Frederic Sowa 			     &do_rr);
735f11e6659SDavid S. Miller 
736afc154e9SHannes Frederic Sowa 	if (do_rr) {
737d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
738f11e6659SDavid S. Miller 
739554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
740f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
741f11e6659SDavid S. Miller 			next = fn->leaf;
742f11e6659SDavid S. Miller 
743f11e6659SDavid S. Miller 		if (next != rt0)
744f11e6659SDavid S. Miller 			fn->rr_ptr = next;
745554cfb7eSYOSHIFUJI Hideaki 	}
746554cfb7eSYOSHIFUJI Hideaki 
747d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
748a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7491da177e4SLinus Torvalds }
7501da177e4SLinus Torvalds 
7518b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7528b9df265SMartin KaFai Lau {
7538b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7548b9df265SMartin KaFai Lau }
7558b9df265SMartin KaFai Lau 
75670ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
75770ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
758b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
75970ceb4f5SYOSHIFUJI Hideaki {
760c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
76170ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
76270ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
76370ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7644bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
76570ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
76670ceb4f5SYOSHIFUJI Hideaki 
76770ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
76870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
76970ceb4f5SYOSHIFUJI Hideaki 	}
77070ceb4f5SYOSHIFUJI Hideaki 
77170ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
77270ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
77370ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
77470ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
77570ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
77670ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
77770ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
77870ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
77970ceb4f5SYOSHIFUJI Hideaki 		}
78070ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
78170ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
78270ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
78370ceb4f5SYOSHIFUJI Hideaki 		}
78470ceb4f5SYOSHIFUJI Hideaki 	}
78570ceb4f5SYOSHIFUJI Hideaki 
78670ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
78770ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
7883933fc95SJens Rosenboom 		return -EINVAL;
78970ceb4f5SYOSHIFUJI Hideaki 
7904bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
79170ceb4f5SYOSHIFUJI Hideaki 
79270ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
79370ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
79470ceb4f5SYOSHIFUJI Hideaki 	else {
79570ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
79670ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
79770ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
79870ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
79970ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
80070ceb4f5SYOSHIFUJI Hideaki 	}
80170ceb4f5SYOSHIFUJI Hideaki 
802f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
803f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
804f104a567SDuan Jiong 	else
805f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
806f104a567SDuan Jiong 					gwaddr, dev->ifindex);
80770ceb4f5SYOSHIFUJI Hideaki 
80870ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
809e0a1ad73SThomas Graf 		ip6_del_rt(rt);
81070ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
81170ceb4f5SYOSHIFUJI Hideaki 	}
81270ceb4f5SYOSHIFUJI Hideaki 
81370ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
814efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
81570ceb4f5SYOSHIFUJI Hideaki 					pref);
81670ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
81770ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
81870ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
81970ceb4f5SYOSHIFUJI Hideaki 
82070ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8211716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8221716a961SGao feng 			rt6_clean_expires(rt);
8231716a961SGao feng 		else
8241716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8251716a961SGao feng 
82694e187c0SAmerigo Wang 		ip6_rt_put(rt);
82770ceb4f5SYOSHIFUJI Hideaki 	}
82870ceb4f5SYOSHIFUJI Hideaki 	return 0;
82970ceb4f5SYOSHIFUJI Hideaki }
83070ceb4f5SYOSHIFUJI Hideaki #endif
83170ceb4f5SYOSHIFUJI Hideaki 
832a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
833a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
834a3c00e46SMartin KaFai Lau {
835a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
836a3c00e46SMartin KaFai Lau 	while (1) {
837a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
838a3c00e46SMartin KaFai Lau 			return NULL;
839a3c00e46SMartin KaFai Lau 		pn = fn->parent;
840a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
841a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
842a3c00e46SMartin KaFai Lau 		else
843a3c00e46SMartin KaFai Lau 			fn = pn;
844a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
845a3c00e46SMartin KaFai Lau 			return fn;
846a3c00e46SMartin KaFai Lau 	}
847a3c00e46SMartin KaFai Lau }
848c71099acSThomas Graf 
8498ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8508ed67789SDaniel Lezcano 					     struct fib6_table *table,
8514c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8521da177e4SLinus Torvalds {
8531da177e4SLinus Torvalds 	struct fib6_node *fn;
8541da177e4SLinus Torvalds 	struct rt6_info *rt;
8551da177e4SLinus Torvalds 
856c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8574c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
858c71099acSThomas Graf restart:
859c71099acSThomas Graf 	rt = fn->leaf;
8604c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
86151ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
86252bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
863a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
864a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
865a3c00e46SMartin KaFai Lau 		if (fn)
866a3c00e46SMartin KaFai Lau 			goto restart;
867a3c00e46SMartin KaFai Lau 	}
868d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
869c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
870b811580dSDavid Ahern 
871b811580dSDavid Ahern 	trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
872b811580dSDavid Ahern 
8731da177e4SLinus Torvalds 	return rt;
874c71099acSThomas Graf 
875c71099acSThomas Graf }
876c71099acSThomas Graf 
877ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
878ea6e574eSFlorian Westphal 				    int flags)
879ea6e574eSFlorian Westphal {
880ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
881ea6e574eSFlorian Westphal }
882ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
883ea6e574eSFlorian Westphal 
8849acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8859acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
886c71099acSThomas Graf {
8874c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8884c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8894c9483b2SDavid S. Miller 		.daddr = *daddr,
890c71099acSThomas Graf 	};
891c71099acSThomas Graf 	struct dst_entry *dst;
89277d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
893c71099acSThomas Graf 
894adaa70bbSThomas Graf 	if (saddr) {
8954c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
896adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
897adaa70bbSThomas Graf 	}
898adaa70bbSThomas Graf 
8994c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
900c71099acSThomas Graf 	if (dst->error == 0)
901c71099acSThomas Graf 		return (struct rt6_info *) dst;
902c71099acSThomas Graf 
903c71099acSThomas Graf 	dst_release(dst);
904c71099acSThomas Graf 
9051da177e4SLinus Torvalds 	return NULL;
9061da177e4SLinus Torvalds }
9077159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
9087159039aSYOSHIFUJI Hideaki 
909c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
9101da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
9111da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
9121da177e4SLinus Torvalds    be destroyed.
9131da177e4SLinus Torvalds  */
9141da177e4SLinus Torvalds 
915e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
916e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9171da177e4SLinus Torvalds {
9181da177e4SLinus Torvalds 	int err;
919c71099acSThomas Graf 	struct fib6_table *table;
9201da177e4SLinus Torvalds 
921c71099acSThomas Graf 	table = rt->rt6i_table;
922c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
923e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
924c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9251da177e4SLinus Torvalds 
9261da177e4SLinus Torvalds 	return err;
9271da177e4SLinus Torvalds }
9281da177e4SLinus Torvalds 
92940e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
93040e22e8fSThomas Graf {
931e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
932e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
933e715b6d3SFlorian Westphal 
934e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
93540e22e8fSThomas Graf }
93640e22e8fSThomas Graf 
9378b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
93821efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
939b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9401da177e4SLinus Torvalds {
9411da177e4SLinus Torvalds 	struct rt6_info *rt;
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds 	/*
9441da177e4SLinus Torvalds 	 *	Clone the route.
9451da177e4SLinus Torvalds 	 */
9461da177e4SLinus Torvalds 
947d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
94883a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9491da177e4SLinus Torvalds 
950ad706862SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0);
95183a09abdSMartin KaFai Lau 
95283a09abdSMartin KaFai Lau 	if (!rt)
95383a09abdSMartin KaFai Lau 		return NULL;
95483a09abdSMartin KaFai Lau 
95583a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9568b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
95783a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
95883a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
95983a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
96083a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9618b9df265SMartin KaFai Lau 
9628b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
963bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
96421efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
96558c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9661da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9671da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9684e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9691da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9701da177e4SLinus Torvalds 		}
9711da177e4SLinus Torvalds #endif
97295a9a5baSYOSHIFUJI Hideaki 	}
97395a9a5baSYOSHIFUJI Hideaki 
974299d9939SYOSHIFUJI Hideaki 	return rt;
975299d9939SYOSHIFUJI Hideaki }
976299d9939SYOSHIFUJI Hideaki 
977d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
978d52d3997SMartin KaFai Lau {
979d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
980d52d3997SMartin KaFai Lau 
981d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
982ad706862SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags);
983d52d3997SMartin KaFai Lau 
984d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
985d52d3997SMartin KaFai Lau 		return NULL;
986d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
987d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
988d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
989d52d3997SMartin KaFai Lau 	return pcpu_rt;
990d52d3997SMartin KaFai Lau }
991d52d3997SMartin KaFai Lau 
992d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
993d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
994d52d3997SMartin KaFai Lau {
995a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, **p;
996d52d3997SMartin KaFai Lau 
997d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
998d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
999d52d3997SMartin KaFai Lau 
1000a73e4195SMartin KaFai Lau 	if (pcpu_rt) {
1001a73e4195SMartin KaFai Lau 		dst_hold(&pcpu_rt->dst);
1002a73e4195SMartin KaFai Lau 		rt6_dst_from_metrics_check(pcpu_rt);
1003a73e4195SMartin KaFai Lau 	}
1004a73e4195SMartin KaFai Lau 	return pcpu_rt;
1005a73e4195SMartin KaFai Lau }
1006a73e4195SMartin KaFai Lau 
1007a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
1008a73e4195SMartin KaFai Lau {
10099c7370a1SMartin KaFai Lau 	struct fib6_table *table = rt->rt6i_table;
1010a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
1011d52d3997SMartin KaFai Lau 
1012d52d3997SMartin KaFai Lau 	pcpu_rt = ip6_rt_pcpu_alloc(rt);
1013d52d3997SMartin KaFai Lau 	if (!pcpu_rt) {
1014d52d3997SMartin KaFai Lau 		struct net *net = dev_net(rt->dst.dev);
1015d52d3997SMartin KaFai Lau 
10169c7370a1SMartin KaFai Lau 		dst_hold(&net->ipv6.ip6_null_entry->dst);
10179c7370a1SMartin KaFai Lau 		return net->ipv6.ip6_null_entry;
1018d52d3997SMartin KaFai Lau 	}
1019d52d3997SMartin KaFai Lau 
10209c7370a1SMartin KaFai Lau 	read_lock_bh(&table->tb6_lock);
10219c7370a1SMartin KaFai Lau 	if (rt->rt6i_pcpu) {
1022a73e4195SMartin KaFai Lau 		p = this_cpu_ptr(rt->rt6i_pcpu);
1023d52d3997SMartin KaFai Lau 		prev = cmpxchg(p, NULL, pcpu_rt);
1024d52d3997SMartin KaFai Lau 		if (prev) {
1025d52d3997SMartin KaFai Lau 			/* If someone did it before us, return prev instead */
1026d52d3997SMartin KaFai Lau 			dst_destroy(&pcpu_rt->dst);
1027d52d3997SMartin KaFai Lau 			pcpu_rt = prev;
1028d52d3997SMartin KaFai Lau 		}
10299c7370a1SMartin KaFai Lau 	} else {
10309c7370a1SMartin KaFai Lau 		/* rt has been removed from the fib6 tree
10319c7370a1SMartin KaFai Lau 		 * before we have a chance to acquire the read_lock.
10329c7370a1SMartin KaFai Lau 		 * In this case, don't brother to create a pcpu rt
10339c7370a1SMartin KaFai Lau 		 * since rt is going away anyway.  The next
10349c7370a1SMartin KaFai Lau 		 * dst_check() will trigger a re-lookup.
10359c7370a1SMartin KaFai Lau 		 */
10369c7370a1SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
10379c7370a1SMartin KaFai Lau 		pcpu_rt = rt;
10389c7370a1SMartin KaFai Lau 	}
1039d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1040d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
10419c7370a1SMartin KaFai Lau 	read_unlock_bh(&table->tb6_lock);
1042d52d3997SMartin KaFai Lau 	return pcpu_rt;
1043d52d3997SMartin KaFai Lau }
1044d52d3997SMartin KaFai Lau 
10458ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
10464c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
10471da177e4SLinus Torvalds {
1048367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
104945e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1050c71099acSThomas Graf 	int strict = 0;
10511da177e4SLinus Torvalds 
105277d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1053367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1054367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10551da177e4SLinus Torvalds 
1056c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10571da177e4SLinus Torvalds 
10584c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1059367efcb9SMartin KaFai Lau 	saved_fn = fn;
10601da177e4SLinus Torvalds 
1061ca254490SDavid Ahern 	if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1062ca254490SDavid Ahern 		oif = 0;
1063ca254490SDavid Ahern 
1064a3c00e46SMartin KaFai Lau redo_rt6_select:
1065367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
106652bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1067367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1068a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1069a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1070a3c00e46SMartin KaFai Lau 		if (fn)
1071a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1072367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1073367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1074367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1075367efcb9SMartin KaFai Lau 			fn = saved_fn;
1076367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1077367efcb9SMartin KaFai Lau 		}
1078a3c00e46SMartin KaFai Lau 	}
1079a3c00e46SMartin KaFai Lau 
1080d52d3997SMartin KaFai Lau 
1081d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10823da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1083c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10841da177e4SLinus Torvalds 
1085d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1086b811580dSDavid Ahern 
1087b811580dSDavid Ahern 		trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
1088d52d3997SMartin KaFai Lau 		return rt;
10893da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10903da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10913da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10923da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10933da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10943da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
10953da59bd9SMartin KaFai Lau 		 */
1096c71099acSThomas Graf 
10973da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
10983da59bd9SMartin KaFai Lau 
1099d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1100d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1101d52d3997SMartin KaFai Lau 
11023da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
11033da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
11043da59bd9SMartin KaFai Lau 
11053da59bd9SMartin KaFai Lau 		if (uncached_rt)
11068d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
11073da59bd9SMartin KaFai Lau 		else
11083da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1109d52d3997SMartin KaFai Lau 
11103da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
1111b811580dSDavid Ahern 
1112b811580dSDavid Ahern 		trace_fib6_table_lookup(net, uncached_rt, table->tb6_id, fl6);
11133da59bd9SMartin KaFai Lau 		return uncached_rt;
11143da59bd9SMartin KaFai Lau 
1115d52d3997SMartin KaFai Lau 	} else {
1116d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1117d52d3997SMartin KaFai Lau 
1118d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1119d52d3997SMartin KaFai Lau 
1120d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1121d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1122d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1123d52d3997SMartin KaFai Lau 
11249c7370a1SMartin KaFai Lau 		if (pcpu_rt) {
1125d52d3997SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11269c7370a1SMartin KaFai Lau 		} else {
11279c7370a1SMartin KaFai Lau 			/* We have to do the read_unlock first
11289c7370a1SMartin KaFai Lau 			 * because rt6_make_pcpu_route() may trigger
11299c7370a1SMartin KaFai Lau 			 * ip6_dst_gc() which will take the write_lock.
11309c7370a1SMartin KaFai Lau 			 */
11319c7370a1SMartin KaFai Lau 			dst_hold(&rt->dst);
11329c7370a1SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11339c7370a1SMartin KaFai Lau 			pcpu_rt = rt6_make_pcpu_route(rt);
11349c7370a1SMartin KaFai Lau 			dst_release(&rt->dst);
11359c7370a1SMartin KaFai Lau 		}
1136d52d3997SMartin KaFai Lau 
1137b811580dSDavid Ahern 		trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6);
1138d52d3997SMartin KaFai Lau 		return pcpu_rt;
11399c7370a1SMartin KaFai Lau 
1140d52d3997SMartin KaFai Lau 	}
1141c71099acSThomas Graf }
1142c71099acSThomas Graf 
11438ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11444c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11454acad72dSPavel Emelyanov {
11464c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11474acad72dSPavel Emelyanov }
11484acad72dSPavel Emelyanov 
114972331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
115072331bc0SShmulik Ladkani 						struct net_device *dev,
115172331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
115272331bc0SShmulik Ladkani {
115372331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
115472331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
115572331bc0SShmulik Ladkani 
115672331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
115772331bc0SShmulik Ladkani }
115872331bc0SShmulik Ladkani 
1159c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1160c71099acSThomas Graf {
1161b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1162c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1163adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1164904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
11654c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
1166ca254490SDavid Ahern 		.flowi6_iif = l3mdev_fib_oif(skb->dev),
11674c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11684c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11696502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11704c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11714c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1172c71099acSThomas Graf 	};
1173adaa70bbSThomas Graf 
1174904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
117546fa062aSJiri Benc 	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
1176904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
117706e9d040SJiri Benc 	skb_dst_drop(skb);
117872331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1179c71099acSThomas Graf }
1180c71099acSThomas Graf 
11818ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11824c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1183c71099acSThomas Graf {
11844c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1185c71099acSThomas Graf }
1186c71099acSThomas Graf 
11876f21c96aSPaolo Abeni struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
11886f21c96aSPaolo Abeni 					 struct flowi6 *fl6, int flags)
1189c71099acSThomas Graf {
1190ca254490SDavid Ahern 	struct dst_entry *dst;
1191d46a9d67SDavid Ahern 	bool any_src;
1192c71099acSThomas Graf 
11934a65896fSDavid Ahern 	dst = l3mdev_get_rt6_dst(net, fl6);
1194ca254490SDavid Ahern 	if (dst)
1195ca254490SDavid Ahern 		return dst;
1196ca254490SDavid Ahern 
11971fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
11984dc27d1cSDavid McCullough 
1199d46a9d67SDavid Ahern 	any_src = ipv6_addr_any(&fl6->saddr);
1200741a11d9SDavid Ahern 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
1201d46a9d67SDavid Ahern 	    (fl6->flowi6_oif && any_src))
120277d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1203c71099acSThomas Graf 
1204d46a9d67SDavid Ahern 	if (!any_src)
1205adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
12060c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
12070c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1208adaa70bbSThomas Graf 
12094c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
12101da177e4SLinus Torvalds }
12116f21c96aSPaolo Abeni EXPORT_SYMBOL_GPL(ip6_route_output_flags);
12121da177e4SLinus Torvalds 
12132774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
121414e50e57SDavid S. Miller {
12155c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
121614e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
121714e50e57SDavid S. Miller 
1218f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
121914e50e57SDavid S. Miller 	if (rt) {
12200a1f5962SMartin KaFai Lau 		rt6_info_init(rt);
12210a1f5962SMartin KaFai Lau 
1222d8d1f30bSChangli Gao 		new = &rt->dst;
122314e50e57SDavid S. Miller 		new->__use = 1;
1224352e512cSHerbert Xu 		new->input = dst_discard;
1225ede2059dSEric W. Biederman 		new->output = dst_discard_out;
122614e50e57SDavid S. Miller 
1227defb3519SDavid S. Miller 		dst_copy_metrics(new, &ort->dst);
122814e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
122914e50e57SDavid S. Miller 		if (rt->rt6i_idev)
123014e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
123114e50e57SDavid S. Miller 
12324e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
12330a1f5962SMartin KaFai Lau 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
123414e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
123514e50e57SDavid S. Miller 
123614e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
123714e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
123814e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
123914e50e57SDavid S. Miller #endif
124014e50e57SDavid S. Miller 
124114e50e57SDavid S. Miller 		dst_free(new);
124214e50e57SDavid S. Miller 	}
124314e50e57SDavid S. Miller 
124469ead7afSDavid S. Miller 	dst_release(dst_orig);
124569ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
124614e50e57SDavid S. Miller }
124714e50e57SDavid S. Miller 
12481da177e4SLinus Torvalds /*
12491da177e4SLinus Torvalds  *	Destination cache support functions
12501da177e4SLinus Torvalds  */
12511da177e4SLinus Torvalds 
12524b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12534b32b5adSMartin KaFai Lau {
12544b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12554b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12564b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12574b32b5adSMartin KaFai Lau }
12584b32b5adSMartin KaFai Lau 
12593da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12603da59bd9SMartin KaFai Lau {
12613da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12623da59bd9SMartin KaFai Lau 		return NULL;
12633da59bd9SMartin KaFai Lau 
12643da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12653da59bd9SMartin KaFai Lau 		return NULL;
12663da59bd9SMartin KaFai Lau 
12673da59bd9SMartin KaFai Lau 	return &rt->dst;
12683da59bd9SMartin KaFai Lau }
12693da59bd9SMartin KaFai Lau 
12703da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12713da59bd9SMartin KaFai Lau {
12725973fb1eSMartin KaFai Lau 	if (!__rt6_check_expired(rt) &&
12735973fb1eSMartin KaFai Lau 	    rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12743da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12753da59bd9SMartin KaFai Lau 		return &rt->dst;
12763da59bd9SMartin KaFai Lau 	else
12773da59bd9SMartin KaFai Lau 		return NULL;
12783da59bd9SMartin KaFai Lau }
12793da59bd9SMartin KaFai Lau 
12801da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12811da177e4SLinus Torvalds {
12821da177e4SLinus Torvalds 	struct rt6_info *rt;
12831da177e4SLinus Torvalds 
12841da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12851da177e4SLinus Torvalds 
12866f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12876f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12886f3118b5SNicolas Dichtel 	 * into this function always.
12896f3118b5SNicolas Dichtel 	 */
1290e3bc10bdSHannes Frederic Sowa 
12914b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
12924b32b5adSMartin KaFai Lau 
129302bcf4e0SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU ||
129402bcf4e0SMartin KaFai Lau 	    (unlikely(dst->flags & DST_NOCACHE) && rt->dst.from))
12953da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
12963da59bd9SMartin KaFai Lau 	else
12973da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
12981da177e4SLinus Torvalds }
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
13011da177e4SLinus Torvalds {
13021da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	if (rt) {
130554c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
130654c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1307e0a1ad73SThomas Graf 				ip6_del_rt(rt);
130854c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
13091da177e4SLinus Torvalds 			}
131054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
131154c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
131254c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
131354c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
131454c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
131554c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
13161da177e4SLinus Torvalds }
13171da177e4SLinus Torvalds 
13181da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
13191da177e4SLinus Torvalds {
13201da177e4SLinus Torvalds 	struct rt6_info *rt;
13211da177e4SLinus Torvalds 
13223ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
13231da177e4SLinus Torvalds 
1324adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
13251da177e4SLinus Torvalds 	if (rt) {
13261eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
13271eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
13288e3d5be7SMartin KaFai Lau 			ip6_del_rt(rt);
13291eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
13301da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
13311da177e4SLinus Torvalds 		}
13321da177e4SLinus Torvalds 	}
13331eb4f758SHannes Frederic Sowa }
13341da177e4SLinus Torvalds 
133545e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
133645e4fd26SMartin KaFai Lau {
133745e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
133845e4fd26SMartin KaFai Lau 
133945e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
134045e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
134145e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
134245e4fd26SMartin KaFai Lau }
134345e4fd26SMartin KaFai Lau 
13440d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
13450d3f6d29SMartin KaFai Lau {
13460d3f6d29SMartin KaFai Lau 	return !(rt->rt6i_flags & RTF_CACHE) &&
13470d3f6d29SMartin KaFai Lau 		(rt->rt6i_flags & RTF_PCPU || rt->rt6i_node);
13480d3f6d29SMartin KaFai Lau }
13490d3f6d29SMartin KaFai Lau 
135045e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
135145e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13521da177e4SLinus Torvalds {
13531da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13541da177e4SLinus Torvalds 
135545e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
135645e4fd26SMartin KaFai Lau 		return;
135745e4fd26SMartin KaFai Lau 
135881aded24SDavid S. Miller 	dst_confirm(dst);
135945e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
136045e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
136145e4fd26SMartin KaFai Lau 		return;
136281aded24SDavid S. Miller 
13630d3f6d29SMartin KaFai Lau 	if (!rt6_cache_allowed_for_pmtu(rt6)) {
136445e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
136545e4fd26SMartin KaFai Lau 	} else {
136645e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
136745e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13689d289715SHagen Paul Pfeifer 
136945e4fd26SMartin KaFai Lau 		if (iph) {
137045e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
137145e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
137245e4fd26SMartin KaFai Lau 		} else if (sk) {
137345e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
137445e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
137545e4fd26SMartin KaFai Lau 		} else {
137645e4fd26SMartin KaFai Lau 			return;
13771da177e4SLinus Torvalds 		}
137845e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
137945e4fd26SMartin KaFai Lau 		if (nrt6) {
138045e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
138145e4fd26SMartin KaFai Lau 
138245e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
138345e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
138445e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
138545e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
138645e4fd26SMartin KaFai Lau 			 */
138745e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
138845e4fd26SMartin KaFai Lau 		}
138945e4fd26SMartin KaFai Lau 	}
139045e4fd26SMartin KaFai Lau }
139145e4fd26SMartin KaFai Lau 
139245e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
139345e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
139445e4fd26SMartin KaFai Lau {
139545e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds 
139842ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
139942ae66c8SDavid S. Miller 		     int oif, u32 mark)
140081aded24SDavid S. Miller {
140181aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
140281aded24SDavid S. Miller 	struct dst_entry *dst;
140381aded24SDavid S. Miller 	struct flowi6 fl6;
140481aded24SDavid S. Miller 
140581aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
140681aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
14071b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
140881aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
140981aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
14106502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
141181aded24SDavid S. Miller 
141281aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
141381aded24SDavid S. Miller 	if (!dst->error)
141445e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
141581aded24SDavid S. Miller 	dst_release(dst);
141681aded24SDavid S. Miller }
141781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
141881aded24SDavid S. Miller 
141981aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
142081aded24SDavid S. Miller {
142133c162a9SMartin KaFai Lau 	struct dst_entry *dst;
142233c162a9SMartin KaFai Lau 
142381aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
142481aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
142533c162a9SMartin KaFai Lau 
142633c162a9SMartin KaFai Lau 	dst = __sk_dst_get(sk);
142733c162a9SMartin KaFai Lau 	if (!dst || !dst->obsolete ||
142833c162a9SMartin KaFai Lau 	    dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
142933c162a9SMartin KaFai Lau 		return;
143033c162a9SMartin KaFai Lau 
143133c162a9SMartin KaFai Lau 	bh_lock_sock(sk);
143233c162a9SMartin KaFai Lau 	if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
143333c162a9SMartin KaFai Lau 		ip6_datagram_dst_update(sk, false);
143433c162a9SMartin KaFai Lau 	bh_unlock_sock(sk);
143581aded24SDavid S. Miller }
143681aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
143781aded24SDavid S. Miller 
1438b55b76b2SDuan Jiong /* Handle redirects */
1439b55b76b2SDuan Jiong struct ip6rd_flowi {
1440b55b76b2SDuan Jiong 	struct flowi6 fl6;
1441b55b76b2SDuan Jiong 	struct in6_addr gateway;
1442b55b76b2SDuan Jiong };
1443b55b76b2SDuan Jiong 
1444b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1445b55b76b2SDuan Jiong 					     struct fib6_table *table,
1446b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1447b55b76b2SDuan Jiong 					     int flags)
1448b55b76b2SDuan Jiong {
1449b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1450b55b76b2SDuan Jiong 	struct rt6_info *rt;
1451b55b76b2SDuan Jiong 	struct fib6_node *fn;
1452b55b76b2SDuan Jiong 
1453b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1454b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1455b55b76b2SDuan Jiong 	 *
1456b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1457b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1458b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1459b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1460b55b76b2SDuan Jiong 	 * routes.
1461b55b76b2SDuan Jiong 	 */
1462b55b76b2SDuan Jiong 
1463b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1464b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1465b55b76b2SDuan Jiong restart:
1466b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1467b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1468b55b76b2SDuan Jiong 			continue;
1469b55b76b2SDuan Jiong 		if (rt->dst.error)
1470b55b76b2SDuan Jiong 			break;
1471b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1472b55b76b2SDuan Jiong 			continue;
1473b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1474b55b76b2SDuan Jiong 			continue;
1475b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1476b55b76b2SDuan Jiong 			continue;
1477b55b76b2SDuan Jiong 		break;
1478b55b76b2SDuan Jiong 	}
1479b55b76b2SDuan Jiong 
1480b55b76b2SDuan Jiong 	if (!rt)
1481b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1482b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1483b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1484b0a1ba59SMartin KaFai Lau 		goto out;
1485b0a1ba59SMartin KaFai Lau 	}
1486b0a1ba59SMartin KaFai Lau 
1487b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1488a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1489a3c00e46SMartin KaFai Lau 		if (fn)
1490a3c00e46SMartin KaFai Lau 			goto restart;
1491b55b76b2SDuan Jiong 	}
1492a3c00e46SMartin KaFai Lau 
1493b0a1ba59SMartin KaFai Lau out:
1494b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1495b55b76b2SDuan Jiong 
1496b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1497b55b76b2SDuan Jiong 
1498b811580dSDavid Ahern 	trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
1499b55b76b2SDuan Jiong 	return rt;
1500b55b76b2SDuan Jiong };
1501b55b76b2SDuan Jiong 
1502b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1503b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1504b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1505b55b76b2SDuan Jiong {
1506b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1507b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1508b55b76b2SDuan Jiong 
1509b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1510b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1511b55b76b2SDuan Jiong 
1512b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1513b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1514b55b76b2SDuan Jiong }
1515b55b76b2SDuan Jiong 
15163a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
15173a5ad2eeSDavid S. Miller {
15183a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
15193a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
15203a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
15213a5ad2eeSDavid S. Miller 
15223a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1523e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
15243a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
15253a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
15263a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
15273a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
15286502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
15293a5ad2eeSDavid S. Miller 
1530b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
15316700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
15323a5ad2eeSDavid S. Miller 	dst_release(dst);
15333a5ad2eeSDavid S. Miller }
15343a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
15353a5ad2eeSDavid S. Miller 
1536c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1537c92a59ecSDuan Jiong 			    u32 mark)
1538c92a59ecSDuan Jiong {
1539c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1540c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1541c92a59ecSDuan Jiong 	struct dst_entry *dst;
1542c92a59ecSDuan Jiong 	struct flowi6 fl6;
1543c92a59ecSDuan Jiong 
1544c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1545e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1546c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1547c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1548c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1549c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1550c92a59ecSDuan Jiong 
1551b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1552c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1553c92a59ecSDuan Jiong 	dst_release(dst);
1554c92a59ecSDuan Jiong }
1555c92a59ecSDuan Jiong 
15563a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
15573a5ad2eeSDavid S. Miller {
15583a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
15593a5ad2eeSDavid S. Miller }
15603a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15613a5ad2eeSDavid S. Miller 
15620dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15631da177e4SLinus Torvalds {
15640dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15650dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15660dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15670dbaee3bSDavid S. Miller 
15681da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15691da177e4SLinus Torvalds 
15705578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15715578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15721da177e4SLinus Torvalds 
15731da177e4SLinus Torvalds 	/*
15741da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15751da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15761da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15771da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15781da177e4SLinus Torvalds 	 */
15791da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15801da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15811da177e4SLinus Torvalds 	return mtu;
15821da177e4SLinus Torvalds }
15831da177e4SLinus Torvalds 
1584ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1585d33e4553SDavid S. Miller {
15864b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
15874b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1588d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1589618f9bc7SSteffen Klassert 
1590618f9bc7SSteffen Klassert 	if (mtu)
159130f78d8eSEric Dumazet 		goto out;
1592618f9bc7SSteffen Klassert 
15934b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
15944b32b5adSMartin KaFai Lau 	if (mtu)
15954b32b5adSMartin KaFai Lau 		goto out;
15964b32b5adSMartin KaFai Lau 
1597618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1598d33e4553SDavid S. Miller 
1599d33e4553SDavid S. Miller 	rcu_read_lock();
1600d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1601d33e4553SDavid S. Miller 	if (idev)
1602d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1603d33e4553SDavid S. Miller 	rcu_read_unlock();
1604d33e4553SDavid S. Miller 
160530f78d8eSEric Dumazet out:
160630f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1607d33e4553SDavid S. Miller }
1608d33e4553SDavid S. Miller 
16093b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
16103b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
16115d0bbeebSThomas Graf 
16123b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
161387a11578SDavid S. Miller 				  struct flowi6 *fl6)
16141da177e4SLinus Torvalds {
161587a11578SDavid S. Miller 	struct dst_entry *dst;
16161da177e4SLinus Torvalds 	struct rt6_info *rt;
16171da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1618c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
16191da177e4SLinus Torvalds 
162038308473SDavid S. Miller 	if (unlikely(!idev))
1621122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
16221da177e4SLinus Torvalds 
1623ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, dev, 0);
162438308473SDavid S. Miller 	if (unlikely(!rt)) {
16251da177e4SLinus Torvalds 		in6_dev_put(idev);
162687a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
16271da177e4SLinus Torvalds 		goto out;
16281da177e4SLinus Torvalds 	}
16291da177e4SLinus Torvalds 
16308e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
16318e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1632d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1633550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
163487a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
16358e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
16368e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
163714edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
16381da177e4SLinus Torvalds 
16393b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1640d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1641d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
16423b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16431da177e4SLinus Torvalds 
16445578689aSDaniel Lezcano 	fib6_force_start_gc(net);
16451da177e4SLinus Torvalds 
164687a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
164787a11578SDavid S. Miller 
16481da177e4SLinus Torvalds out:
164987a11578SDavid S. Miller 	return dst;
16501da177e4SLinus Torvalds }
16511da177e4SLinus Torvalds 
16523d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
16531da177e4SLinus Torvalds {
1654e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
16553d0f24a7SStephen Hemminger 	int more = 0;
16561da177e4SLinus Torvalds 
16573b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16583b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16595d0bbeebSThomas Graf 
16601da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16611da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16621da177e4SLinus Torvalds 			*pprev = dst->next;
16631da177e4SLinus Torvalds 			dst_free(dst);
16641da177e4SLinus Torvalds 		} else {
16651da177e4SLinus Torvalds 			pprev = &dst->next;
16663d0f24a7SStephen Hemminger 			++more;
16671da177e4SLinus Torvalds 		}
16681da177e4SLinus Torvalds 	}
16691da177e4SLinus Torvalds 
16703b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16715d0bbeebSThomas Graf 
16723d0f24a7SStephen Hemminger 	return more;
16731da177e4SLinus Torvalds }
16741da177e4SLinus Torvalds 
16751e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16761e493d19SDavid S. Miller 			    void *arg)
16771e493d19SDavid S. Miller {
16781e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16791e493d19SDavid S. Miller 
16801e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
16811e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
16821e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
16831e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
16841e493d19SDavid S. Miller 		if (func(rt, arg)) {
16851e493d19SDavid S. Miller 			*pprev = dst->next;
16861e493d19SDavid S. Miller 			dst_free(dst);
16871e493d19SDavid S. Miller 		} else {
16881e493d19SDavid S. Miller 			pprev = &dst->next;
16891e493d19SDavid S. Miller 		}
16901e493d19SDavid S. Miller 	}
16911e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
16921e493d19SDavid S. Miller }
16931e493d19SDavid S. Miller 
1694569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
16951da177e4SLinus Torvalds {
169686393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
16977019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
16987019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
16997019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
17007019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
17017019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1702fc66f95cSEric Dumazet 	int entries;
17031da177e4SLinus Torvalds 
1704fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
170549a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1706fc66f95cSEric Dumazet 	    entries <= rt_max_size)
17071da177e4SLinus Torvalds 		goto out;
17081da177e4SLinus Torvalds 
17096891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
171014956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1711fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1712fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
17137019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
17141da177e4SLinus Torvalds out:
17157019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1716fc66f95cSEric Dumazet 	return entries > rt_max_size;
17171da177e4SLinus Torvalds }
17181da177e4SLinus Torvalds 
1719e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1720e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1721e715b6d3SFlorian Westphal {
1722c3a8d947SDaniel Borkmann 	bool ecn_ca = false;
1723e715b6d3SFlorian Westphal 	struct nlattr *nla;
1724e715b6d3SFlorian Westphal 	int remaining;
1725e715b6d3SFlorian Westphal 	u32 *mp;
1726e715b6d3SFlorian Westphal 
172763159f29SIan Morris 	if (!cfg->fc_mx)
1728e715b6d3SFlorian Westphal 		return 0;
1729e715b6d3SFlorian Westphal 
1730e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1731e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1732e715b6d3SFlorian Westphal 		return -ENOMEM;
1733e715b6d3SFlorian Westphal 
1734e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1735e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1736ea697639SDaniel Borkmann 		u32 val;
1737ea697639SDaniel Borkmann 
17381bb14807SDaniel Borkmann 		if (!type)
17391bb14807SDaniel Borkmann 			continue;
1740e715b6d3SFlorian Westphal 		if (unlikely(type > RTAX_MAX))
1741e715b6d3SFlorian Westphal 			goto err;
17421bb14807SDaniel Borkmann 
1743ea697639SDaniel Borkmann 		if (type == RTAX_CC_ALGO) {
1744ea697639SDaniel Borkmann 			char tmp[TCP_CA_NAME_MAX];
1745e715b6d3SFlorian Westphal 
1746ea697639SDaniel Borkmann 			nla_strlcpy(tmp, nla, sizeof(tmp));
1747c3a8d947SDaniel Borkmann 			val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
1748ea697639SDaniel Borkmann 			if (val == TCP_CA_UNSPEC)
1749ea697639SDaniel Borkmann 				goto err;
1750ea697639SDaniel Borkmann 		} else {
1751ea697639SDaniel Borkmann 			val = nla_get_u32(nla);
1752ea697639SDaniel Borkmann 		}
1753626abd59SPaolo Abeni 		if (type == RTAX_HOPLIMIT && val > 255)
1754626abd59SPaolo Abeni 			val = 255;
1755b8d3e416SDaniel Borkmann 		if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
1756b8d3e416SDaniel Borkmann 			goto err;
1757ea697639SDaniel Borkmann 
1758ea697639SDaniel Borkmann 		mp[type - 1] = val;
1759e715b6d3SFlorian Westphal 		__set_bit(type - 1, mxc->mx_valid);
1760e715b6d3SFlorian Westphal 	}
1761e715b6d3SFlorian Westphal 
1762c3a8d947SDaniel Borkmann 	if (ecn_ca) {
1763c3a8d947SDaniel Borkmann 		__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
1764c3a8d947SDaniel Borkmann 		mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
1765c3a8d947SDaniel Borkmann 	}
1766e715b6d3SFlorian Westphal 
1767c3a8d947SDaniel Borkmann 	mxc->mx = mp;
1768e715b6d3SFlorian Westphal 	return 0;
1769e715b6d3SFlorian Westphal  err:
1770e715b6d3SFlorian Westphal 	kfree(mp);
1771e715b6d3SFlorian Westphal 	return -EINVAL;
1772e715b6d3SFlorian Westphal }
17731da177e4SLinus Torvalds 
17748c14586fSDavid Ahern static struct rt6_info *ip6_nh_lookup_table(struct net *net,
17758c14586fSDavid Ahern 					    struct fib6_config *cfg,
17768c14586fSDavid Ahern 					    const struct in6_addr *gw_addr)
17778c14586fSDavid Ahern {
17788c14586fSDavid Ahern 	struct flowi6 fl6 = {
17798c14586fSDavid Ahern 		.flowi6_oif = cfg->fc_ifindex,
17808c14586fSDavid Ahern 		.daddr = *gw_addr,
17818c14586fSDavid Ahern 		.saddr = cfg->fc_prefsrc,
17828c14586fSDavid Ahern 	};
17838c14586fSDavid Ahern 	struct fib6_table *table;
17848c14586fSDavid Ahern 	struct rt6_info *rt;
17858c14586fSDavid Ahern 	int flags = 0;
17868c14586fSDavid Ahern 
17878c14586fSDavid Ahern 	table = fib6_get_table(net, cfg->fc_table);
17888c14586fSDavid Ahern 	if (!table)
17898c14586fSDavid Ahern 		return NULL;
17908c14586fSDavid Ahern 
17918c14586fSDavid Ahern 	if (!ipv6_addr_any(&cfg->fc_prefsrc))
17928c14586fSDavid Ahern 		flags |= RT6_LOOKUP_F_HAS_SADDR;
17938c14586fSDavid Ahern 
17948c14586fSDavid Ahern 	rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags);
17958c14586fSDavid Ahern 
17968c14586fSDavid Ahern 	/* if table lookup failed, fall back to full lookup */
17978c14586fSDavid Ahern 	if (rt == net->ipv6.ip6_null_entry) {
17988c14586fSDavid Ahern 		ip6_rt_put(rt);
17998c14586fSDavid Ahern 		rt = NULL;
18008c14586fSDavid Ahern 	}
18018c14586fSDavid Ahern 
18028c14586fSDavid Ahern 	return rt;
18038c14586fSDavid Ahern }
18048c14586fSDavid Ahern 
18058c5b83f0SRoopa Prabhu static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
18061da177e4SLinus Torvalds {
18075578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
18081da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
18091da177e4SLinus Torvalds 	struct net_device *dev = NULL;
18101da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1811c71099acSThomas Graf 	struct fib6_table *table;
18121da177e4SLinus Torvalds 	int addr_type;
18138c5b83f0SRoopa Prabhu 	int err = -EINVAL;
18141da177e4SLinus Torvalds 
181586872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
18168c5b83f0SRoopa Prabhu 		goto out;
18171da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
181886872cb5SThomas Graf 	if (cfg->fc_src_len)
18198c5b83f0SRoopa Prabhu 		goto out;
18201da177e4SLinus Torvalds #endif
182186872cb5SThomas Graf 	if (cfg->fc_ifindex) {
18221da177e4SLinus Torvalds 		err = -ENODEV;
18235578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
18241da177e4SLinus Torvalds 		if (!dev)
18251da177e4SLinus Torvalds 			goto out;
18261da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
18271da177e4SLinus Torvalds 		if (!idev)
18281da177e4SLinus Torvalds 			goto out;
18291da177e4SLinus Torvalds 	}
18301da177e4SLinus Torvalds 
183186872cb5SThomas Graf 	if (cfg->fc_metric == 0)
183286872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
18331da177e4SLinus Torvalds 
1834c71099acSThomas Graf 	err = -ENOBUFS;
183538308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1836d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1837d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
183838308473SDavid S. Miller 		if (!table) {
1839f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1840d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1841d71314b4SMatti Vaittinen 		}
1842d71314b4SMatti Vaittinen 	} else {
1843d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1844d71314b4SMatti Vaittinen 	}
184538308473SDavid S. Miller 
184638308473SDavid S. Miller 	if (!table)
1847c71099acSThomas Graf 		goto out;
1848c71099acSThomas Graf 
1849ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, NULL,
1850ad706862SMartin KaFai Lau 			   (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT);
18511da177e4SLinus Torvalds 
185238308473SDavid S. Miller 	if (!rt) {
18531da177e4SLinus Torvalds 		err = -ENOMEM;
18541da177e4SLinus Torvalds 		goto out;
18551da177e4SLinus Torvalds 	}
18561da177e4SLinus Torvalds 
18571716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
18581716a961SGao feng 		rt6_set_expires(rt, jiffies +
18591716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
18601716a961SGao feng 	else
18611716a961SGao feng 		rt6_clean_expires(rt);
18621da177e4SLinus Torvalds 
186386872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
186486872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
186586872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
186686872cb5SThomas Graf 
186786872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
18681da177e4SLinus Torvalds 
18691da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1870d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1871ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1872ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
18731da177e4SLinus Torvalds 	else
1874d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
18751da177e4SLinus Torvalds 
1876d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
18771da177e4SLinus Torvalds 
187819e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
187919e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
188019e42e45SRoopa Prabhu 
188119e42e45SRoopa Prabhu 		err = lwtunnel_build_state(dev, cfg->fc_encap_type,
1882127eb7cdSTom Herbert 					   cfg->fc_encap, AF_INET6, cfg,
1883127eb7cdSTom Herbert 					   &lwtstate);
188419e42e45SRoopa Prabhu 		if (err)
188519e42e45SRoopa Prabhu 			goto out;
188661adedf3SJiri Benc 		rt->dst.lwtstate = lwtstate_get(lwtstate);
188761adedf3SJiri Benc 		if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
188861adedf3SJiri Benc 			rt->dst.lwtstate->orig_output = rt->dst.output;
188961adedf3SJiri Benc 			rt->dst.output = lwtunnel_output;
189019e42e45SRoopa Prabhu 		}
189161adedf3SJiri Benc 		if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
189261adedf3SJiri Benc 			rt->dst.lwtstate->orig_input = rt->dst.input;
189361adedf3SJiri Benc 			rt->dst.input = lwtunnel_input;
189425368623STom Herbert 		}
189525368623STom Herbert 	}
189619e42e45SRoopa Prabhu 
189786872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
189886872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1899afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
190011d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
19011da177e4SLinus Torvalds 
19021da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
190386872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
190486872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
19051da177e4SLinus Torvalds #endif
19061da177e4SLinus Torvalds 
190786872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
19081da177e4SLinus Torvalds 
19091da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
19101da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
19111da177e4SLinus Torvalds 	 */
191286872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
191338308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
191438308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
191538308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
19161da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
19175578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
19181da177e4SLinus Torvalds 			if (dev) {
19191da177e4SLinus Torvalds 				dev_put(dev);
19201da177e4SLinus Torvalds 				in6_dev_put(idev);
19211da177e4SLinus Torvalds 			}
19225578689aSDaniel Lezcano 			dev = net->loopback_dev;
19231da177e4SLinus Torvalds 			dev_hold(dev);
19241da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
19251da177e4SLinus Torvalds 			if (!idev) {
19261da177e4SLinus Torvalds 				err = -ENODEV;
19271da177e4SLinus Torvalds 				goto out;
19281da177e4SLinus Torvalds 			}
19291da177e4SLinus Torvalds 		}
19301da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1931ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1932ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1933ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1934ede2059dSEric W. Biederman 			rt->dst.output = dst_discard_out;
19357150aedeSKamala R 			rt->dst.input = dst_discard;
1936ef2c7d7bSNicolas Dichtel 			break;
1937ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1938ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
19397150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
19407150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1941ef2c7d7bSNicolas Dichtel 			break;
1942b4949ab2SNicolas Dichtel 		case RTN_THROW:
19430315e382SNikola Forró 		case RTN_UNREACHABLE:
1944ef2c7d7bSNicolas Dichtel 		default:
19457150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
19460315e382SNikola Forró 					: (cfg->fc_type == RTN_UNREACHABLE)
19470315e382SNikola Forró 					? -EHOSTUNREACH : -ENETUNREACH;
19487150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
19497150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1950ef2c7d7bSNicolas Dichtel 			break;
1951ef2c7d7bSNicolas Dichtel 		}
19521da177e4SLinus Torvalds 		goto install_route;
19531da177e4SLinus Torvalds 	}
19541da177e4SLinus Torvalds 
195586872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1956b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
19571da177e4SLinus Torvalds 		int gwa_type;
19581da177e4SLinus Torvalds 
195986872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1960330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
196148ed7b26SFlorian Westphal 
196248ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
196348ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
196448ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
196548ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
196648ed7b26SFlorian Westphal 		 */
196748ed7b26SFlorian Westphal 		err = -EINVAL;
1968330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1969330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1970330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
197148ed7b26SFlorian Westphal 			goto out;
197248ed7b26SFlorian Westphal 
19734e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
19741da177e4SLinus Torvalds 
19751da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
19768c14586fSDavid Ahern 			struct rt6_info *grt = NULL;
19771da177e4SLinus Torvalds 
19781da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
19791da177e4SLinus Torvalds 			   addresses as nexthop address.
19801da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
19811da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
19821da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
19831da177e4SLinus Torvalds 			   some exceptions. --ANK
19841da177e4SLinus Torvalds 			 */
19851da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
19861da177e4SLinus Torvalds 				goto out;
19871da177e4SLinus Torvalds 
19888c14586fSDavid Ahern 			if (cfg->fc_table)
19898c14586fSDavid Ahern 				grt = ip6_nh_lookup_table(net, cfg, gw_addr);
19908c14586fSDavid Ahern 
19918c14586fSDavid Ahern 			if (!grt)
19928c14586fSDavid Ahern 				grt = rt6_lookup(net, gw_addr, NULL,
19938c14586fSDavid Ahern 						 cfg->fc_ifindex, 1);
19941da177e4SLinus Torvalds 
19951da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
199638308473SDavid S. Miller 			if (!grt)
19971da177e4SLinus Torvalds 				goto out;
19981da177e4SLinus Torvalds 			if (dev) {
1999d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
200094e187c0SAmerigo Wang 					ip6_rt_put(grt);
20011da177e4SLinus Torvalds 					goto out;
20021da177e4SLinus Torvalds 				}
20031da177e4SLinus Torvalds 			} else {
2004d1918542SDavid S. Miller 				dev = grt->dst.dev;
20051da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
20061da177e4SLinus Torvalds 				dev_hold(dev);
20071da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
20081da177e4SLinus Torvalds 			}
20091da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
20101da177e4SLinus Torvalds 				err = 0;
201194e187c0SAmerigo Wang 			ip6_rt_put(grt);
20121da177e4SLinus Torvalds 
20131da177e4SLinus Torvalds 			if (err)
20141da177e4SLinus Torvalds 				goto out;
20151da177e4SLinus Torvalds 		}
20161da177e4SLinus Torvalds 		err = -EINVAL;
201738308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
20181da177e4SLinus Torvalds 			goto out;
20191da177e4SLinus Torvalds 	}
20201da177e4SLinus Torvalds 
20211da177e4SLinus Torvalds 	err = -ENODEV;
202238308473SDavid S. Miller 	if (!dev)
20231da177e4SLinus Torvalds 		goto out;
20241da177e4SLinus Torvalds 
2025c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
2026c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
2027c3968a85SDaniel Walter 			err = -EINVAL;
2028c3968a85SDaniel Walter 			goto out;
2029c3968a85SDaniel Walter 		}
20304e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
2031c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
2032c3968a85SDaniel Walter 	} else
2033c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2034c3968a85SDaniel Walter 
203586872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
20361da177e4SLinus Torvalds 
20371da177e4SLinus Torvalds install_route:
2038d8d1f30bSChangli Gao 	rt->dst.dev = dev;
20391da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
2040c71099acSThomas Graf 	rt->rt6i_table = table;
204163152fc0SDaniel Lezcano 
2042c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
204363152fc0SDaniel Lezcano 
20448c5b83f0SRoopa Prabhu 	return rt;
20451da177e4SLinus Torvalds out:
20461da177e4SLinus Torvalds 	if (dev)
20471da177e4SLinus Torvalds 		dev_put(dev);
20481da177e4SLinus Torvalds 	if (idev)
20491da177e4SLinus Torvalds 		in6_dev_put(idev);
20501da177e4SLinus Torvalds 	if (rt)
2051d8d1f30bSChangli Gao 		dst_free(&rt->dst);
20526b9ea5a6SRoopa Prabhu 
20538c5b83f0SRoopa Prabhu 	return ERR_PTR(err);
20546b9ea5a6SRoopa Prabhu }
20556b9ea5a6SRoopa Prabhu 
20566b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg)
20576b9ea5a6SRoopa Prabhu {
20586b9ea5a6SRoopa Prabhu 	struct mx6_config mxc = { .mx = NULL, };
20598c5b83f0SRoopa Prabhu 	struct rt6_info *rt;
20606b9ea5a6SRoopa Prabhu 	int err;
20616b9ea5a6SRoopa Prabhu 
20628c5b83f0SRoopa Prabhu 	rt = ip6_route_info_create(cfg);
20638c5b83f0SRoopa Prabhu 	if (IS_ERR(rt)) {
20648c5b83f0SRoopa Prabhu 		err = PTR_ERR(rt);
20658c5b83f0SRoopa Prabhu 		rt = NULL;
20666b9ea5a6SRoopa Prabhu 		goto out;
20678c5b83f0SRoopa Prabhu 	}
20686b9ea5a6SRoopa Prabhu 
20696b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&mxc, cfg);
20706b9ea5a6SRoopa Prabhu 	if (err)
20716b9ea5a6SRoopa Prabhu 		goto out;
20726b9ea5a6SRoopa Prabhu 
20736b9ea5a6SRoopa Prabhu 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
20746b9ea5a6SRoopa Prabhu 
20756b9ea5a6SRoopa Prabhu 	kfree(mxc.mx);
20766b9ea5a6SRoopa Prabhu 
20776b9ea5a6SRoopa Prabhu 	return err;
20786b9ea5a6SRoopa Prabhu out:
20796b9ea5a6SRoopa Prabhu 	if (rt)
20806b9ea5a6SRoopa Prabhu 		dst_free(&rt->dst);
20816b9ea5a6SRoopa Prabhu 
20821da177e4SLinus Torvalds 	return err;
20831da177e4SLinus Torvalds }
20841da177e4SLinus Torvalds 
208586872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
20861da177e4SLinus Torvalds {
20871da177e4SLinus Torvalds 	int err;
2088c71099acSThomas Graf 	struct fib6_table *table;
2089d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
20901da177e4SLinus Torvalds 
20918e3d5be7SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry ||
20928e3d5be7SMartin KaFai Lau 	    rt->dst.flags & DST_NOCACHE) {
20936825a26cSGao feng 		err = -ENOENT;
20946825a26cSGao feng 		goto out;
20956825a26cSGao feng 	}
20966c813a72SPatrick McHardy 
2097c71099acSThomas Graf 	table = rt->rt6i_table;
2098c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
209986872cb5SThomas Graf 	err = fib6_del(rt, info);
2100c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
21011da177e4SLinus Torvalds 
21026825a26cSGao feng out:
210394e187c0SAmerigo Wang 	ip6_rt_put(rt);
21041da177e4SLinus Torvalds 	return err;
21051da177e4SLinus Torvalds }
21061da177e4SLinus Torvalds 
2107e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
2108e0a1ad73SThomas Graf {
21094d1169c1SDenis V. Lunev 	struct nl_info info = {
2110d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
21114d1169c1SDenis V. Lunev 	};
2112528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
2113e0a1ad73SThomas Graf }
2114e0a1ad73SThomas Graf 
211586872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
21161da177e4SLinus Torvalds {
2117c71099acSThomas Graf 	struct fib6_table *table;
21181da177e4SLinus Torvalds 	struct fib6_node *fn;
21191da177e4SLinus Torvalds 	struct rt6_info *rt;
21201da177e4SLinus Torvalds 	int err = -ESRCH;
21211da177e4SLinus Torvalds 
21225578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
212338308473SDavid S. Miller 	if (!table)
2124c71099acSThomas Graf 		return err;
21251da177e4SLinus Torvalds 
2126c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2127c71099acSThomas Graf 
2128c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
212986872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
213086872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
21311da177e4SLinus Torvalds 
21321da177e4SLinus Torvalds 	if (fn) {
2133d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
21341f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
21351f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
21361f56a01fSMartin KaFai Lau 				continue;
213786872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2138d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2139d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
21401da177e4SLinus Torvalds 				continue;
214186872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
214286872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
21431da177e4SLinus Torvalds 				continue;
214486872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
21451da177e4SLinus Torvalds 				continue;
2146d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2147c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
21481da177e4SLinus Torvalds 
214986872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
21501da177e4SLinus Torvalds 		}
21511da177e4SLinus Torvalds 	}
2152c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
21531da177e4SLinus Torvalds 
21541da177e4SLinus Torvalds 	return err;
21551da177e4SLinus Torvalds }
21561da177e4SLinus Torvalds 
21576700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2158a6279458SYOSHIFUJI Hideaki {
2159a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2160e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2161e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2162e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2163e8599ff4SDavid S. Miller 	struct neighbour *neigh;
216471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
21656e157b6aSDavid S. Miller 	int optlen, on_link;
21666e157b6aSDavid S. Miller 	u8 *lladdr;
2167e8599ff4SDavid S. Miller 
216829a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
216971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2170e8599ff4SDavid S. Miller 
2171e8599ff4SDavid S. Miller 	if (optlen < 0) {
21726e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2173e8599ff4SDavid S. Miller 		return;
2174e8599ff4SDavid S. Miller 	}
2175e8599ff4SDavid S. Miller 
217671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2177e8599ff4SDavid S. Miller 
217871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
21796e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2180e8599ff4SDavid S. Miller 		return;
2181e8599ff4SDavid S. Miller 	}
2182e8599ff4SDavid S. Miller 
21836e157b6aSDavid S. Miller 	on_link = 0;
218471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2185e8599ff4SDavid S. Miller 		on_link = 1;
218671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2187e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
21886e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2189e8599ff4SDavid S. Miller 		return;
2190e8599ff4SDavid S. Miller 	}
2191e8599ff4SDavid S. Miller 
2192e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2193e8599ff4SDavid S. Miller 	if (!in6_dev)
2194e8599ff4SDavid S. Miller 		return;
2195e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2196e8599ff4SDavid S. Miller 		return;
2197e8599ff4SDavid S. Miller 
2198e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2199e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2200e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2201e8599ff4SDavid S. Miller 	 */
2202e8599ff4SDavid S. Miller 
220371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
2204e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2205e8599ff4SDavid S. Miller 		return;
2206e8599ff4SDavid S. Miller 	}
22076e157b6aSDavid S. Miller 
22086e157b6aSDavid S. Miller 	lladdr = NULL;
2209e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2210e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2211e8599ff4SDavid S. Miller 					     skb->dev);
2212e8599ff4SDavid S. Miller 		if (!lladdr) {
2213e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2214e8599ff4SDavid S. Miller 			return;
2215e8599ff4SDavid S. Miller 		}
2216e8599ff4SDavid S. Miller 	}
2217e8599ff4SDavid S. Miller 
22186e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
2219ec13ad1dSMatthias Schiffer 	if (rt->rt6i_flags & RTF_REJECT) {
22206e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
22216e157b6aSDavid S. Miller 		return;
22226e157b6aSDavid S. Miller 	}
22236e157b6aSDavid S. Miller 
22246e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
22256e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
22266e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
22276e157b6aSDavid S. Miller 	 */
22286e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
22296e157b6aSDavid S. Miller 
223071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2231e8599ff4SDavid S. Miller 	if (!neigh)
2232e8599ff4SDavid S. Miller 		return;
2233e8599ff4SDavid S. Miller 
22341da177e4SLinus Torvalds 	/*
22351da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
22361da177e4SLinus Torvalds 	 */
22371da177e4SLinus Torvalds 
22381da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
22391da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
22401da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
22411da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
22421da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
22431da177e4SLinus Torvalds 		     );
22441da177e4SLinus Torvalds 
224583a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
224638308473SDavid S. Miller 	if (!nrt)
22471da177e4SLinus Torvalds 		goto out;
22481da177e4SLinus Torvalds 
22491da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
22501da177e4SLinus Torvalds 	if (on_link)
22511da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
22521da177e4SLinus Torvalds 
22534e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
22541da177e4SLinus Torvalds 
225540e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
22561da177e4SLinus Torvalds 		goto out;
22571da177e4SLinus Torvalds 
2258d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2259d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
226071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
226160592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
22628d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
22638d71740cSTom Tucker 
22641da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
22656e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2266e0a1ad73SThomas Graf 		ip6_del_rt(rt);
22671da177e4SLinus Torvalds 	}
22681da177e4SLinus Torvalds 
22691da177e4SLinus Torvalds out:
2270e8599ff4SDavid S. Miller 	neigh_release(neigh);
22716e157b6aSDavid S. Miller }
22726e157b6aSDavid S. Miller 
22731da177e4SLinus Torvalds /*
22741da177e4SLinus Torvalds  *	Misc support functions
22751da177e4SLinus Torvalds  */
22761da177e4SLinus Torvalds 
22774b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
22784b32b5adSMartin KaFai Lau {
22794b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
22804b32b5adSMartin KaFai Lau 
22814b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
22824b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
22834b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
22844b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
22854b32b5adSMartin KaFai Lau }
22864b32b5adSMartin KaFai Lau 
228783a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
22881da177e4SLinus Torvalds {
2289d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2290d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
229183a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2292d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
22931da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
22941da177e4SLinus Torvalds 	if (rt->rt6i_idev)
22951da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2296d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
22974e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
22981716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
22991716a961SGao feng 	rt6_set_from(rt, ort);
230083a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
23011da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
230283a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
23031da177e4SLinus Torvalds #endif
230483a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2305c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
230661adedf3SJiri Benc 	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
23071da177e4SLinus Torvalds }
23081da177e4SLinus Torvalds 
230970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2310efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2311b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2312b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
231370ceb4f5SYOSHIFUJI Hideaki {
231470ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
231570ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2316c71099acSThomas Graf 	struct fib6_table *table;
231770ceb4f5SYOSHIFUJI Hideaki 
2318efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
231938308473SDavid S. Miller 	if (!table)
2320c71099acSThomas Graf 		return NULL;
2321c71099acSThomas Graf 
23225744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2323c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
232470ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
232570ceb4f5SYOSHIFUJI Hideaki 		goto out;
232670ceb4f5SYOSHIFUJI Hideaki 
2327d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2328d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
232970ceb4f5SYOSHIFUJI Hideaki 			continue;
233070ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
233170ceb4f5SYOSHIFUJI Hideaki 			continue;
233270ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
233370ceb4f5SYOSHIFUJI Hideaki 			continue;
2334d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
233570ceb4f5SYOSHIFUJI Hideaki 		break;
233670ceb4f5SYOSHIFUJI Hideaki 	}
233770ceb4f5SYOSHIFUJI Hideaki out:
23385744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
233970ceb4f5SYOSHIFUJI Hideaki 	return rt;
234070ceb4f5SYOSHIFUJI Hideaki }
234170ceb4f5SYOSHIFUJI Hideaki 
2342efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2343b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2344b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
234595c96174SEric Dumazet 					   unsigned int pref)
234670ceb4f5SYOSHIFUJI Hideaki {
234786872cb5SThomas Graf 	struct fib6_config cfg = {
2348238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
234986872cb5SThomas Graf 		.fc_ifindex	= ifindex,
235086872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
235186872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
235286872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
235315e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2354efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2355efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
235686872cb5SThomas Graf 	};
235770ceb4f5SYOSHIFUJI Hideaki 
2358ca254490SDavid Ahern 	cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO;
23594e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
23604e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
236186872cb5SThomas Graf 
2362e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2363e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
236486872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
236570ceb4f5SYOSHIFUJI Hideaki 
236686872cb5SThomas Graf 	ip6_route_add(&cfg);
236770ceb4f5SYOSHIFUJI Hideaki 
2368efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
236970ceb4f5SYOSHIFUJI Hideaki }
237070ceb4f5SYOSHIFUJI Hideaki #endif
237170ceb4f5SYOSHIFUJI Hideaki 
2372b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
23731da177e4SLinus Torvalds {
23741da177e4SLinus Torvalds 	struct rt6_info *rt;
2375c71099acSThomas Graf 	struct fib6_table *table;
23761da177e4SLinus Torvalds 
2377c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
237838308473SDavid S. Miller 	if (!table)
2379c71099acSThomas Graf 		return NULL;
23801da177e4SLinus Torvalds 
23815744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2382d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2383d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2384045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
23851da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
23861da177e4SLinus Torvalds 			break;
23871da177e4SLinus Torvalds 	}
23881da177e4SLinus Torvalds 	if (rt)
2389d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
23905744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
23911da177e4SLinus Torvalds 	return rt;
23921da177e4SLinus Torvalds }
23931da177e4SLinus Torvalds 
2394b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2395ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2396ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
23971da177e4SLinus Torvalds {
239886872cb5SThomas Graf 	struct fib6_config cfg = {
2399ca254490SDavid Ahern 		.fc_table	= l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
2400238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
240186872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
240286872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
240386872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
240415e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
24055578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2406c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
240786872cb5SThomas Graf 	};
24081da177e4SLinus Torvalds 
24094e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
24101da177e4SLinus Torvalds 
241186872cb5SThomas Graf 	ip6_route_add(&cfg);
24121da177e4SLinus Torvalds 
24131da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
24141da177e4SLinus Torvalds }
24151da177e4SLinus Torvalds 
24167b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
24171da177e4SLinus Torvalds {
24181da177e4SLinus Torvalds 	struct rt6_info *rt;
2419c71099acSThomas Graf 	struct fib6_table *table;
2420c71099acSThomas Graf 
2421c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
24227b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
242338308473SDavid S. Miller 	if (!table)
2424c71099acSThomas Graf 		return;
24251da177e4SLinus Torvalds 
24261da177e4SLinus Torvalds restart:
2427c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2428d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
24293e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
24303e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2431d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2432c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2433e0a1ad73SThomas Graf 			ip6_del_rt(rt);
24341da177e4SLinus Torvalds 			goto restart;
24351da177e4SLinus Torvalds 		}
24361da177e4SLinus Torvalds 	}
2437c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
24381da177e4SLinus Torvalds }
24391da177e4SLinus Torvalds 
24405578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
24415578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
244286872cb5SThomas Graf 				 struct fib6_config *cfg)
244386872cb5SThomas Graf {
244486872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
244586872cb5SThomas Graf 
2446ca254490SDavid Ahern 	cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
2447ca254490SDavid Ahern 			 : RT6_TABLE_MAIN;
244886872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
244986872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
245086872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
245186872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
245286872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
245386872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
245486872cb5SThomas Graf 
24555578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2456f1243c2dSBenjamin Thery 
24574e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
24584e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
24594e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
246086872cb5SThomas Graf }
246186872cb5SThomas Graf 
24625578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
24631da177e4SLinus Torvalds {
246486872cb5SThomas Graf 	struct fib6_config cfg;
24651da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
24661da177e4SLinus Torvalds 	int err;
24671da177e4SLinus Torvalds 
24681da177e4SLinus Torvalds 	switch (cmd) {
24691da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
24701da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2471af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
24721da177e4SLinus Torvalds 			return -EPERM;
24731da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
24741da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
24751da177e4SLinus Torvalds 		if (err)
24761da177e4SLinus Torvalds 			return -EFAULT;
24771da177e4SLinus Torvalds 
24785578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
247986872cb5SThomas Graf 
24801da177e4SLinus Torvalds 		rtnl_lock();
24811da177e4SLinus Torvalds 		switch (cmd) {
24821da177e4SLinus Torvalds 		case SIOCADDRT:
248386872cb5SThomas Graf 			err = ip6_route_add(&cfg);
24841da177e4SLinus Torvalds 			break;
24851da177e4SLinus Torvalds 		case SIOCDELRT:
248686872cb5SThomas Graf 			err = ip6_route_del(&cfg);
24871da177e4SLinus Torvalds 			break;
24881da177e4SLinus Torvalds 		default:
24891da177e4SLinus Torvalds 			err = -EINVAL;
24901da177e4SLinus Torvalds 		}
24911da177e4SLinus Torvalds 		rtnl_unlock();
24921da177e4SLinus Torvalds 
24931da177e4SLinus Torvalds 		return err;
24943ff50b79SStephen Hemminger 	}
24951da177e4SLinus Torvalds 
24961da177e4SLinus Torvalds 	return -EINVAL;
24971da177e4SLinus Torvalds }
24981da177e4SLinus Torvalds 
24991da177e4SLinus Torvalds /*
25001da177e4SLinus Torvalds  *	Drop the packet on the floor
25011da177e4SLinus Torvalds  */
25021da177e4SLinus Torvalds 
2503d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
25041da177e4SLinus Torvalds {
2505612f09e8SYOSHIFUJI Hideaki 	int type;
2506adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2507612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2508612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
25090660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
251045bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
25113bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
25123bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2513612f09e8SYOSHIFUJI Hideaki 			break;
2514612f09e8SYOSHIFUJI Hideaki 		}
2515612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2516612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
25173bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
25183bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2519612f09e8SYOSHIFUJI Hideaki 		break;
2520612f09e8SYOSHIFUJI Hideaki 	}
25213ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
25221da177e4SLinus Torvalds 	kfree_skb(skb);
25231da177e4SLinus Torvalds 	return 0;
25241da177e4SLinus Torvalds }
25251da177e4SLinus Torvalds 
25269ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
25279ce8ade0SThomas Graf {
2528612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
25299ce8ade0SThomas Graf }
25309ce8ade0SThomas Graf 
2531ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
25321da177e4SLinus Torvalds {
2533adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2534612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
25351da177e4SLinus Torvalds }
25361da177e4SLinus Torvalds 
25379ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
25389ce8ade0SThomas Graf {
2539612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
25409ce8ade0SThomas Graf }
25419ce8ade0SThomas Graf 
2542ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
25439ce8ade0SThomas Graf {
2544adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2545612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
25469ce8ade0SThomas Graf }
25479ce8ade0SThomas Graf 
25481da177e4SLinus Torvalds /*
25491da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
25501da177e4SLinus Torvalds  */
25511da177e4SLinus Torvalds 
25521da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
25531da177e4SLinus Torvalds 				    const struct in6_addr *addr,
25548f031519SDavid S. Miller 				    bool anycast)
25551da177e4SLinus Torvalds {
2556ca254490SDavid Ahern 	u32 tb_id;
2557c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2558a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2559ad706862SMartin KaFai Lau 					    DST_NOCOUNT);
2560a3300ef4SHannes Frederic Sowa 	if (!rt)
25611da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
25621da177e4SLinus Torvalds 
25631da177e4SLinus Torvalds 	in6_dev_hold(idev);
25641da177e4SLinus Torvalds 
256511d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2566d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2567d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
25681da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
25691da177e4SLinus Torvalds 
25701da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
257158c4fb86SYOSHIFUJI Hideaki 	if (anycast)
257258c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
257358c4fb86SYOSHIFUJI Hideaki 	else
25741da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
25751da177e4SLinus Torvalds 
2576550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
25774e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
25781da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
2579ca254490SDavid Ahern 	tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
2580ca254490SDavid Ahern 	rt->rt6i_table = fib6_get_table(net, tb_id);
25818e3d5be7SMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
25821da177e4SLinus Torvalds 
2583d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
25841da177e4SLinus Torvalds 
25851da177e4SLinus Torvalds 	return rt;
25861da177e4SLinus Torvalds }
25871da177e4SLinus Torvalds 
2588c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2589c3968a85SDaniel Walter 			struct rt6_info *rt,
2590b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2591c3968a85SDaniel Walter 			unsigned int prefs,
2592c3968a85SDaniel Walter 			struct in6_addr *saddr)
2593c3968a85SDaniel Walter {
2594e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2595e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2596c3968a85SDaniel Walter 	int err = 0;
2597e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
25984e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2599c3968a85SDaniel Walter 	else
2600c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2601c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2602c3968a85SDaniel Walter 	return err;
2603c3968a85SDaniel Walter }
2604c3968a85SDaniel Walter 
2605c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2606c3968a85SDaniel Walter struct arg_dev_net_ip {
2607c3968a85SDaniel Walter 	struct net_device *dev;
2608c3968a85SDaniel Walter 	struct net *net;
2609c3968a85SDaniel Walter 	struct in6_addr *addr;
2610c3968a85SDaniel Walter };
2611c3968a85SDaniel Walter 
2612c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2613c3968a85SDaniel Walter {
2614c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2615c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2616c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2617c3968a85SDaniel Walter 
2618d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2619c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2620c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2621c3968a85SDaniel Walter 		/* remove prefsrc entry */
2622c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2623c3968a85SDaniel Walter 	}
2624c3968a85SDaniel Walter 	return 0;
2625c3968a85SDaniel Walter }
2626c3968a85SDaniel Walter 
2627c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2628c3968a85SDaniel Walter {
2629c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2630c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2631c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2632c3968a85SDaniel Walter 		.net = net,
2633c3968a85SDaniel Walter 		.addr = &ifp->addr,
2634c3968a85SDaniel Walter 	};
26350c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2636c3968a85SDaniel Walter }
2637c3968a85SDaniel Walter 
2638be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2639be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2640be7a010dSDuan Jiong 
2641be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2642be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2643be7a010dSDuan Jiong {
2644be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2645be7a010dSDuan Jiong 
2646be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2647be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2648be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2649be7a010dSDuan Jiong 		return -1;
2650be7a010dSDuan Jiong 	}
2651be7a010dSDuan Jiong 	return 0;
2652be7a010dSDuan Jiong }
2653be7a010dSDuan Jiong 
2654be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2655be7a010dSDuan Jiong {
2656be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2657be7a010dSDuan Jiong }
2658be7a010dSDuan Jiong 
26598ed67789SDaniel Lezcano struct arg_dev_net {
26608ed67789SDaniel Lezcano 	struct net_device *dev;
26618ed67789SDaniel Lezcano 	struct net *net;
26628ed67789SDaniel Lezcano };
26638ed67789SDaniel Lezcano 
26641da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
26651da177e4SLinus Torvalds {
2666bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2667bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
26688ed67789SDaniel Lezcano 
2669d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2670c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
26711da177e4SLinus Torvalds 		return -1;
2672c159d30cSDavid S. Miller 
26731da177e4SLinus Torvalds 	return 0;
26741da177e4SLinus Torvalds }
26751da177e4SLinus Torvalds 
2676f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
26771da177e4SLinus Torvalds {
26788ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
26798ed67789SDaniel Lezcano 		.dev = dev,
26808ed67789SDaniel Lezcano 		.net = net,
26818ed67789SDaniel Lezcano 	};
26828ed67789SDaniel Lezcano 
26830c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
26841e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
2685e332bc67SEric W. Biederman 	if (dev)
26868d0b94afSMartin KaFai Lau 		rt6_uncached_list_flush_dev(net, dev);
26871da177e4SLinus Torvalds }
26881da177e4SLinus Torvalds 
268995c96174SEric Dumazet struct rt6_mtu_change_arg {
26901da177e4SLinus Torvalds 	struct net_device *dev;
269195c96174SEric Dumazet 	unsigned int mtu;
26921da177e4SLinus Torvalds };
26931da177e4SLinus Torvalds 
26941da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
26951da177e4SLinus Torvalds {
26961da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
26971da177e4SLinus Torvalds 	struct inet6_dev *idev;
26981da177e4SLinus Torvalds 
26991da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
27001da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
27011da177e4SLinus Torvalds 	   We still use this lock to block changes
27021da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
27031da177e4SLinus Torvalds 	*/
27041da177e4SLinus Torvalds 
27051da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
270638308473SDavid S. Miller 	if (!idev)
27071da177e4SLinus Torvalds 		return 0;
27081da177e4SLinus Torvalds 
27091da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
27101da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
27111da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
27121da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
27131da177e4SLinus Torvalds 	 */
27141da177e4SLinus Torvalds 	/*
27151da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
27161da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
27171da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
27181da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
27191da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
27201da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
27211da177e4SLinus Torvalds 	   PMTU discouvery.
27221da177e4SLinus Torvalds 	 */
2723d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
27244b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
27254b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
27264b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
27274b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
27284b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
27294b32b5adSMartin KaFai Lau 			 * been updated.
27304b32b5adSMartin KaFai Lau 			 */
27314b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
27324b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
27334b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2734d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
27354b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2736defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2737566cfd8fSSimon Arlott 		}
27384b32b5adSMartin KaFai Lau 	}
27391da177e4SLinus Torvalds 	return 0;
27401da177e4SLinus Torvalds }
27411da177e4SLinus Torvalds 
274295c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
27431da177e4SLinus Torvalds {
2744c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2745c71099acSThomas Graf 		.dev = dev,
2746c71099acSThomas Graf 		.mtu = mtu,
2747c71099acSThomas Graf 	};
27481da177e4SLinus Torvalds 
27490c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
27501da177e4SLinus Torvalds }
27511da177e4SLinus Torvalds 
2752ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
27535176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
275486872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2755ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
275686872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
275786872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
275851ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2759c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
276019e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
276119e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
276232bc201eSXin Long 	[RTA_EXPIRES]		= { .type = NLA_U32 },
276386872cb5SThomas Graf };
276486872cb5SThomas Graf 
276586872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
276686872cb5SThomas Graf 			      struct fib6_config *cfg)
27671da177e4SLinus Torvalds {
276886872cb5SThomas Graf 	struct rtmsg *rtm;
276986872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2770c78ba6d6SLubomir Rintel 	unsigned int pref;
277186872cb5SThomas Graf 	int err;
27721da177e4SLinus Torvalds 
277386872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
277486872cb5SThomas Graf 	if (err < 0)
277586872cb5SThomas Graf 		goto errout;
27761da177e4SLinus Torvalds 
277786872cb5SThomas Graf 	err = -EINVAL;
277886872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
277986872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
278086872cb5SThomas Graf 
278186872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
278286872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
278386872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
278486872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
278586872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2786ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
278786872cb5SThomas Graf 
2788ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2789ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2790b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2791b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
279286872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
279386872cb5SThomas Graf 
2794ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2795ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2796ab79ad14SMaciej Żenczykowski 
27971f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
27981f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
27991f56a01fSMartin KaFai Lau 
280015e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
280186872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
28023b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
280386872cb5SThomas Graf 
280486872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
280567b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
280686872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
28071da177e4SLinus Torvalds 	}
280886872cb5SThomas Graf 
280986872cb5SThomas Graf 	if (tb[RTA_DST]) {
281086872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
281186872cb5SThomas Graf 
281286872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
281386872cb5SThomas Graf 			goto errout;
281486872cb5SThomas Graf 
281586872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
28161da177e4SLinus Torvalds 	}
281786872cb5SThomas Graf 
281886872cb5SThomas Graf 	if (tb[RTA_SRC]) {
281986872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
282086872cb5SThomas Graf 
282186872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
282286872cb5SThomas Graf 			goto errout;
282386872cb5SThomas Graf 
282486872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
28251da177e4SLinus Torvalds 	}
282686872cb5SThomas Graf 
2827c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
282867b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2829c3968a85SDaniel Walter 
283086872cb5SThomas Graf 	if (tb[RTA_OIF])
283186872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
283286872cb5SThomas Graf 
283386872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
283486872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
283586872cb5SThomas Graf 
283686872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
283786872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
283886872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
28391da177e4SLinus Torvalds 	}
284086872cb5SThomas Graf 
284186872cb5SThomas Graf 	if (tb[RTA_TABLE])
284286872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
284386872cb5SThomas Graf 
284451ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
284551ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
284651ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
284751ebd318SNicolas Dichtel 	}
284851ebd318SNicolas Dichtel 
2849c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2850c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2851c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2852c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2853c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2854c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2855c78ba6d6SLubomir Rintel 	}
2856c78ba6d6SLubomir Rintel 
285719e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
285819e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
285919e42e45SRoopa Prabhu 
286019e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP_TYPE])
286119e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
286219e42e45SRoopa Prabhu 
286332bc201eSXin Long 	if (tb[RTA_EXPIRES]) {
286432bc201eSXin Long 		unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
286532bc201eSXin Long 
286632bc201eSXin Long 		if (addrconf_finite_timeout(timeout)) {
286732bc201eSXin Long 			cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
286832bc201eSXin Long 			cfg->fc_flags |= RTF_EXPIRES;
286932bc201eSXin Long 		}
287032bc201eSXin Long 	}
287132bc201eSXin Long 
287286872cb5SThomas Graf 	err = 0;
287386872cb5SThomas Graf errout:
287486872cb5SThomas Graf 	return err;
28751da177e4SLinus Torvalds }
28761da177e4SLinus Torvalds 
28776b9ea5a6SRoopa Prabhu struct rt6_nh {
28786b9ea5a6SRoopa Prabhu 	struct rt6_info *rt6_info;
28796b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
28806b9ea5a6SRoopa Prabhu 	struct mx6_config mxc;
28816b9ea5a6SRoopa Prabhu 	struct list_head next;
28826b9ea5a6SRoopa Prabhu };
28836b9ea5a6SRoopa Prabhu 
28846b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
28856b9ea5a6SRoopa Prabhu {
28866b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28876b9ea5a6SRoopa Prabhu 
28886b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28896b9ea5a6SRoopa Prabhu 		pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n",
28906b9ea5a6SRoopa Prabhu 		        &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway,
28916b9ea5a6SRoopa Prabhu 		        nh->r_cfg.fc_ifindex);
28926b9ea5a6SRoopa Prabhu 	}
28936b9ea5a6SRoopa Prabhu }
28946b9ea5a6SRoopa Prabhu 
28956b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list,
28966b9ea5a6SRoopa Prabhu 				 struct rt6_info *rt, struct fib6_config *r_cfg)
28976b9ea5a6SRoopa Prabhu {
28986b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28996b9ea5a6SRoopa Prabhu 	struct rt6_info *rtnh;
29006b9ea5a6SRoopa Prabhu 	int err = -EEXIST;
29016b9ea5a6SRoopa Prabhu 
29026b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
29036b9ea5a6SRoopa Prabhu 		/* check if rt6_info already exists */
29046b9ea5a6SRoopa Prabhu 		rtnh = nh->rt6_info;
29056b9ea5a6SRoopa Prabhu 
29066b9ea5a6SRoopa Prabhu 		if (rtnh->dst.dev == rt->dst.dev &&
29076b9ea5a6SRoopa Prabhu 		    rtnh->rt6i_idev == rt->rt6i_idev &&
29086b9ea5a6SRoopa Prabhu 		    ipv6_addr_equal(&rtnh->rt6i_gateway,
29096b9ea5a6SRoopa Prabhu 				    &rt->rt6i_gateway))
29106b9ea5a6SRoopa Prabhu 			return err;
29116b9ea5a6SRoopa Prabhu 	}
29126b9ea5a6SRoopa Prabhu 
29136b9ea5a6SRoopa Prabhu 	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
29146b9ea5a6SRoopa Prabhu 	if (!nh)
29156b9ea5a6SRoopa Prabhu 		return -ENOMEM;
29166b9ea5a6SRoopa Prabhu 	nh->rt6_info = rt;
29176b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&nh->mxc, r_cfg);
29186b9ea5a6SRoopa Prabhu 	if (err) {
29196b9ea5a6SRoopa Prabhu 		kfree(nh);
29206b9ea5a6SRoopa Prabhu 		return err;
29216b9ea5a6SRoopa Prabhu 	}
29226b9ea5a6SRoopa Prabhu 	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
29236b9ea5a6SRoopa Prabhu 	list_add_tail(&nh->next, rt6_nh_list);
29246b9ea5a6SRoopa Prabhu 
29256b9ea5a6SRoopa Prabhu 	return 0;
29266b9ea5a6SRoopa Prabhu }
29276b9ea5a6SRoopa Prabhu 
29286b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg)
292951ebd318SNicolas Dichtel {
293051ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
293151ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
29326b9ea5a6SRoopa Prabhu 	struct rt6_info *rt;
29336b9ea5a6SRoopa Prabhu 	struct rt6_nh *err_nh;
29346b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh, *nh_safe;
293551ebd318SNicolas Dichtel 	int remaining;
293651ebd318SNicolas Dichtel 	int attrlen;
29376b9ea5a6SRoopa Prabhu 	int err = 1;
29386b9ea5a6SRoopa Prabhu 	int nhn = 0;
29396b9ea5a6SRoopa Prabhu 	int replace = (cfg->fc_nlinfo.nlh &&
29406b9ea5a6SRoopa Prabhu 		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
29416b9ea5a6SRoopa Prabhu 	LIST_HEAD(rt6_nh_list);
294251ebd318SNicolas Dichtel 
294335f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
294451ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
294551ebd318SNicolas Dichtel 
29466b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
29476b9ea5a6SRoopa Prabhu 	 * rt6_info structs per nexthop
29486b9ea5a6SRoopa Prabhu 	 */
294951ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
295051ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
295151ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
295251ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
295351ebd318SNicolas Dichtel 
295451ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
295551ebd318SNicolas Dichtel 		if (attrlen > 0) {
295651ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
295751ebd318SNicolas Dichtel 
295851ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
295951ebd318SNicolas Dichtel 			if (nla) {
296067b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
296151ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
296251ebd318SNicolas Dichtel 			}
296319e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
296419e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
296519e42e45SRoopa Prabhu 			if (nla)
296619e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
296751ebd318SNicolas Dichtel 		}
29686b9ea5a6SRoopa Prabhu 
29698c5b83f0SRoopa Prabhu 		rt = ip6_route_info_create(&r_cfg);
29708c5b83f0SRoopa Prabhu 		if (IS_ERR(rt)) {
29718c5b83f0SRoopa Prabhu 			err = PTR_ERR(rt);
29728c5b83f0SRoopa Prabhu 			rt = NULL;
29736b9ea5a6SRoopa Prabhu 			goto cleanup;
29748c5b83f0SRoopa Prabhu 		}
29756b9ea5a6SRoopa Prabhu 
29766b9ea5a6SRoopa Prabhu 		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
297751ebd318SNicolas Dichtel 		if (err) {
29786b9ea5a6SRoopa Prabhu 			dst_free(&rt->dst);
29796b9ea5a6SRoopa Prabhu 			goto cleanup;
298051ebd318SNicolas Dichtel 		}
29816b9ea5a6SRoopa Prabhu 
29826b9ea5a6SRoopa Prabhu 		rtnh = rtnh_next(rtnh, &remaining);
298351ebd318SNicolas Dichtel 	}
29846b9ea5a6SRoopa Prabhu 
29856b9ea5a6SRoopa Prabhu 	err_nh = NULL;
29866b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29876b9ea5a6SRoopa Prabhu 		err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc);
29886b9ea5a6SRoopa Prabhu 		/* nh->rt6_info is used or freed at this point, reset to NULL*/
29896b9ea5a6SRoopa Prabhu 		nh->rt6_info = NULL;
29906b9ea5a6SRoopa Prabhu 		if (err) {
29916b9ea5a6SRoopa Prabhu 			if (replace && nhn)
29926b9ea5a6SRoopa Prabhu 				ip6_print_replace_route_err(&rt6_nh_list);
29936b9ea5a6SRoopa Prabhu 			err_nh = nh;
29946b9ea5a6SRoopa Prabhu 			goto add_errout;
29956b9ea5a6SRoopa Prabhu 		}
29966b9ea5a6SRoopa Prabhu 
29971a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
299827596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
299927596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
300027596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
300127596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
300227596472SMichal Kubeček 		 * be added to it.
30031a72418bSNicolas Dichtel 		 */
300427596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
300527596472SMichal Kubeček 						     NLM_F_REPLACE);
30066b9ea5a6SRoopa Prabhu 		nhn++;
30076b9ea5a6SRoopa Prabhu 	}
30086b9ea5a6SRoopa Prabhu 
30096b9ea5a6SRoopa Prabhu 	goto cleanup;
30106b9ea5a6SRoopa Prabhu 
30116b9ea5a6SRoopa Prabhu add_errout:
30126b9ea5a6SRoopa Prabhu 	/* Delete routes that were already added */
30136b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
30146b9ea5a6SRoopa Prabhu 		if (err_nh == nh)
30156b9ea5a6SRoopa Prabhu 			break;
30166b9ea5a6SRoopa Prabhu 		ip6_route_del(&nh->r_cfg);
30176b9ea5a6SRoopa Prabhu 	}
30186b9ea5a6SRoopa Prabhu 
30196b9ea5a6SRoopa Prabhu cleanup:
30206b9ea5a6SRoopa Prabhu 	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
30216b9ea5a6SRoopa Prabhu 		if (nh->rt6_info)
30226b9ea5a6SRoopa Prabhu 			dst_free(&nh->rt6_info->dst);
30236b9ea5a6SRoopa Prabhu 		kfree(nh->mxc.mx);
30246b9ea5a6SRoopa Prabhu 		list_del(&nh->next);
30256b9ea5a6SRoopa Prabhu 		kfree(nh);
30266b9ea5a6SRoopa Prabhu 	}
30276b9ea5a6SRoopa Prabhu 
30286b9ea5a6SRoopa Prabhu 	return err;
30296b9ea5a6SRoopa Prabhu }
30306b9ea5a6SRoopa Prabhu 
30316b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg)
30326b9ea5a6SRoopa Prabhu {
30336b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
30346b9ea5a6SRoopa Prabhu 	struct rtnexthop *rtnh;
30356b9ea5a6SRoopa Prabhu 	int remaining;
30366b9ea5a6SRoopa Prabhu 	int attrlen;
30376b9ea5a6SRoopa Prabhu 	int err = 1, last_err = 0;
30386b9ea5a6SRoopa Prabhu 
30396b9ea5a6SRoopa Prabhu 	remaining = cfg->fc_mp_len;
30406b9ea5a6SRoopa Prabhu 	rtnh = (struct rtnexthop *)cfg->fc_mp;
30416b9ea5a6SRoopa Prabhu 
30426b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry */
30436b9ea5a6SRoopa Prabhu 	while (rtnh_ok(rtnh, remaining)) {
30446b9ea5a6SRoopa Prabhu 		memcpy(&r_cfg, cfg, sizeof(*cfg));
30456b9ea5a6SRoopa Prabhu 		if (rtnh->rtnh_ifindex)
30466b9ea5a6SRoopa Prabhu 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
30476b9ea5a6SRoopa Prabhu 
30486b9ea5a6SRoopa Prabhu 		attrlen = rtnh_attrlen(rtnh);
30496b9ea5a6SRoopa Prabhu 		if (attrlen > 0) {
30506b9ea5a6SRoopa Prabhu 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
30516b9ea5a6SRoopa Prabhu 
30526b9ea5a6SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
30536b9ea5a6SRoopa Prabhu 			if (nla) {
30546b9ea5a6SRoopa Prabhu 				nla_memcpy(&r_cfg.fc_gateway, nla, 16);
30556b9ea5a6SRoopa Prabhu 				r_cfg.fc_flags |= RTF_GATEWAY;
30566b9ea5a6SRoopa Prabhu 			}
30576b9ea5a6SRoopa Prabhu 		}
30586b9ea5a6SRoopa Prabhu 		err = ip6_route_del(&r_cfg);
30596b9ea5a6SRoopa Prabhu 		if (err)
30606b9ea5a6SRoopa Prabhu 			last_err = err;
30616b9ea5a6SRoopa Prabhu 
306251ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
306351ebd318SNicolas Dichtel 	}
306451ebd318SNicolas Dichtel 
306551ebd318SNicolas Dichtel 	return last_err;
306651ebd318SNicolas Dichtel }
306751ebd318SNicolas Dichtel 
3068661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
30691da177e4SLinus Torvalds {
307086872cb5SThomas Graf 	struct fib6_config cfg;
307186872cb5SThomas Graf 	int err;
30721da177e4SLinus Torvalds 
307386872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
307486872cb5SThomas Graf 	if (err < 0)
307586872cb5SThomas Graf 		return err;
307686872cb5SThomas Graf 
307751ebd318SNicolas Dichtel 	if (cfg.fc_mp)
30786b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_del(&cfg);
307951ebd318SNicolas Dichtel 	else
308086872cb5SThomas Graf 		return ip6_route_del(&cfg);
30811da177e4SLinus Torvalds }
30821da177e4SLinus Torvalds 
3083661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
30841da177e4SLinus Torvalds {
308586872cb5SThomas Graf 	struct fib6_config cfg;
308686872cb5SThomas Graf 	int err;
30871da177e4SLinus Torvalds 
308886872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
308986872cb5SThomas Graf 	if (err < 0)
309086872cb5SThomas Graf 		return err;
309186872cb5SThomas Graf 
309251ebd318SNicolas Dichtel 	if (cfg.fc_mp)
30936b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_add(&cfg);
309451ebd318SNicolas Dichtel 	else
309586872cb5SThomas Graf 		return ip6_route_add(&cfg);
30961da177e4SLinus Torvalds }
30971da177e4SLinus Torvalds 
309819e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
3099339bf98fSThomas Graf {
3100339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
3101339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
3102339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
3103339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
3104339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
3105339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
3106339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
3107339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
3108339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
31096a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
3110ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
3111c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
311219e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
311361adedf3SJiri Benc 	       + lwtunnel_get_encap_size(rt->dst.lwtstate);
3114339bf98fSThomas Graf }
3115339bf98fSThomas Graf 
3116191cd582SBrian Haley static int rt6_fill_node(struct net *net,
3117191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
31180d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
311915e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
31207bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
31211da177e4SLinus Torvalds {
31224b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
31231da177e4SLinus Torvalds 	struct rtmsg *rtm;
31241da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
3125e3703b3dSThomas Graf 	long expires;
31269e762a4aSPatrick McHardy 	u32 table;
31271da177e4SLinus Torvalds 
31281da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
31291da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
31301da177e4SLinus Torvalds 			/* success since this is not a prefix route */
31311da177e4SLinus Torvalds 			return 1;
31321da177e4SLinus Torvalds 		}
31331da177e4SLinus Torvalds 	}
31341da177e4SLinus Torvalds 
313515e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
313638308473SDavid S. Miller 	if (!nlh)
313726932566SPatrick McHardy 		return -EMSGSIZE;
31382d7202bfSThomas Graf 
31392d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
31401da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
31411da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
31421da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
31431da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
3144c71099acSThomas Graf 	if (rt->rt6i_table)
31459e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
3146c71099acSThomas Graf 	else
31479e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
31489e762a4aSPatrick McHardy 	rtm->rtm_table = table;
3149c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
3150c78679e8SDavid S. Miller 		goto nla_put_failure;
3151ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
3152ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
3153ef2c7d7bSNicolas Dichtel 		case -EINVAL:
3154ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
3155ef2c7d7bSNicolas Dichtel 			break;
3156ef2c7d7bSNicolas Dichtel 		case -EACCES:
3157ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
3158ef2c7d7bSNicolas Dichtel 			break;
3159b4949ab2SNicolas Dichtel 		case -EAGAIN:
3160b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
3161b4949ab2SNicolas Dichtel 			break;
3162ef2c7d7bSNicolas Dichtel 		default:
31631da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
3164ef2c7d7bSNicolas Dichtel 			break;
3165ef2c7d7bSNicolas Dichtel 		}
3166ef2c7d7bSNicolas Dichtel 	}
3167ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
3168ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
3169d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
31701da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
31711da177e4SLinus Torvalds 	else
31721da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
31731da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
317435103d11SAndy Gospodarek 	if (!netif_carrier_ok(rt->dst.dev)) {
3175cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
317635103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
317735103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
317835103d11SAndy Gospodarek 	}
31791da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
31801da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
31811da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
31821da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
3183f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
3184f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
31851da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
3186f0396f60SDenis Ovsienko 		else
3187f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
3188f0396f60SDenis Ovsienko 	}
31891da177e4SLinus Torvalds 
31901da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
31911da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
31921da177e4SLinus Torvalds 
31931da177e4SLinus Torvalds 	if (dst) {
3194930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
3195c78679e8SDavid S. Miller 			goto nla_put_failure;
31961da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
31971da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
3198930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
3199c78679e8SDavid S. Miller 			goto nla_put_failure;
32001da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
32011da177e4SLinus Torvalds 	if (src) {
3202930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
3203c78679e8SDavid S. Miller 			goto nla_put_failure;
32041da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
3205c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
3206930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
3207c78679e8SDavid S. Miller 		goto nla_put_failure;
32081da177e4SLinus Torvalds #endif
32097bc570c8SYOSHIFUJI Hideaki 	if (iif) {
32107bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
32117bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
32128229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
32137bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
32147bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
32157bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
32167bc570c8SYOSHIFUJI Hideaki 						return 0;
32177bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
32187bc570c8SYOSHIFUJI Hideaki 				} else {
32197bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
32207bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
32217bc570c8SYOSHIFUJI Hideaki 				}
32227bc570c8SYOSHIFUJI Hideaki 			}
32237bc570c8SYOSHIFUJI Hideaki 		} else
32247bc570c8SYOSHIFUJI Hideaki #endif
3225c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
3226c78679e8SDavid S. Miller 				goto nla_put_failure;
32277bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
32281da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
3229c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
3230930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3231c78679e8SDavid S. Miller 			goto nla_put_failure;
3232c3968a85SDaniel Walter 	}
3233c3968a85SDaniel Walter 
3234c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
3235c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
32364e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
3237930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3238c78679e8SDavid S. Miller 			goto nla_put_failure;
32391da177e4SLinus Torvalds 	}
32402d7202bfSThomas Graf 
32414b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
32424b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
32434b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
32444b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
32452d7202bfSThomas Graf 		goto nla_put_failure;
32462d7202bfSThomas Graf 
3247dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
3248930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
324994f826b8SEric Dumazet 			goto nla_put_failure;
325094f826b8SEric Dumazet 	}
32512d7202bfSThomas Graf 
3252c78679e8SDavid S. Miller 	if (rt->dst.dev &&
3253c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3254c78679e8SDavid S. Miller 		goto nla_put_failure;
3255c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
3256c78679e8SDavid S. Miller 		goto nla_put_failure;
32578253947eSLi Wei 
32588253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
325969cdf8f9SYOSHIFUJI Hideaki 
326087a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
3261e3703b3dSThomas Graf 		goto nla_put_failure;
32621da177e4SLinus Torvalds 
3263c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
3264c78ba6d6SLubomir Rintel 		goto nla_put_failure;
3265c78ba6d6SLubomir Rintel 
326661adedf3SJiri Benc 	lwtunnel_fill_encap(skb, rt->dst.lwtstate);
326719e42e45SRoopa Prabhu 
3268053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
3269053c095aSJohannes Berg 	return 0;
32702d7202bfSThomas Graf 
32712d7202bfSThomas Graf nla_put_failure:
327226932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
327326932566SPatrick McHardy 	return -EMSGSIZE;
32741da177e4SLinus Torvalds }
32751da177e4SLinus Torvalds 
32761b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
32771da177e4SLinus Torvalds {
32781da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
32791da177e4SLinus Torvalds 	int prefix;
32801da177e4SLinus Torvalds 
32812d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
32822d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
32831da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
32841da177e4SLinus Torvalds 	} else
32851da177e4SLinus Torvalds 		prefix = 0;
32861da177e4SLinus Torvalds 
3287191cd582SBrian Haley 	return rt6_fill_node(arg->net,
3288191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
328915e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
32907bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
32911da177e4SLinus Torvalds }
32921da177e4SLinus Torvalds 
3293661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
32941da177e4SLinus Torvalds {
32953b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3296ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
32971da177e4SLinus Torvalds 	struct rt6_info *rt;
3298ab364a6fSThomas Graf 	struct sk_buff *skb;
3299ab364a6fSThomas Graf 	struct rtmsg *rtm;
33004c9483b2SDavid S. Miller 	struct flowi6 fl6;
330172331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3302ab364a6fSThomas Graf 
3303ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3304ab364a6fSThomas Graf 	if (err < 0)
3305ab364a6fSThomas Graf 		goto errout;
3306ab364a6fSThomas Graf 
3307ab364a6fSThomas Graf 	err = -EINVAL;
33084c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
3309*38b7097bSHannes Frederic Sowa 	rtm = nlmsg_data(nlh);
3310*38b7097bSHannes Frederic Sowa 	fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
3311ab364a6fSThomas Graf 
3312ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3313ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3314ab364a6fSThomas Graf 			goto errout;
3315ab364a6fSThomas Graf 
33164e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3317ab364a6fSThomas Graf 	}
3318ab364a6fSThomas Graf 
3319ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3320ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3321ab364a6fSThomas Graf 			goto errout;
3322ab364a6fSThomas Graf 
33234e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3324ab364a6fSThomas Graf 	}
3325ab364a6fSThomas Graf 
3326ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3327ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3328ab364a6fSThomas Graf 
3329ab364a6fSThomas Graf 	if (tb[RTA_OIF])
333072331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3331ab364a6fSThomas Graf 
33322e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
33332e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
33342e47b291SLorenzo Colitti 
3335ab364a6fSThomas Graf 	if (iif) {
3336ab364a6fSThomas Graf 		struct net_device *dev;
333772331bc0SShmulik Ladkani 		int flags = 0;
333872331bc0SShmulik Ladkani 
33395578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3340ab364a6fSThomas Graf 		if (!dev) {
3341ab364a6fSThomas Graf 			err = -ENODEV;
3342ab364a6fSThomas Graf 			goto errout;
3343ab364a6fSThomas Graf 		}
334472331bc0SShmulik Ladkani 
334572331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
334672331bc0SShmulik Ladkani 
334772331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
334872331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
334972331bc0SShmulik Ladkani 
335072331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
335172331bc0SShmulik Ladkani 							       flags);
335272331bc0SShmulik Ladkani 	} else {
335372331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
335472331bc0SShmulik Ladkani 
3355ca254490SDavid Ahern 		if (netif_index_is_l3_master(net, oif)) {
3356ca254490SDavid Ahern 			fl6.flowi6_flags = FLOWI_FLAG_L3MDEV_SRC |
3357ca254490SDavid Ahern 					   FLOWI_FLAG_SKIP_NH_OIF;
3358ca254490SDavid Ahern 		}
3359ca254490SDavid Ahern 
336072331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3361ab364a6fSThomas Graf 	}
33621da177e4SLinus Torvalds 
33631da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
336438308473SDavid S. Miller 	if (!skb) {
336594e187c0SAmerigo Wang 		ip6_rt_put(rt);
3366ab364a6fSThomas Graf 		err = -ENOBUFS;
3367ab364a6fSThomas Graf 		goto errout;
3368ab364a6fSThomas Graf 	}
33691da177e4SLinus Torvalds 
33701da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
33711da177e4SLinus Torvalds 	   through good chunk of routing engine.
33721da177e4SLinus Torvalds 	 */
3373459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
33741da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
33751da177e4SLinus Torvalds 
3376d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
33771da177e4SLinus Torvalds 
33784c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
337915e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
33807bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
33811da177e4SLinus Torvalds 	if (err < 0) {
3382ab364a6fSThomas Graf 		kfree_skb(skb);
3383ab364a6fSThomas Graf 		goto errout;
33841da177e4SLinus Torvalds 	}
33851da177e4SLinus Torvalds 
338615e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3387ab364a6fSThomas Graf errout:
33881da177e4SLinus Torvalds 	return err;
33891da177e4SLinus Torvalds }
33901da177e4SLinus Torvalds 
339137a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
339237a1d361SRoopa Prabhu 		     unsigned int nlm_flags)
33931da177e4SLinus Torvalds {
33941da177e4SLinus Torvalds 	struct sk_buff *skb;
33955578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3396528c4cebSDenis V. Lunev 	u32 seq;
3397528c4cebSDenis V. Lunev 	int err;
33980d51aa80SJamal Hadi Salim 
3399528c4cebSDenis V. Lunev 	err = -ENOBUFS;
340038308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
340186872cb5SThomas Graf 
340219e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
340338308473SDavid S. Miller 	if (!skb)
340421713ebcSThomas Graf 		goto errout;
34051da177e4SLinus Torvalds 
3406191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
340737a1d361SRoopa Prabhu 				event, info->portid, seq, 0, 0, nlm_flags);
340826932566SPatrick McHardy 	if (err < 0) {
340926932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
341026932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
341126932566SPatrick McHardy 		kfree_skb(skb);
341226932566SPatrick McHardy 		goto errout;
341326932566SPatrick McHardy 	}
341415e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
34155578689aSDaniel Lezcano 		    info->nlh, gfp_any());
34161ce85fe4SPablo Neira Ayuso 	return;
341721713ebcSThomas Graf errout:
341821713ebcSThomas Graf 	if (err < 0)
34195578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
34201da177e4SLinus Torvalds }
34211da177e4SLinus Torvalds 
34228ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3423351638e7SJiri Pirko 				unsigned long event, void *ptr)
34248ed67789SDaniel Lezcano {
3425351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3426c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
34278ed67789SDaniel Lezcano 
34288ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3429d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
34308ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
34318ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3432d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
34338ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3434d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
34358ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
34368ed67789SDaniel Lezcano #endif
34378ed67789SDaniel Lezcano 	}
34388ed67789SDaniel Lezcano 
34398ed67789SDaniel Lezcano 	return NOTIFY_OK;
34408ed67789SDaniel Lezcano }
34418ed67789SDaniel Lezcano 
34421da177e4SLinus Torvalds /*
34431da177e4SLinus Torvalds  *	/proc
34441da177e4SLinus Torvalds  */
34451da177e4SLinus Torvalds 
34461da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
34471da177e4SLinus Torvalds 
344833120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
344933120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
345033120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
345133120b30SAlexey Dobriyan 	.read		= seq_read,
345233120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
34538d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
345433120b30SAlexey Dobriyan };
345533120b30SAlexey Dobriyan 
34561da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
34571da177e4SLinus Torvalds {
345869ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
34591da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
346069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
346169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
346269ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
346369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
346469ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3465fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
346669ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
34671da177e4SLinus Torvalds 
34681da177e4SLinus Torvalds 	return 0;
34691da177e4SLinus Torvalds }
34701da177e4SLinus Torvalds 
34711da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
34721da177e4SLinus Torvalds {
3473de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
347469ddb805SDaniel Lezcano }
347569ddb805SDaniel Lezcano 
34769a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
34771da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
34781da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
34791da177e4SLinus Torvalds 	.read	 = seq_read,
34801da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3481b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
34821da177e4SLinus Torvalds };
34831da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
34841da177e4SLinus Torvalds 
34851da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
34861da177e4SLinus Torvalds 
34871da177e4SLinus Torvalds static
3488fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
34891da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
34901da177e4SLinus Torvalds {
3491c486da34SLucian Adrian Grijincu 	struct net *net;
3492c486da34SLucian Adrian Grijincu 	int delay;
3493c486da34SLucian Adrian Grijincu 	if (!write)
3494c486da34SLucian Adrian Grijincu 		return -EINVAL;
3495c486da34SLucian Adrian Grijincu 
3496c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3497c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
34988d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
34992ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
35001da177e4SLinus Torvalds 	return 0;
35011da177e4SLinus Torvalds }
35021da177e4SLinus Torvalds 
3503fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
35041da177e4SLinus Torvalds 	{
35051da177e4SLinus Torvalds 		.procname	=	"flush",
35064990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
35071da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
350889c8b3a1SDave Jones 		.mode		=	0200,
35096d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
35101da177e4SLinus Torvalds 	},
35111da177e4SLinus Torvalds 	{
35121da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
35139a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
35141da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35151da177e4SLinus Torvalds 		.mode		=	0644,
35166d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
35171da177e4SLinus Torvalds 	},
35181da177e4SLinus Torvalds 	{
35191da177e4SLinus Torvalds 		.procname	=	"max_size",
35204990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
35211da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35221da177e4SLinus Torvalds 		.mode		=	0644,
35236d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
35241da177e4SLinus Torvalds 	},
35251da177e4SLinus Torvalds 	{
35261da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
35274990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
35281da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35291da177e4SLinus Torvalds 		.mode		=	0644,
35306d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
35311da177e4SLinus Torvalds 	},
35321da177e4SLinus Torvalds 	{
35331da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
35344990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
35351da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35361da177e4SLinus Torvalds 		.mode		=	0644,
35376d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
35381da177e4SLinus Torvalds 	},
35391da177e4SLinus Torvalds 	{
35401da177e4SLinus Torvalds 		.procname	=	"gc_interval",
35414990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
35421da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35431da177e4SLinus Torvalds 		.mode		=	0644,
35446d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
35451da177e4SLinus Torvalds 	},
35461da177e4SLinus Torvalds 	{
35471da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
35484990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
35491da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35501da177e4SLinus Torvalds 		.mode		=	0644,
3551f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
35521da177e4SLinus Torvalds 	},
35531da177e4SLinus Torvalds 	{
35541da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
35554990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
35561da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35571da177e4SLinus Torvalds 		.mode		=	0644,
35586d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
35591da177e4SLinus Torvalds 	},
35601da177e4SLinus Torvalds 	{
35611da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
35624990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
35631da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35641da177e4SLinus Torvalds 		.mode		=	0644,
3565f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
35661da177e4SLinus Torvalds 	},
35671da177e4SLinus Torvalds 	{
35681da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
35694990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
35701da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
35711da177e4SLinus Torvalds 		.mode		=	0644,
35726d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
35731da177e4SLinus Torvalds 	},
3574f8572d8fSEric W. Biederman 	{ }
35751da177e4SLinus Torvalds };
35761da177e4SLinus Torvalds 
35772c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3578760f2d01SDaniel Lezcano {
3579760f2d01SDaniel Lezcano 	struct ctl_table *table;
3580760f2d01SDaniel Lezcano 
3581760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3582760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3583760f2d01SDaniel Lezcano 			GFP_KERNEL);
35845ee09105SYOSHIFUJI Hideaki 
35855ee09105SYOSHIFUJI Hideaki 	if (table) {
35865ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3587c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
358886393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
35895ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
35905ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
35915ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
35925ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
35935ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
35945ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
35955ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
35969c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3597464dc801SEric W. Biederman 
3598464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3599464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3600464dc801SEric W. Biederman 			table[0].procname = NULL;
36015ee09105SYOSHIFUJI Hideaki 	}
36025ee09105SYOSHIFUJI Hideaki 
3603760f2d01SDaniel Lezcano 	return table;
3604760f2d01SDaniel Lezcano }
36051da177e4SLinus Torvalds #endif
36061da177e4SLinus Torvalds 
36072c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3608cdb18761SDaniel Lezcano {
3609633d424bSPavel Emelyanov 	int ret = -ENOMEM;
36108ed67789SDaniel Lezcano 
361186393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
361286393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3613f2fc6a54SBenjamin Thery 
3614fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3615fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3616fc66f95cSEric Dumazet 
36178ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
36188ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
36198ed67789SDaniel Lezcano 					   GFP_KERNEL);
36208ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3621fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3622d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
36238ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3624d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
362562fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
362662fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
36278ed67789SDaniel Lezcano 
36288ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
36298ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
36308ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
36318ed67789SDaniel Lezcano 					       GFP_KERNEL);
363268fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
363368fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3634d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
36358ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3636d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
363762fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
363862fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
36398ed67789SDaniel Lezcano 
36408ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
36418ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
36428ed67789SDaniel Lezcano 					       GFP_KERNEL);
364368fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
364468fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3645d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
36468ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3647d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
364862fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
364962fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
36508ed67789SDaniel Lezcano #endif
36518ed67789SDaniel Lezcano 
3652b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3653b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3654b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3655b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3656b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3657b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3658b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3659b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3660b339a47cSPeter Zijlstra 
36616891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
36626891a346SBenjamin Thery 
36638ed67789SDaniel Lezcano 	ret = 0;
36648ed67789SDaniel Lezcano out:
36658ed67789SDaniel Lezcano 	return ret;
3666f2fc6a54SBenjamin Thery 
366768fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
366868fffc67SPeter Zijlstra out_ip6_prohibit_entry:
366968fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
367068fffc67SPeter Zijlstra out_ip6_null_entry:
367168fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
367268fffc67SPeter Zijlstra #endif
3673fc66f95cSEric Dumazet out_ip6_dst_entries:
3674fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3675f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3676f2fc6a54SBenjamin Thery 	goto out;
3677cdb18761SDaniel Lezcano }
3678cdb18761SDaniel Lezcano 
36792c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3680cdb18761SDaniel Lezcano {
36818ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
36828ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
36838ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
36848ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
36858ed67789SDaniel Lezcano #endif
368641bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3687cdb18761SDaniel Lezcano }
3688cdb18761SDaniel Lezcano 
3689d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3690d189634eSThomas Graf {
3691d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3692d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3693d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3694d189634eSThomas Graf #endif
3695d189634eSThomas Graf 	return 0;
3696d189634eSThomas Graf }
3697d189634eSThomas Graf 
3698d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3699d189634eSThomas Graf {
3700d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3701ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3702ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3703d189634eSThomas Graf #endif
3704d189634eSThomas Graf }
3705d189634eSThomas Graf 
3706cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3707cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3708cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3709cdb18761SDaniel Lezcano };
3710cdb18761SDaniel Lezcano 
3711c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3712c3426b47SDavid S. Miller {
3713c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3714c3426b47SDavid S. Miller 
3715c3426b47SDavid S. Miller 	if (!bp)
3716c3426b47SDavid S. Miller 		return -ENOMEM;
3717c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3718c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3719c3426b47SDavid S. Miller 	return 0;
3720c3426b47SDavid S. Miller }
3721c3426b47SDavid S. Miller 
3722c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3723c3426b47SDavid S. Miller {
3724c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3725c3426b47SDavid S. Miller 
3726c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
372756a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3728c3426b47SDavid S. Miller 	kfree(bp);
3729c3426b47SDavid S. Miller }
3730c3426b47SDavid S. Miller 
37312b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3732c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3733c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3734c3426b47SDavid S. Miller };
3735c3426b47SDavid S. Miller 
3736d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3737d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3738d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3739d189634eSThomas Graf };
3740d189634eSThomas Graf 
37418ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
37428ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
37438ed67789SDaniel Lezcano 	.priority = 0,
37448ed67789SDaniel Lezcano };
37458ed67789SDaniel Lezcano 
3746433d49c3SDaniel Lezcano int __init ip6_route_init(void)
37471da177e4SLinus Torvalds {
3748433d49c3SDaniel Lezcano 	int ret;
37498d0b94afSMartin KaFai Lau 	int cpu;
3750433d49c3SDaniel Lezcano 
37519a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
37529a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
37539a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
37549a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
37559a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3756c19a28e1SFernando Carrijo 		goto out;
375714e50e57SDavid S. Miller 
3758fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
37598ed67789SDaniel Lezcano 	if (ret)
3760bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3761bdb3289fSDaniel Lezcano 
3762c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3763c3426b47SDavid S. Miller 	if (ret)
3764e8803b6cSDavid S. Miller 		goto out_dst_entries;
37652a0c451aSThomas Graf 
37667e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
37677e52b33bSDavid S. Miller 	if (ret)
37687e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3769c3426b47SDavid S. Miller 
37705dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
37715dc121e9SArnaud Ebalard 
37728ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
37738ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
37748ed67789SDaniel Lezcano 	 * manually for init_net */
3775d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
37768ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3777bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3778d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
37798ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3780d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
37818ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3782bdb3289fSDaniel Lezcano   #endif
3783e8803b6cSDavid S. Miller 	ret = fib6_init();
3784433d49c3SDaniel Lezcano 	if (ret)
37858ed67789SDaniel Lezcano 		goto out_register_subsys;
3786433d49c3SDaniel Lezcano 
3787433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3788433d49c3SDaniel Lezcano 	if (ret)
3789e8803b6cSDavid S. Miller 		goto out_fib6_init;
3790c35b7e72SDaniel Lezcano 
3791433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3792433d49c3SDaniel Lezcano 	if (ret)
3793433d49c3SDaniel Lezcano 		goto xfrm6_init;
37947e5449c2SDaniel Lezcano 
3795d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3796d189634eSThomas Graf 	if (ret)
3797d189634eSThomas Graf 		goto fib6_rules_init;
3798d189634eSThomas Graf 
3799433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3800c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3801c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3802c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3803d189634eSThomas Graf 		goto out_register_late_subsys;
3804433d49c3SDaniel Lezcano 
38058ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3806cdb18761SDaniel Lezcano 	if (ret)
3807d189634eSThomas Graf 		goto out_register_late_subsys;
38088ed67789SDaniel Lezcano 
38098d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
38108d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
38118d0b94afSMartin KaFai Lau 
38128d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
38138d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
38148d0b94afSMartin KaFai Lau 	}
38158d0b94afSMartin KaFai Lau 
3816433d49c3SDaniel Lezcano out:
3817433d49c3SDaniel Lezcano 	return ret;
3818433d49c3SDaniel Lezcano 
3819d189634eSThomas Graf out_register_late_subsys:
3820d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3821433d49c3SDaniel Lezcano fib6_rules_init:
3822433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3823433d49c3SDaniel Lezcano xfrm6_init:
3824433d49c3SDaniel Lezcano 	xfrm6_fini();
38252a0c451aSThomas Graf out_fib6_init:
38262a0c451aSThomas Graf 	fib6_gc_cleanup();
38278ed67789SDaniel Lezcano out_register_subsys:
38288ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
38297e52b33bSDavid S. Miller out_register_inetpeer:
38307e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3831fc66f95cSEric Dumazet out_dst_entries:
3832fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3833433d49c3SDaniel Lezcano out_kmem_cache:
3834f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3835433d49c3SDaniel Lezcano 	goto out;
38361da177e4SLinus Torvalds }
38371da177e4SLinus Torvalds 
38381da177e4SLinus Torvalds void ip6_route_cleanup(void)
38391da177e4SLinus Torvalds {
38408ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3841d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3842101367c2SThomas Graf 	fib6_rules_cleanup();
38431da177e4SLinus Torvalds 	xfrm6_fini();
38441da177e4SLinus Torvalds 	fib6_gc_cleanup();
3845c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
38468ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
384741bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3848f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
38491da177e4SLinus Torvalds }
3850