xref: /openbmc/linux/net/ipv6/route.c (revision 8e3d5be7368107f0c27a1f8126d79b01a47e9567)
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>
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds #include <asm/uaccess.h>
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
681da177e4SLinus Torvalds #include <linux/sysctl.h>
691da177e4SLinus Torvalds #endif
701da177e4SLinus Torvalds 
71afc154e9SHannes Frederic Sowa enum rt6_nud_state {
727e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
737e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
747e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
75afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
76afc154e9SHannes Frederic Sowa };
77afc154e9SHannes Frederic Sowa 
7883a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort);
791da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
800dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
81ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
821da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
831da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
841da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
851da177e4SLinus Torvalds 				       struct net_device *dev, int how);
86569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
89aad88724SEric Dumazet static int		ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb);
907150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
91aad88724SEric Dumazet static int		ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb);
921da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
936700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
946700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
956700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
966700c270SDavid S. Miller 					struct sk_buff *skb);
974b32b5adSMartin KaFai Lau static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
9852bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
991da177e4SLinus Torvalds 
10070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
101efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
102b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
103b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
10495c96174SEric Dumazet 					   unsigned int pref);
105efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
106b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
107b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
10870ceb4f5SYOSHIFUJI Hideaki #endif
10970ceb4f5SYOSHIFUJI Hideaki 
1108d0b94afSMartin KaFai Lau struct uncached_list {
1118d0b94afSMartin KaFai Lau 	spinlock_t		lock;
1128d0b94afSMartin KaFai Lau 	struct list_head	head;
1138d0b94afSMartin KaFai Lau };
1148d0b94afSMartin KaFai Lau 
1158d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
1168d0b94afSMartin KaFai Lau 
1178d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt)
1188d0b94afSMartin KaFai Lau {
1198d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
1208d0b94afSMartin KaFai Lau 
1218d0b94afSMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
1228d0b94afSMartin KaFai Lau 	rt->rt6i_uncached_list = ul;
1238d0b94afSMartin KaFai Lau 
1248d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
1258d0b94afSMartin KaFai Lau 	list_add_tail(&rt->rt6i_uncached, &ul->head);
1268d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
1278d0b94afSMartin KaFai Lau }
1288d0b94afSMartin KaFai Lau 
1298d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt)
1308d0b94afSMartin KaFai Lau {
1318d0b94afSMartin KaFai Lau 	if (!list_empty(&rt->rt6i_uncached)) {
1328d0b94afSMartin KaFai Lau 		struct uncached_list *ul = rt->rt6i_uncached_list;
1338d0b94afSMartin KaFai Lau 
1348d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1358d0b94afSMartin KaFai Lau 		list_del(&rt->rt6i_uncached);
1368d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1378d0b94afSMartin KaFai Lau 	}
1388d0b94afSMartin KaFai Lau }
1398d0b94afSMartin KaFai Lau 
1408d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
1418d0b94afSMartin KaFai Lau {
1428d0b94afSMartin KaFai Lau 	struct net_device *loopback_dev = net->loopback_dev;
1438d0b94afSMartin KaFai Lau 	int cpu;
1448d0b94afSMartin KaFai Lau 
1458d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
1468d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
1478d0b94afSMartin KaFai Lau 		struct rt6_info *rt;
1488d0b94afSMartin KaFai Lau 
1498d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
1508d0b94afSMartin KaFai Lau 		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
1518d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
1528d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
1538d0b94afSMartin KaFai Lau 
1548d0b94afSMartin KaFai Lau 			if (rt_idev && (rt_idev->dev == dev || !dev) &&
1558d0b94afSMartin KaFai Lau 			    rt_idev->dev != loopback_dev) {
1568d0b94afSMartin KaFai Lau 				rt->rt6i_idev = in6_dev_get(loopback_dev);
1578d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
1588d0b94afSMartin KaFai Lau 			}
1598d0b94afSMartin KaFai Lau 
1608d0b94afSMartin KaFai Lau 			if (rt_dev && (rt_dev == dev || !dev) &&
1618d0b94afSMartin KaFai Lau 			    rt_dev != loopback_dev) {
1628d0b94afSMartin KaFai Lau 				rt->dst.dev = loopback_dev;
1638d0b94afSMartin KaFai Lau 				dev_hold(rt->dst.dev);
1648d0b94afSMartin KaFai Lau 				dev_put(rt_dev);
1658d0b94afSMartin KaFai Lau 			}
1668d0b94afSMartin KaFai Lau 		}
1678d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
1688d0b94afSMartin KaFai Lau 	}
1698d0b94afSMartin KaFai Lau }
1708d0b94afSMartin KaFai Lau 
171d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
172d52d3997SMartin KaFai Lau {
173d52d3997SMartin KaFai Lau 	return dst_metrics_write_ptr(rt->dst.from);
174d52d3997SMartin KaFai Lau }
175d52d3997SMartin KaFai Lau 
17606582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
17706582540SDavid S. Miller {
17806582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
17906582540SDavid S. Miller 
180d52d3997SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_PCPU)
181d52d3997SMartin KaFai Lau 		return rt6_pcpu_cow_metrics(rt);
182d52d3997SMartin KaFai Lau 	else if (rt->rt6i_flags & RTF_CACHE)
1834b32b5adSMartin KaFai Lau 		return NULL;
1844b32b5adSMartin KaFai Lau 	else
1853b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
18606582540SDavid S. Miller }
18706582540SDavid S. Miller 
188f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
189f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
190f894cbf8SDavid S. Miller 					     const void *daddr)
19139232973SDavid S. Miller {
19239232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
19339232973SDavid S. Miller 
194a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
19539232973SDavid S. Miller 		return (const void *) p;
196f894cbf8SDavid S. Miller 	else if (skb)
197f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
19839232973SDavid S. Miller 	return daddr;
19939232973SDavid S. Miller }
20039232973SDavid S. Miller 
201f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
202f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
203f894cbf8SDavid S. Miller 					  const void *daddr)
204d3aaeb38SDavid S. Miller {
20539232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
20639232973SDavid S. Miller 	struct neighbour *n;
20739232973SDavid S. Miller 
208f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
2098e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
210f83c7790SDavid S. Miller 	if (n)
211f83c7790SDavid S. Miller 		return n;
212f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
213f83c7790SDavid S. Miller }
214f83c7790SDavid S. Miller 
2159a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2161da177e4SLinus Torvalds 	.family			=	AF_INET6,
2171da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2181da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2191da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2200dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
221ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
22206582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
2231da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2241da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2251da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2261da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2271da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2286e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2291ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
230d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
2311da177e4SLinus Torvalds };
2321da177e4SLinus Torvalds 
233ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
234ec831ea7SRoland Dreier {
235618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
236618f9bc7SSteffen Klassert 
237618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
238ec831ea7SRoland Dreier }
239ec831ea7SRoland Dreier 
2406700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2416700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
24214e50e57SDavid S. Miller {
24314e50e57SDavid S. Miller }
24414e50e57SDavid S. Miller 
2456700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2466700c270SDavid S. Miller 				      struct sk_buff *skb)
247b587ee3bSDavid S. Miller {
248b587ee3bSDavid S. Miller }
249b587ee3bSDavid S. Miller 
2500972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
2510972ddb2SHeld Bernhard 					 unsigned long old)
2520972ddb2SHeld Bernhard {
2530972ddb2SHeld Bernhard 	return NULL;
2540972ddb2SHeld Bernhard }
2550972ddb2SHeld Bernhard 
25614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
25714e50e57SDavid S. Miller 	.family			=	AF_INET6,
25814e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
25914e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
260ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
261214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
26214e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
263b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2640972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
265d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
26614e50e57SDavid S. Miller };
26714e50e57SDavid S. Miller 
26862fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
26914edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
27062fa8a84SDavid S. Miller };
27162fa8a84SDavid S. Miller 
272fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2731da177e4SLinus Torvalds 	.dst = {
2741da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2751da177e4SLinus Torvalds 		.__use		= 1,
2762c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2771da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2781da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2791da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2801da177e4SLinus Torvalds 	},
2811da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2824f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2831da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2841da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2851da177e4SLinus Torvalds };
2861da177e4SLinus Torvalds 
287101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
288101367c2SThomas Graf 
289fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
290101367c2SThomas Graf 	.dst = {
291101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
292101367c2SThomas Graf 		.__use		= 1,
2932c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
294101367c2SThomas Graf 		.error		= -EACCES,
2959ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2969ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
297101367c2SThomas Graf 	},
298101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2994f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
300101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
301101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
302101367c2SThomas Graf };
303101367c2SThomas Graf 
304fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
305101367c2SThomas Graf 	.dst = {
306101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
307101367c2SThomas Graf 		.__use		= 1,
3082c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
309101367c2SThomas Graf 		.error		= -EINVAL,
310352e512cSHerbert Xu 		.input		= dst_discard,
311aad88724SEric Dumazet 		.output		= dst_discard_sk,
312101367c2SThomas Graf 	},
313101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3144f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
315101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
316101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
317101367c2SThomas Graf };
318101367c2SThomas Graf 
319101367c2SThomas Graf #endif
320101367c2SThomas Graf 
3211da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
322d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net,
323957c665fSDavid S. Miller 					struct net_device *dev,
324ad706862SMartin KaFai Lau 					int flags)
3251da177e4SLinus Torvalds {
32697bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3276f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
328cf911662SDavid S. Miller 
32997bab73fSDavid S. Miller 	if (rt) {
3308104891bSSteffen Klassert 		struct dst_entry *dst = &rt->dst;
3318104891bSSteffen Klassert 
3328104891bSSteffen Klassert 		memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
33351ebd318SNicolas Dichtel 		INIT_LIST_HEAD(&rt->rt6i_siblings);
3348d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&rt->rt6i_uncached);
33597bab73fSDavid S. Miller 	}
336cf911662SDavid S. Miller 	return rt;
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds 
339d52d3997SMartin KaFai Lau static struct rt6_info *ip6_dst_alloc(struct net *net,
340d52d3997SMartin KaFai Lau 				      struct net_device *dev,
341ad706862SMartin KaFai Lau 				      int flags)
342d52d3997SMartin KaFai Lau {
343ad706862SMartin KaFai Lau 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
344d52d3997SMartin KaFai Lau 
345d52d3997SMartin KaFai Lau 	if (rt) {
346d52d3997SMartin KaFai Lau 		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
347d52d3997SMartin KaFai Lau 		if (rt->rt6i_pcpu) {
348d52d3997SMartin KaFai Lau 			int cpu;
349d52d3997SMartin KaFai Lau 
350d52d3997SMartin KaFai Lau 			for_each_possible_cpu(cpu) {
351d52d3997SMartin KaFai Lau 				struct rt6_info **p;
352d52d3997SMartin KaFai Lau 
353d52d3997SMartin KaFai Lau 				p = per_cpu_ptr(rt->rt6i_pcpu, cpu);
354d52d3997SMartin KaFai Lau 				/* no one shares rt */
355d52d3997SMartin KaFai Lau 				*p =  NULL;
356d52d3997SMartin KaFai Lau 			}
357d52d3997SMartin KaFai Lau 		} else {
358d52d3997SMartin KaFai Lau 			dst_destroy((struct dst_entry *)rt);
359d52d3997SMartin KaFai Lau 			return NULL;
360d52d3997SMartin KaFai Lau 		}
361d52d3997SMartin KaFai Lau 	}
362d52d3997SMartin KaFai Lau 
363d52d3997SMartin KaFai Lau 	return rt;
364d52d3997SMartin KaFai Lau }
365d52d3997SMartin KaFai Lau 
3661da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
369ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
3708d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3711da177e4SLinus Torvalds 
3728e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
373d52d3997SMartin KaFai Lau 	free_percpu(rt->rt6i_pcpu);
3748d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
3758d0b94afSMartin KaFai Lau 
3768d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
37738308473SDavid S. Miller 	if (idev) {
3781da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3791da177e4SLinus Torvalds 		in6_dev_put(idev);
3801da177e4SLinus Torvalds 	}
3811716a961SGao feng 
382ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
383ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
384b3419363SDavid S. Miller }
385b3419363SDavid S. Miller 
3861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3871da177e4SLinus Torvalds 			   int how)
3881da177e4SLinus Torvalds {
3891da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3901da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3915a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
392c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3931da177e4SLinus Torvalds 
39497cac082SDavid S. Miller 	if (dev != loopback_dev) {
39597cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3965a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3975a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
39838308473SDavid S. Miller 			if (loopback_idev) {
3991da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
4001da177e4SLinus Torvalds 				in6_dev_put(idev);
4011da177e4SLinus Torvalds 			}
4021da177e4SLinus Torvalds 		}
40397cac082SDavid S. Miller 	}
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds 
406a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
4071da177e4SLinus Torvalds {
4081716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
4091716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
410a50feda5SEric Dumazet 			return true;
4111716a961SGao feng 	} else if (rt->dst.from) {
4123fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
4131716a961SGao feng 	}
414a50feda5SEric Dumazet 	return false;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
41751ebd318SNicolas Dichtel /* Multipath route selection:
41851ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
41951ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
42051ebd318SNicolas Dichtel  */
42151ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
42251ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
42351ebd318SNicolas Dichtel {
42451ebd318SNicolas Dichtel 	unsigned int val = fl6->flowi6_proto;
42551ebd318SNicolas Dichtel 
426c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->daddr);
427c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->saddr);
42851ebd318SNicolas Dichtel 
42951ebd318SNicolas Dichtel 	/* Work only if this not encapsulated */
43051ebd318SNicolas Dichtel 	switch (fl6->flowi6_proto) {
43151ebd318SNicolas Dichtel 	case IPPROTO_UDP:
43251ebd318SNicolas Dichtel 	case IPPROTO_TCP:
43351ebd318SNicolas Dichtel 	case IPPROTO_SCTP:
434b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_sport;
435b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_dport;
43651ebd318SNicolas Dichtel 		break;
43751ebd318SNicolas Dichtel 
43851ebd318SNicolas Dichtel 	case IPPROTO_ICMPV6:
439b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_type;
440b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_code;
44151ebd318SNicolas Dichtel 		break;
44251ebd318SNicolas Dichtel 	}
44351ebd318SNicolas Dichtel 	/* RFC6438 recommands to use flowlabel */
444b3ce5ae1SNicolas Dichtel 	val ^= (__force u32)fl6->flowlabel;
44551ebd318SNicolas Dichtel 
44651ebd318SNicolas Dichtel 	/* Perhaps, we need to tune, this function? */
44751ebd318SNicolas Dichtel 	val = val ^ (val >> 7) ^ (val >> 12);
44851ebd318SNicolas Dichtel 	return val % candidate_count;
44951ebd318SNicolas Dichtel }
45051ebd318SNicolas Dichtel 
45151ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
45252bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
45352bd4c0cSNicolas Dichtel 					     int strict)
45451ebd318SNicolas Dichtel {
45551ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
45651ebd318SNicolas Dichtel 	int route_choosen;
45751ebd318SNicolas Dichtel 
45851ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
45951ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
46051ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
46151ebd318SNicolas Dichtel 	 */
46251ebd318SNicolas Dichtel 	if (route_choosen)
46351ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
46451ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
46551ebd318SNicolas Dichtel 			route_choosen--;
46651ebd318SNicolas Dichtel 			if (route_choosen == 0) {
46752bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
46852bd4c0cSNicolas Dichtel 					break;
46951ebd318SNicolas Dichtel 				match = sibling;
47051ebd318SNicolas Dichtel 				break;
47151ebd318SNicolas Dichtel 			}
47251ebd318SNicolas Dichtel 		}
47351ebd318SNicolas Dichtel 	return match;
47451ebd318SNicolas Dichtel }
47551ebd318SNicolas Dichtel 
4761da177e4SLinus Torvalds /*
477c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4781da177e4SLinus Torvalds  */
4791da177e4SLinus Torvalds 
4808ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4818ed67789SDaniel Lezcano 						    struct rt6_info *rt,
482b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4831da177e4SLinus Torvalds 						    int oif,
484d420895eSYOSHIFUJI Hideaki 						    int flags)
4851da177e4SLinus Torvalds {
4861da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4871da177e4SLinus Torvalds 	struct rt6_info *sprt;
4881da177e4SLinus Torvalds 
489dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
490dd3abc4eSYOSHIFUJI Hideaki 		goto out;
491dd3abc4eSYOSHIFUJI Hideaki 
492d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
493d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
494dd3abc4eSYOSHIFUJI Hideaki 
495dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4961da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4971da177e4SLinus Torvalds 				return sprt;
4981da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
49938308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
5001da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
501d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
5021da177e4SLinus Torvalds 						continue;
5031da177e4SLinus Torvalds 					if (local && (!oif ||
5041da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
5051da177e4SLinus Torvalds 						continue;
5061da177e4SLinus Torvalds 				}
5071da177e4SLinus Torvalds 				local = sprt;
5081da177e4SLinus Torvalds 			}
509dd3abc4eSYOSHIFUJI Hideaki 		} else {
510dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
511dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
512dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
513dd3abc4eSYOSHIFUJI Hideaki 		}
5141da177e4SLinus Torvalds 	}
5151da177e4SLinus Torvalds 
516dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
5171da177e4SLinus Torvalds 		if (local)
5181da177e4SLinus Torvalds 			return local;
5191da177e4SLinus Torvalds 
520d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
5218ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
5221da177e4SLinus Torvalds 	}
523dd3abc4eSYOSHIFUJI Hideaki out:
5241da177e4SLinus Torvalds 	return rt;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
52727097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
528c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
529c2f17e82SHannes Frederic Sowa 	struct work_struct work;
530c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
531c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
532c2f17e82SHannes Frederic Sowa };
533c2f17e82SHannes Frederic Sowa 
534c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
535c2f17e82SHannes Frederic Sowa {
536c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
537c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
538c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
539c2f17e82SHannes Frederic Sowa 
540c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
541ab450605SJiri Benc 	ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL, NULL);
542c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
543662f5533SMichael Büsch 	kfree(work);
544c2f17e82SHannes Frederic Sowa }
545c2f17e82SHannes Frederic Sowa 
54627097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
54727097255SYOSHIFUJI Hideaki {
548990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
549f2c31e32SEric Dumazet 	struct neighbour *neigh;
55027097255SYOSHIFUJI Hideaki 	/*
55127097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
55227097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
55327097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
55427097255SYOSHIFUJI Hideaki 	 *
55527097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
55627097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
55727097255SYOSHIFUJI Hideaki 	 */
5582152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
559fdd6681dSAmerigo Wang 		return;
5602152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5612152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5622152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5638d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5648d6c31bfSMartin KaFai Lau 			goto out;
5658d6c31bfSMartin KaFai Lau 
566990edb42SMartin KaFai Lau 		work = NULL;
5672152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
568990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
569990edb42SMartin KaFai Lau 		    time_after(jiffies,
570990edb42SMartin KaFai Lau 			       neigh->updated +
571990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
572c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
573990edb42SMartin KaFai Lau 			if (work)
5747e980569SJiri Benc 				__neigh_set_probe_once(neigh);
575990edb42SMartin KaFai Lau 		}
576c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
577990edb42SMartin KaFai Lau 	} else {
578990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
579990edb42SMartin KaFai Lau 	}
580c2f17e82SHannes Frederic Sowa 
581c2f17e82SHannes Frederic Sowa 	if (work) {
582c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
583c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
584c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
585c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
586c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
587c2f17e82SHannes Frederic Sowa 	}
588990edb42SMartin KaFai Lau 
5898d6c31bfSMartin KaFai Lau out:
5902152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
591f2c31e32SEric Dumazet }
59227097255SYOSHIFUJI Hideaki #else
59327097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
59427097255SYOSHIFUJI Hideaki {
59527097255SYOSHIFUJI Hideaki }
59627097255SYOSHIFUJI Hideaki #endif
59727097255SYOSHIFUJI Hideaki 
5981da177e4SLinus Torvalds /*
599554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
6001da177e4SLinus Torvalds  */
601b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
6021da177e4SLinus Torvalds {
603d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
604161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
605554cfb7eSYOSHIFUJI Hideaki 		return 2;
606161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
607161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
608161980f4SDavid S. Miller 		return 1;
609554cfb7eSYOSHIFUJI Hideaki 	return 0;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds 
612afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
6131da177e4SLinus Torvalds {
614f2c31e32SEric Dumazet 	struct neighbour *neigh;
615afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
616f2c31e32SEric Dumazet 
6174d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
6184d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
619afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
620145a3621SYOSHIFUJI Hideaki / 吉藤英明 
621145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
622145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
623145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
624145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
625554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
626afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
627398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
628a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
629afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6307e980569SJiri Benc 		else
6317e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
632398bcbebSYOSHIFUJI Hideaki #endif
633145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
634afc154e9SHannes Frederic Sowa 	} else {
635afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6367e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
637a5a81f0bSPaul Marks 	}
638145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
639145a3621SYOSHIFUJI Hideaki / 吉藤英明 
640a5a81f0bSPaul Marks 	return ret;
6411da177e4SLinus Torvalds }
6421da177e4SLinus Torvalds 
643554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
644554cfb7eSYOSHIFUJI Hideaki 			   int strict)
645554cfb7eSYOSHIFUJI Hideaki {
646a5a81f0bSPaul Marks 	int m;
6474d0c5911SYOSHIFUJI Hideaki 
6484d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
64977d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
650afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
651ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
652ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
653ebacaaa0SYOSHIFUJI Hideaki #endif
654afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
655afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
656afc154e9SHannes Frederic Sowa 		if (n < 0)
657afc154e9SHannes Frederic Sowa 			return n;
658afc154e9SHannes Frederic Sowa 	}
659554cfb7eSYOSHIFUJI Hideaki 	return m;
660554cfb7eSYOSHIFUJI Hideaki }
661554cfb7eSYOSHIFUJI Hideaki 
662f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
663afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
664afc154e9SHannes Frederic Sowa 				   bool *do_rr)
665554cfb7eSYOSHIFUJI Hideaki {
666554cfb7eSYOSHIFUJI Hideaki 	int m;
667afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
66835103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
66935103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
67035103d11SAndy Gospodarek 
67135103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
67235103d11SAndy Gospodarek 	    idev->cnf.ignore_routes_with_linkdown)
67335103d11SAndy Gospodarek 		goto out;
674554cfb7eSYOSHIFUJI Hideaki 
675554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
676f11e6659SDavid S. Miller 		goto out;
677554cfb7eSYOSHIFUJI Hideaki 
678554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6797e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
680afc154e9SHannes Frederic Sowa 		match_do_rr = true;
681afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6827e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
683f11e6659SDavid S. Miller 		goto out;
6841da177e4SLinus Torvalds 	}
685f11e6659SDavid S. Miller 
686afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
687afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
688afc154e9SHannes Frederic Sowa 
6897e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
690afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
691afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
692afc154e9SHannes Frederic Sowa 		*mpri = m;
693afc154e9SHannes Frederic Sowa 		match = rt;
694afc154e9SHannes Frederic Sowa 	}
695f11e6659SDavid S. Miller out:
696f11e6659SDavid S. Miller 	return match;
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds 
699f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
700f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
701afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
702afc154e9SHannes Frederic Sowa 				     bool *do_rr)
703f11e6659SDavid S. Miller {
7049fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
705f11e6659SDavid S. Miller 	int mpri = -1;
706f11e6659SDavid S. Miller 
707f11e6659SDavid S. Miller 	match = NULL;
7089fbdcfafSSteffen Klassert 	cont = NULL;
7099fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
7109fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7119fbdcfafSSteffen Klassert 			cont = rt;
7129fbdcfafSSteffen Klassert 			break;
7139fbdcfafSSteffen Klassert 		}
7149fbdcfafSSteffen Klassert 
715afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7169fbdcfafSSteffen Klassert 	}
7179fbdcfafSSteffen Klassert 
7189fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
7199fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
7209fbdcfafSSteffen Klassert 			cont = rt;
7219fbdcfafSSteffen Klassert 			break;
7229fbdcfafSSteffen Klassert 		}
7239fbdcfafSSteffen Klassert 
7249fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7259fbdcfafSSteffen Klassert 	}
7269fbdcfafSSteffen Klassert 
7279fbdcfafSSteffen Klassert 	if (match || !cont)
7289fbdcfafSSteffen Klassert 		return match;
7299fbdcfafSSteffen Klassert 
7309fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
731afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
732f11e6659SDavid S. Miller 
733f11e6659SDavid S. Miller 	return match;
734f11e6659SDavid S. Miller }
735f11e6659SDavid S. Miller 
736f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
737f11e6659SDavid S. Miller {
738f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7398ed67789SDaniel Lezcano 	struct net *net;
740afc154e9SHannes Frederic Sowa 	bool do_rr = false;
741f11e6659SDavid S. Miller 
742f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
743f11e6659SDavid S. Miller 	if (!rt0)
744f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
745f11e6659SDavid S. Miller 
746afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
747afc154e9SHannes Frederic Sowa 			     &do_rr);
748f11e6659SDavid S. Miller 
749afc154e9SHannes Frederic Sowa 	if (do_rr) {
750d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
751f11e6659SDavid S. Miller 
752554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
753f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
754f11e6659SDavid S. Miller 			next = fn->leaf;
755f11e6659SDavid S. Miller 
756f11e6659SDavid S. Miller 		if (next != rt0)
757f11e6659SDavid S. Miller 			fn->rr_ptr = next;
758554cfb7eSYOSHIFUJI Hideaki 	}
759554cfb7eSYOSHIFUJI Hideaki 
760d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
761a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds 
7648b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7658b9df265SMartin KaFai Lau {
7668b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7678b9df265SMartin KaFai Lau }
7688b9df265SMartin KaFai Lau 
76970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
77070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
771b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
77270ceb4f5SYOSHIFUJI Hideaki {
773c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
77470ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
77570ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
77670ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7774bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
77870ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
77970ceb4f5SYOSHIFUJI Hideaki 
78070ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
78170ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78270ceb4f5SYOSHIFUJI Hideaki 	}
78370ceb4f5SYOSHIFUJI Hideaki 
78470ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
78570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
78670ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78770ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
78870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
78970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
79070ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
79170ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
79270ceb4f5SYOSHIFUJI Hideaki 		}
79370ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
79470ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
79570ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
79670ceb4f5SYOSHIFUJI Hideaki 		}
79770ceb4f5SYOSHIFUJI Hideaki 	}
79870ceb4f5SYOSHIFUJI Hideaki 
79970ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
80070ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
8013933fc95SJens Rosenboom 		return -EINVAL;
80270ceb4f5SYOSHIFUJI Hideaki 
8034bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
80470ceb4f5SYOSHIFUJI Hideaki 
80570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
80670ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
80770ceb4f5SYOSHIFUJI Hideaki 	else {
80870ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
80970ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
81070ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
81170ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
81270ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
81370ceb4f5SYOSHIFUJI Hideaki 	}
81470ceb4f5SYOSHIFUJI Hideaki 
815f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
816f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
817f104a567SDuan Jiong 	else
818f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
819f104a567SDuan Jiong 					gwaddr, dev->ifindex);
82070ceb4f5SYOSHIFUJI Hideaki 
82170ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
822e0a1ad73SThomas Graf 		ip6_del_rt(rt);
82370ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
82470ceb4f5SYOSHIFUJI Hideaki 	}
82570ceb4f5SYOSHIFUJI Hideaki 
82670ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
827efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
82870ceb4f5SYOSHIFUJI Hideaki 					pref);
82970ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
83070ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
83170ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
83270ceb4f5SYOSHIFUJI Hideaki 
83370ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8341716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8351716a961SGao feng 			rt6_clean_expires(rt);
8361716a961SGao feng 		else
8371716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8381716a961SGao feng 
83994e187c0SAmerigo Wang 		ip6_rt_put(rt);
84070ceb4f5SYOSHIFUJI Hideaki 	}
84170ceb4f5SYOSHIFUJI Hideaki 	return 0;
84270ceb4f5SYOSHIFUJI Hideaki }
84370ceb4f5SYOSHIFUJI Hideaki #endif
84470ceb4f5SYOSHIFUJI Hideaki 
845a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
846a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
847a3c00e46SMartin KaFai Lau {
848a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
849a3c00e46SMartin KaFai Lau 	while (1) {
850a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
851a3c00e46SMartin KaFai Lau 			return NULL;
852a3c00e46SMartin KaFai Lau 		pn = fn->parent;
853a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
854a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
855a3c00e46SMartin KaFai Lau 		else
856a3c00e46SMartin KaFai Lau 			fn = pn;
857a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
858a3c00e46SMartin KaFai Lau 			return fn;
859a3c00e46SMartin KaFai Lau 	}
860a3c00e46SMartin KaFai Lau }
861c71099acSThomas Graf 
8628ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8638ed67789SDaniel Lezcano 					     struct fib6_table *table,
8644c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8651da177e4SLinus Torvalds {
8661da177e4SLinus Torvalds 	struct fib6_node *fn;
8671da177e4SLinus Torvalds 	struct rt6_info *rt;
8681da177e4SLinus Torvalds 
869c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8704c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
871c71099acSThomas Graf restart:
872c71099acSThomas Graf 	rt = fn->leaf;
8734c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
87451ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
87552bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
876a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
877a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
878a3c00e46SMartin KaFai Lau 		if (fn)
879a3c00e46SMartin KaFai Lau 			goto restart;
880a3c00e46SMartin KaFai Lau 	}
881d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
882c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8831da177e4SLinus Torvalds 	return rt;
884c71099acSThomas Graf 
885c71099acSThomas Graf }
886c71099acSThomas Graf 
887ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
888ea6e574eSFlorian Westphal 				    int flags)
889ea6e574eSFlorian Westphal {
890ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
891ea6e574eSFlorian Westphal }
892ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
893ea6e574eSFlorian Westphal 
8949acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8959acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
896c71099acSThomas Graf {
8974c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8984c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8994c9483b2SDavid S. Miller 		.daddr = *daddr,
900c71099acSThomas Graf 	};
901c71099acSThomas Graf 	struct dst_entry *dst;
90277d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
903c71099acSThomas Graf 
904adaa70bbSThomas Graf 	if (saddr) {
9054c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
906adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
907adaa70bbSThomas Graf 	}
908adaa70bbSThomas Graf 
9094c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
910c71099acSThomas Graf 	if (dst->error == 0)
911c71099acSThomas Graf 		return (struct rt6_info *) dst;
912c71099acSThomas Graf 
913c71099acSThomas Graf 	dst_release(dst);
914c71099acSThomas Graf 
9151da177e4SLinus Torvalds 	return NULL;
9161da177e4SLinus Torvalds }
9177159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
9187159039aSYOSHIFUJI Hideaki 
919c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
9201da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
9211da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
9221da177e4SLinus Torvalds    be destroyed.
9231da177e4SLinus Torvalds  */
9241da177e4SLinus Torvalds 
925e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
926e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9271da177e4SLinus Torvalds {
9281da177e4SLinus Torvalds 	int err;
929c71099acSThomas Graf 	struct fib6_table *table;
9301da177e4SLinus Torvalds 
931c71099acSThomas Graf 	table = rt->rt6i_table;
932c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
933e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
934c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	return err;
9371da177e4SLinus Torvalds }
9381da177e4SLinus Torvalds 
93940e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
94040e22e8fSThomas Graf {
941e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
942e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
943e715b6d3SFlorian Westphal 
944e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
94540e22e8fSThomas Graf }
94640e22e8fSThomas Graf 
9478b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
94821efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
949b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9501da177e4SLinus Torvalds {
9511da177e4SLinus Torvalds 	struct rt6_info *rt;
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	/*
9541da177e4SLinus Torvalds 	 *	Clone the route.
9551da177e4SLinus Torvalds 	 */
9561da177e4SLinus Torvalds 
957d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
95883a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9591da177e4SLinus Torvalds 
960ad706862SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0);
96183a09abdSMartin KaFai Lau 
96283a09abdSMartin KaFai Lau 	if (!rt)
96383a09abdSMartin KaFai Lau 		return NULL;
96483a09abdSMartin KaFai Lau 
96583a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9668b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
96783a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
96883a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
96983a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
97083a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9718b9df265SMartin KaFai Lau 
9728b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
973bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
97421efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
97558c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9761da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9771da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9784e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9791da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9801da177e4SLinus Torvalds 		}
9811da177e4SLinus Torvalds #endif
98295a9a5baSYOSHIFUJI Hideaki 	}
98395a9a5baSYOSHIFUJI Hideaki 
984299d9939SYOSHIFUJI Hideaki 	return rt;
985299d9939SYOSHIFUJI Hideaki }
986299d9939SYOSHIFUJI Hideaki 
987d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
988d52d3997SMartin KaFai Lau {
989d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
990d52d3997SMartin KaFai Lau 
991d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
992ad706862SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags);
993d52d3997SMartin KaFai Lau 
994d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
995d52d3997SMartin KaFai Lau 		return NULL;
996d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
997d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
998d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
999d52d3997SMartin KaFai Lau 	return pcpu_rt;
1000d52d3997SMartin KaFai Lau }
1001d52d3997SMartin KaFai Lau 
1002d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
1003d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
1004d52d3997SMartin KaFai Lau {
1005a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, **p;
1006d52d3997SMartin KaFai Lau 
1007d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
1008d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
1009d52d3997SMartin KaFai Lau 
1010a73e4195SMartin KaFai Lau 	if (pcpu_rt) {
1011a73e4195SMartin KaFai Lau 		dst_hold(&pcpu_rt->dst);
1012a73e4195SMartin KaFai Lau 		rt6_dst_from_metrics_check(pcpu_rt);
1013a73e4195SMartin KaFai Lau 	}
1014a73e4195SMartin KaFai Lau 	return pcpu_rt;
1015a73e4195SMartin KaFai Lau }
1016a73e4195SMartin KaFai Lau 
1017a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
1018a73e4195SMartin KaFai Lau {
10199c7370a1SMartin KaFai Lau 	struct fib6_table *table = rt->rt6i_table;
1020a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
1021d52d3997SMartin KaFai Lau 
1022d52d3997SMartin KaFai Lau 	pcpu_rt = ip6_rt_pcpu_alloc(rt);
1023d52d3997SMartin KaFai Lau 	if (!pcpu_rt) {
1024d52d3997SMartin KaFai Lau 		struct net *net = dev_net(rt->dst.dev);
1025d52d3997SMartin KaFai Lau 
10269c7370a1SMartin KaFai Lau 		dst_hold(&net->ipv6.ip6_null_entry->dst);
10279c7370a1SMartin KaFai Lau 		return net->ipv6.ip6_null_entry;
1028d52d3997SMartin KaFai Lau 	}
1029d52d3997SMartin KaFai Lau 
10309c7370a1SMartin KaFai Lau 	read_lock_bh(&table->tb6_lock);
10319c7370a1SMartin KaFai Lau 	if (rt->rt6i_pcpu) {
1032a73e4195SMartin KaFai Lau 		p = this_cpu_ptr(rt->rt6i_pcpu);
1033d52d3997SMartin KaFai Lau 		prev = cmpxchg(p, NULL, pcpu_rt);
1034d52d3997SMartin KaFai Lau 		if (prev) {
1035d52d3997SMartin KaFai Lau 			/* If someone did it before us, return prev instead */
1036d52d3997SMartin KaFai Lau 			dst_destroy(&pcpu_rt->dst);
1037d52d3997SMartin KaFai Lau 			pcpu_rt = prev;
1038d52d3997SMartin KaFai Lau 		}
10399c7370a1SMartin KaFai Lau 	} else {
10409c7370a1SMartin KaFai Lau 		/* rt has been removed from the fib6 tree
10419c7370a1SMartin KaFai Lau 		 * before we have a chance to acquire the read_lock.
10429c7370a1SMartin KaFai Lau 		 * In this case, don't brother to create a pcpu rt
10439c7370a1SMartin KaFai Lau 		 * since rt is going away anyway.  The next
10449c7370a1SMartin KaFai Lau 		 * dst_check() will trigger a re-lookup.
10459c7370a1SMartin KaFai Lau 		 */
10469c7370a1SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
10479c7370a1SMartin KaFai Lau 		pcpu_rt = rt;
10489c7370a1SMartin KaFai Lau 	}
1049d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1050d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
10519c7370a1SMartin KaFai Lau 	read_unlock_bh(&table->tb6_lock);
1052d52d3997SMartin KaFai Lau 	return pcpu_rt;
1053d52d3997SMartin KaFai Lau }
1054d52d3997SMartin KaFai Lau 
10558ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
10564c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
10571da177e4SLinus Torvalds {
1058367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
105945e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1060c71099acSThomas Graf 	int strict = 0;
10611da177e4SLinus Torvalds 
106277d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1063367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1064367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10651da177e4SLinus Torvalds 
1066c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10671da177e4SLinus Torvalds 
10684c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1069367efcb9SMartin KaFai Lau 	saved_fn = fn;
10701da177e4SLinus Torvalds 
1071a3c00e46SMartin KaFai Lau redo_rt6_select:
1072367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
107352bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1074367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1075a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1076a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1077a3c00e46SMartin KaFai Lau 		if (fn)
1078a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1079367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1080367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1081367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1082367efcb9SMartin KaFai Lau 			fn = saved_fn;
1083367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1084367efcb9SMartin KaFai Lau 		}
1085a3c00e46SMartin KaFai Lau 	}
1086a3c00e46SMartin KaFai Lau 
1087d52d3997SMartin KaFai Lau 
1088d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10893da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1090c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10911da177e4SLinus Torvalds 
1092d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1093d52d3997SMartin KaFai Lau 		return rt;
10943da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10953da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10963da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10973da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10983da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10993da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
11003da59bd9SMartin KaFai Lau 		 */
1101c71099acSThomas Graf 
11023da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
11033da59bd9SMartin KaFai Lau 
1104d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1105d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1106d52d3997SMartin KaFai Lau 
11073da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
11083da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
11093da59bd9SMartin KaFai Lau 
11103da59bd9SMartin KaFai Lau 		if (uncached_rt)
11118d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
11123da59bd9SMartin KaFai Lau 		else
11133da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1114d52d3997SMartin KaFai Lau 
11153da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
11163da59bd9SMartin KaFai Lau 		return uncached_rt;
11173da59bd9SMartin KaFai Lau 
1118d52d3997SMartin KaFai Lau 	} else {
1119d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1120d52d3997SMartin KaFai Lau 
1121d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1122d52d3997SMartin KaFai Lau 
1123d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1124d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1125d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1126d52d3997SMartin KaFai Lau 
11279c7370a1SMartin KaFai Lau 		if (pcpu_rt) {
1128d52d3997SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11299c7370a1SMartin KaFai Lau 		} else {
11309c7370a1SMartin KaFai Lau 			/* We have to do the read_unlock first
11319c7370a1SMartin KaFai Lau 			 * because rt6_make_pcpu_route() may trigger
11329c7370a1SMartin KaFai Lau 			 * ip6_dst_gc() which will take the write_lock.
11339c7370a1SMartin KaFai Lau 			 */
11349c7370a1SMartin KaFai Lau 			dst_hold(&rt->dst);
11359c7370a1SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11369c7370a1SMartin KaFai Lau 			pcpu_rt = rt6_make_pcpu_route(rt);
11379c7370a1SMartin KaFai Lau 			dst_release(&rt->dst);
11389c7370a1SMartin KaFai Lau 		}
1139d52d3997SMartin KaFai Lau 
1140d52d3997SMartin KaFai Lau 		return pcpu_rt;
11419c7370a1SMartin KaFai Lau 
1142d52d3997SMartin KaFai Lau 	}
1143c71099acSThomas Graf }
1144c71099acSThomas Graf 
11458ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11464c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11474acad72dSPavel Emelyanov {
11484c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11494acad72dSPavel Emelyanov }
11504acad72dSPavel Emelyanov 
115172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
115272331bc0SShmulik Ladkani 						struct net_device *dev,
115372331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
115472331bc0SShmulik Ladkani {
115572331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
115672331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
115772331bc0SShmulik Ladkani 
115872331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
115972331bc0SShmulik Ladkani }
116072331bc0SShmulik Ladkani 
1161c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1162c71099acSThomas Graf {
1163b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1164c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1165adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1166904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
11674c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
11684c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
11694c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11704c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11716502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11724c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11734c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1174c71099acSThomas Graf 	};
1175adaa70bbSThomas Graf 
1176904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
117746fa062aSJiri Benc 	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
1178904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
117906e9d040SJiri Benc 	skb_dst_drop(skb);
118072331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1181c71099acSThomas Graf }
1182c71099acSThomas Graf 
11838ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11844c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1185c71099acSThomas Graf {
11864c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1187c71099acSThomas Graf }
1188c71099acSThomas Graf 
11899c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
11904c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1191c71099acSThomas Graf {
1192c71099acSThomas Graf 	int flags = 0;
1193c71099acSThomas Graf 
11941fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
11954dc27d1cSDavid McCullough 
11964c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
119777d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1198c71099acSThomas Graf 
11994c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
1200adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
12010c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
12020c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1203adaa70bbSThomas Graf 
12044c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
12051da177e4SLinus Torvalds }
12067159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
12071da177e4SLinus Torvalds 
12082774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
120914e50e57SDavid S. Miller {
12105c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
121114e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
121214e50e57SDavid S. Miller 
1213f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
121414e50e57SDavid S. Miller 	if (rt) {
1215d8d1f30bSChangli Gao 		new = &rt->dst;
121614e50e57SDavid S. Miller 
12178104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
12188104891bSSteffen Klassert 
121914e50e57SDavid S. Miller 		new->__use = 1;
1220352e512cSHerbert Xu 		new->input = dst_discard;
1221aad88724SEric Dumazet 		new->output = dst_discard_sk;
122214e50e57SDavid S. Miller 
122321efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
122421efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
122521efcfa0SEric Dumazet 		else
1226defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
122714e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
122814e50e57SDavid S. Miller 		if (rt->rt6i_idev)
122914e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
123014e50e57SDavid S. Miller 
12314e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
12321716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
123314e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
123414e50e57SDavid S. Miller 
123514e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
123614e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
123714e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
123814e50e57SDavid S. Miller #endif
123914e50e57SDavid S. Miller 
124014e50e57SDavid S. Miller 		dst_free(new);
124114e50e57SDavid S. Miller 	}
124214e50e57SDavid S. Miller 
124369ead7afSDavid S. Miller 	dst_release(dst_orig);
124469ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
124514e50e57SDavid S. Miller }
124614e50e57SDavid S. Miller 
12471da177e4SLinus Torvalds /*
12481da177e4SLinus Torvalds  *	Destination cache support functions
12491da177e4SLinus Torvalds  */
12501da177e4SLinus Torvalds 
12514b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12524b32b5adSMartin KaFai Lau {
12534b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12544b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12554b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12564b32b5adSMartin KaFai Lau }
12574b32b5adSMartin KaFai Lau 
12583da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12593da59bd9SMartin KaFai Lau {
12603da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12613da59bd9SMartin KaFai Lau 		return NULL;
12623da59bd9SMartin KaFai Lau 
12633da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12643da59bd9SMartin KaFai Lau 		return NULL;
12653da59bd9SMartin KaFai Lau 
12663da59bd9SMartin KaFai Lau 	return &rt->dst;
12673da59bd9SMartin KaFai Lau }
12683da59bd9SMartin KaFai Lau 
12693da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12703da59bd9SMartin KaFai Lau {
12713da59bd9SMartin KaFai Lau 	if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12723da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12733da59bd9SMartin KaFai Lau 		return &rt->dst;
12743da59bd9SMartin KaFai Lau 	else
12753da59bd9SMartin KaFai Lau 		return NULL;
12763da59bd9SMartin KaFai Lau }
12773da59bd9SMartin KaFai Lau 
12781da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12791da177e4SLinus Torvalds {
12801da177e4SLinus Torvalds 	struct rt6_info *rt;
12811da177e4SLinus Torvalds 
12821da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12831da177e4SLinus Torvalds 
12846f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12856f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12866f3118b5SNicolas Dichtel 	 * into this function always.
12876f3118b5SNicolas Dichtel 	 */
1288e3bc10bdSHannes Frederic Sowa 
12894b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
12904b32b5adSMartin KaFai Lau 
1291d52d3997SMartin KaFai Lau 	if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE))
12923da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
12933da59bd9SMartin KaFai Lau 	else
12943da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
12951da177e4SLinus Torvalds }
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
12981da177e4SLinus Torvalds {
12991da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
13001da177e4SLinus Torvalds 
13011da177e4SLinus Torvalds 	if (rt) {
130254c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
130354c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1304e0a1ad73SThomas Graf 				ip6_del_rt(rt);
130554c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
13061da177e4SLinus Torvalds 			}
130754c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
130854c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
130954c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
131054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
131154c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
131254c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
13131da177e4SLinus Torvalds }
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
13161da177e4SLinus Torvalds {
13171da177e4SLinus Torvalds 	struct rt6_info *rt;
13181da177e4SLinus Torvalds 
13193ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
13201da177e4SLinus Torvalds 
1321adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
13221da177e4SLinus Torvalds 	if (rt) {
13231eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
13241eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
1325*8e3d5be7SMartin KaFai Lau 			ip6_del_rt(rt);
13261eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
13271da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
13281da177e4SLinus Torvalds 		}
13291da177e4SLinus Torvalds 	}
13301eb4f758SHannes Frederic Sowa }
13311da177e4SLinus Torvalds 
133245e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
133345e4fd26SMartin KaFai Lau {
133445e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
133545e4fd26SMartin KaFai Lau 
133645e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
133745e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
133845e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
133945e4fd26SMartin KaFai Lau }
134045e4fd26SMartin KaFai Lau 
134145e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
134245e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13431da177e4SLinus Torvalds {
13441da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13451da177e4SLinus Torvalds 
134645e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
134745e4fd26SMartin KaFai Lau 		return;
134845e4fd26SMartin KaFai Lau 
134981aded24SDavid S. Miller 	dst_confirm(dst);
135045e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
135145e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
135245e4fd26SMartin KaFai Lau 		return;
135381aded24SDavid S. Miller 
135445e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_CACHE) {
135545e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
135645e4fd26SMartin KaFai Lau 	} else {
135745e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
135845e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13599d289715SHagen Paul Pfeifer 
136045e4fd26SMartin KaFai Lau 		if (iph) {
136145e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
136245e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
136345e4fd26SMartin KaFai Lau 		} else if (sk) {
136445e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
136545e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
136645e4fd26SMartin KaFai Lau 		} else {
136745e4fd26SMartin KaFai Lau 			return;
13681da177e4SLinus Torvalds 		}
136945e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
137045e4fd26SMartin KaFai Lau 		if (nrt6) {
137145e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
137245e4fd26SMartin KaFai Lau 
137345e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
137445e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
137545e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
137645e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
137745e4fd26SMartin KaFai Lau 			 */
137845e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
137945e4fd26SMartin KaFai Lau 		}
138045e4fd26SMartin KaFai Lau 	}
138145e4fd26SMartin KaFai Lau }
138245e4fd26SMartin KaFai Lau 
138345e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
138445e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
138545e4fd26SMartin KaFai Lau {
138645e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
13871da177e4SLinus Torvalds }
13881da177e4SLinus Torvalds 
138942ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
139042ae66c8SDavid S. Miller 		     int oif, u32 mark)
139181aded24SDavid S. Miller {
139281aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
139381aded24SDavid S. Miller 	struct dst_entry *dst;
139481aded24SDavid S. Miller 	struct flowi6 fl6;
139581aded24SDavid S. Miller 
139681aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
139781aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
13981b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
139981aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
140081aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
14016502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
140281aded24SDavid S. Miller 
140381aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
140481aded24SDavid S. Miller 	if (!dst->error)
140545e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
140681aded24SDavid S. Miller 	dst_release(dst);
140781aded24SDavid S. Miller }
140881aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
140981aded24SDavid S. Miller 
141081aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
141181aded24SDavid S. Miller {
141281aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
141381aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
141481aded24SDavid S. Miller }
141581aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
141681aded24SDavid S. Miller 
1417b55b76b2SDuan Jiong /* Handle redirects */
1418b55b76b2SDuan Jiong struct ip6rd_flowi {
1419b55b76b2SDuan Jiong 	struct flowi6 fl6;
1420b55b76b2SDuan Jiong 	struct in6_addr gateway;
1421b55b76b2SDuan Jiong };
1422b55b76b2SDuan Jiong 
1423b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1424b55b76b2SDuan Jiong 					     struct fib6_table *table,
1425b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1426b55b76b2SDuan Jiong 					     int flags)
1427b55b76b2SDuan Jiong {
1428b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1429b55b76b2SDuan Jiong 	struct rt6_info *rt;
1430b55b76b2SDuan Jiong 	struct fib6_node *fn;
1431b55b76b2SDuan Jiong 
1432b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1433b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1434b55b76b2SDuan Jiong 	 *
1435b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1436b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1437b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1438b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1439b55b76b2SDuan Jiong 	 * routes.
1440b55b76b2SDuan Jiong 	 */
1441b55b76b2SDuan Jiong 
1442b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1443b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1444b55b76b2SDuan Jiong restart:
1445b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1446b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1447b55b76b2SDuan Jiong 			continue;
1448b55b76b2SDuan Jiong 		if (rt->dst.error)
1449b55b76b2SDuan Jiong 			break;
1450b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1451b55b76b2SDuan Jiong 			continue;
1452b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1453b55b76b2SDuan Jiong 			continue;
1454b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1455b55b76b2SDuan Jiong 			continue;
1456b55b76b2SDuan Jiong 		break;
1457b55b76b2SDuan Jiong 	}
1458b55b76b2SDuan Jiong 
1459b55b76b2SDuan Jiong 	if (!rt)
1460b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1461b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1462b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1463b0a1ba59SMartin KaFai Lau 		goto out;
1464b0a1ba59SMartin KaFai Lau 	}
1465b0a1ba59SMartin KaFai Lau 
1466b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1467a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1468a3c00e46SMartin KaFai Lau 		if (fn)
1469a3c00e46SMartin KaFai Lau 			goto restart;
1470b55b76b2SDuan Jiong 	}
1471a3c00e46SMartin KaFai Lau 
1472b0a1ba59SMartin KaFai Lau out:
1473b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1474b55b76b2SDuan Jiong 
1475b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1476b55b76b2SDuan Jiong 
1477b55b76b2SDuan Jiong 	return rt;
1478b55b76b2SDuan Jiong };
1479b55b76b2SDuan Jiong 
1480b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1481b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1482b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1483b55b76b2SDuan Jiong {
1484b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1485b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1486b55b76b2SDuan Jiong 
1487b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1488b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1489b55b76b2SDuan Jiong 
1490b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1491b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1492b55b76b2SDuan Jiong }
1493b55b76b2SDuan Jiong 
14943a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
14953a5ad2eeSDavid S. Miller {
14963a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
14973a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
14983a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
14993a5ad2eeSDavid S. Miller 
15003a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1501e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
15023a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
15033a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
15043a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
15053a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
15066502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
15073a5ad2eeSDavid S. Miller 
1508b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
15096700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
15103a5ad2eeSDavid S. Miller 	dst_release(dst);
15113a5ad2eeSDavid S. Miller }
15123a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
15133a5ad2eeSDavid S. Miller 
1514c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1515c92a59ecSDuan Jiong 			    u32 mark)
1516c92a59ecSDuan Jiong {
1517c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1518c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1519c92a59ecSDuan Jiong 	struct dst_entry *dst;
1520c92a59ecSDuan Jiong 	struct flowi6 fl6;
1521c92a59ecSDuan Jiong 
1522c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1523e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1524c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1525c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1526c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1527c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1528c92a59ecSDuan Jiong 
1529b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1530c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1531c92a59ecSDuan Jiong 	dst_release(dst);
1532c92a59ecSDuan Jiong }
1533c92a59ecSDuan Jiong 
15343a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
15353a5ad2eeSDavid S. Miller {
15363a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
15373a5ad2eeSDavid S. Miller }
15383a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15393a5ad2eeSDavid S. Miller 
15400dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15411da177e4SLinus Torvalds {
15420dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15430dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15440dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15450dbaee3bSDavid S. Miller 
15461da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15471da177e4SLinus Torvalds 
15485578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15495578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15501da177e4SLinus Torvalds 
15511da177e4SLinus Torvalds 	/*
15521da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15531da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15541da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15551da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15561da177e4SLinus Torvalds 	 */
15571da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15581da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15591da177e4SLinus Torvalds 	return mtu;
15601da177e4SLinus Torvalds }
15611da177e4SLinus Torvalds 
1562ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1563d33e4553SDavid S. Miller {
15644b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
15654b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1566d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1567618f9bc7SSteffen Klassert 
1568618f9bc7SSteffen Klassert 	if (mtu)
156930f78d8eSEric Dumazet 		goto out;
1570618f9bc7SSteffen Klassert 
15714b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
15724b32b5adSMartin KaFai Lau 	if (mtu)
15734b32b5adSMartin KaFai Lau 		goto out;
15744b32b5adSMartin KaFai Lau 
1575618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1576d33e4553SDavid S. Miller 
1577d33e4553SDavid S. Miller 	rcu_read_lock();
1578d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1579d33e4553SDavid S. Miller 	if (idev)
1580d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1581d33e4553SDavid S. Miller 	rcu_read_unlock();
1582d33e4553SDavid S. Miller 
158330f78d8eSEric Dumazet out:
158430f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1585d33e4553SDavid S. Miller }
1586d33e4553SDavid S. Miller 
15873b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
15883b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
15895d0bbeebSThomas Graf 
15903b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
159187a11578SDavid S. Miller 				  struct flowi6 *fl6)
15921da177e4SLinus Torvalds {
159387a11578SDavid S. Miller 	struct dst_entry *dst;
15941da177e4SLinus Torvalds 	struct rt6_info *rt;
15951da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1596c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
15971da177e4SLinus Torvalds 
159838308473SDavid S. Miller 	if (unlikely(!idev))
1599122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
16001da177e4SLinus Torvalds 
1601ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, dev, 0);
160238308473SDavid S. Miller 	if (unlikely(!rt)) {
16031da177e4SLinus Torvalds 		in6_dev_put(idev);
160487a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
16051da177e4SLinus Torvalds 		goto out;
16061da177e4SLinus Torvalds 	}
16071da177e4SLinus Torvalds 
16088e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
16098e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1610d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1611550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
161287a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
16138e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
16148e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
161514edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
16161da177e4SLinus Torvalds 
16173b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1618d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1619d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
16203b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16211da177e4SLinus Torvalds 
16225578689aSDaniel Lezcano 	fib6_force_start_gc(net);
16231da177e4SLinus Torvalds 
162487a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
162587a11578SDavid S. Miller 
16261da177e4SLinus Torvalds out:
162787a11578SDavid S. Miller 	return dst;
16281da177e4SLinus Torvalds }
16291da177e4SLinus Torvalds 
16303d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
16311da177e4SLinus Torvalds {
1632e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
16333d0f24a7SStephen Hemminger 	int more = 0;
16341da177e4SLinus Torvalds 
16353b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16363b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16375d0bbeebSThomas Graf 
16381da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16391da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16401da177e4SLinus Torvalds 			*pprev = dst->next;
16411da177e4SLinus Torvalds 			dst_free(dst);
16421da177e4SLinus Torvalds 		} else {
16431da177e4SLinus Torvalds 			pprev = &dst->next;
16443d0f24a7SStephen Hemminger 			++more;
16451da177e4SLinus Torvalds 		}
16461da177e4SLinus Torvalds 	}
16471da177e4SLinus Torvalds 
16483b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16495d0bbeebSThomas Graf 
16503d0f24a7SStephen Hemminger 	return more;
16511da177e4SLinus Torvalds }
16521da177e4SLinus Torvalds 
16531e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16541e493d19SDavid S. Miller 			    void *arg)
16551e493d19SDavid S. Miller {
16561e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16571e493d19SDavid S. Miller 
16581e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
16591e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
16601e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
16611e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
16621e493d19SDavid S. Miller 		if (func(rt, arg)) {
16631e493d19SDavid S. Miller 			*pprev = dst->next;
16641e493d19SDavid S. Miller 			dst_free(dst);
16651e493d19SDavid S. Miller 		} else {
16661e493d19SDavid S. Miller 			pprev = &dst->next;
16671e493d19SDavid S. Miller 		}
16681e493d19SDavid S. Miller 	}
16691e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
16701e493d19SDavid S. Miller }
16711e493d19SDavid S. Miller 
1672569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
16731da177e4SLinus Torvalds {
167486393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
16757019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
16767019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
16777019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
16787019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
16797019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1680fc66f95cSEric Dumazet 	int entries;
16811da177e4SLinus Torvalds 
1682fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
168349a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1684fc66f95cSEric Dumazet 	    entries <= rt_max_size)
16851da177e4SLinus Torvalds 		goto out;
16861da177e4SLinus Torvalds 
16876891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
168814956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1689fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1690fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
16917019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
16921da177e4SLinus Torvalds out:
16937019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1694fc66f95cSEric Dumazet 	return entries > rt_max_size;
16951da177e4SLinus Torvalds }
16961da177e4SLinus Torvalds 
1697e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1698e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1699e715b6d3SFlorian Westphal {
1700c3a8d947SDaniel Borkmann 	bool ecn_ca = false;
1701e715b6d3SFlorian Westphal 	struct nlattr *nla;
1702e715b6d3SFlorian Westphal 	int remaining;
1703e715b6d3SFlorian Westphal 	u32 *mp;
1704e715b6d3SFlorian Westphal 
170563159f29SIan Morris 	if (!cfg->fc_mx)
1706e715b6d3SFlorian Westphal 		return 0;
1707e715b6d3SFlorian Westphal 
1708e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1709e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1710e715b6d3SFlorian Westphal 		return -ENOMEM;
1711e715b6d3SFlorian Westphal 
1712e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1713e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1714ea697639SDaniel Borkmann 		u32 val;
1715ea697639SDaniel Borkmann 
17161bb14807SDaniel Borkmann 		if (!type)
17171bb14807SDaniel Borkmann 			continue;
1718e715b6d3SFlorian Westphal 		if (unlikely(type > RTAX_MAX))
1719e715b6d3SFlorian Westphal 			goto err;
17201bb14807SDaniel Borkmann 
1721ea697639SDaniel Borkmann 		if (type == RTAX_CC_ALGO) {
1722ea697639SDaniel Borkmann 			char tmp[TCP_CA_NAME_MAX];
1723e715b6d3SFlorian Westphal 
1724ea697639SDaniel Borkmann 			nla_strlcpy(tmp, nla, sizeof(tmp));
1725c3a8d947SDaniel Borkmann 			val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
1726ea697639SDaniel Borkmann 			if (val == TCP_CA_UNSPEC)
1727ea697639SDaniel Borkmann 				goto err;
1728ea697639SDaniel Borkmann 		} else {
1729ea697639SDaniel Borkmann 			val = nla_get_u32(nla);
1730ea697639SDaniel Borkmann 		}
1731b8d3e416SDaniel Borkmann 		if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
1732b8d3e416SDaniel Borkmann 			goto err;
1733ea697639SDaniel Borkmann 
1734ea697639SDaniel Borkmann 		mp[type - 1] = val;
1735e715b6d3SFlorian Westphal 		__set_bit(type - 1, mxc->mx_valid);
1736e715b6d3SFlorian Westphal 	}
1737e715b6d3SFlorian Westphal 
1738c3a8d947SDaniel Borkmann 	if (ecn_ca) {
1739c3a8d947SDaniel Borkmann 		__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
1740c3a8d947SDaniel Borkmann 		mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
1741c3a8d947SDaniel Borkmann 	}
1742e715b6d3SFlorian Westphal 
1743c3a8d947SDaniel Borkmann 	mxc->mx = mp;
1744e715b6d3SFlorian Westphal 	return 0;
1745e715b6d3SFlorian Westphal  err:
1746e715b6d3SFlorian Westphal 	kfree(mp);
1747e715b6d3SFlorian Westphal 	return -EINVAL;
1748e715b6d3SFlorian Westphal }
17491da177e4SLinus Torvalds 
17506b9ea5a6SRoopa Prabhu int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret)
17511da177e4SLinus Torvalds {
17521da177e4SLinus Torvalds 	int err;
17535578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
17541da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
17551da177e4SLinus Torvalds 	struct net_device *dev = NULL;
17561da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1757c71099acSThomas Graf 	struct fib6_table *table;
17581da177e4SLinus Torvalds 	int addr_type;
17591da177e4SLinus Torvalds 
176086872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
17611da177e4SLinus Torvalds 		return -EINVAL;
17621da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
176386872cb5SThomas Graf 	if (cfg->fc_src_len)
17641da177e4SLinus Torvalds 		return -EINVAL;
17651da177e4SLinus Torvalds #endif
176686872cb5SThomas Graf 	if (cfg->fc_ifindex) {
17671da177e4SLinus Torvalds 		err = -ENODEV;
17685578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
17691da177e4SLinus Torvalds 		if (!dev)
17701da177e4SLinus Torvalds 			goto out;
17711da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
17721da177e4SLinus Torvalds 		if (!idev)
17731da177e4SLinus Torvalds 			goto out;
17741da177e4SLinus Torvalds 	}
17751da177e4SLinus Torvalds 
177686872cb5SThomas Graf 	if (cfg->fc_metric == 0)
177786872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
17781da177e4SLinus Torvalds 
1779c71099acSThomas Graf 	err = -ENOBUFS;
178038308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1781d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1782d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
178338308473SDavid S. Miller 		if (!table) {
1784f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1785d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1786d71314b4SMatti Vaittinen 		}
1787d71314b4SMatti Vaittinen 	} else {
1788d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1789d71314b4SMatti Vaittinen 	}
179038308473SDavid S. Miller 
179138308473SDavid S. Miller 	if (!table)
1792c71099acSThomas Graf 		goto out;
1793c71099acSThomas Graf 
1794ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, NULL,
1795ad706862SMartin KaFai Lau 			   (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT);
17961da177e4SLinus Torvalds 
179738308473SDavid S. Miller 	if (!rt) {
17981da177e4SLinus Torvalds 		err = -ENOMEM;
17991da177e4SLinus Torvalds 		goto out;
18001da177e4SLinus Torvalds 	}
18011da177e4SLinus Torvalds 
18021716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
18031716a961SGao feng 		rt6_set_expires(rt, jiffies +
18041716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
18051716a961SGao feng 	else
18061716a961SGao feng 		rt6_clean_expires(rt);
18071da177e4SLinus Torvalds 
180886872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
180986872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
181086872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
181186872cb5SThomas Graf 
181286872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
18131da177e4SLinus Torvalds 
18141da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1815d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1816ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1817ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
18181da177e4SLinus Torvalds 	else
1819d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
18201da177e4SLinus Torvalds 
1821d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
18221da177e4SLinus Torvalds 
182319e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
182419e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
182519e42e45SRoopa Prabhu 
182619e42e45SRoopa Prabhu 		err = lwtunnel_build_state(dev, cfg->fc_encap_type,
1827127eb7cdSTom Herbert 					   cfg->fc_encap, AF_INET6, cfg,
1828127eb7cdSTom Herbert 					   &lwtstate);
182919e42e45SRoopa Prabhu 		if (err)
183019e42e45SRoopa Prabhu 			goto out;
183161adedf3SJiri Benc 		rt->dst.lwtstate = lwtstate_get(lwtstate);
183261adedf3SJiri Benc 		if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
183361adedf3SJiri Benc 			rt->dst.lwtstate->orig_output = rt->dst.output;
183461adedf3SJiri Benc 			rt->dst.output = lwtunnel_output;
183519e42e45SRoopa Prabhu 		}
183661adedf3SJiri Benc 		if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
183761adedf3SJiri Benc 			rt->dst.lwtstate->orig_input = rt->dst.input;
183861adedf3SJiri Benc 			rt->dst.input = lwtunnel_input;
183925368623STom Herbert 		}
184025368623STom Herbert 	}
184119e42e45SRoopa Prabhu 
184286872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
184386872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1844afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
184511d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
18461da177e4SLinus Torvalds 
18471da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
184886872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
184986872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
18501da177e4SLinus Torvalds #endif
18511da177e4SLinus Torvalds 
185286872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
18531da177e4SLinus Torvalds 
18541da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
18551da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
18561da177e4SLinus Torvalds 	 */
185786872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
185838308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
185938308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
186038308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
18611da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
18625578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
18631da177e4SLinus Torvalds 			if (dev) {
18641da177e4SLinus Torvalds 				dev_put(dev);
18651da177e4SLinus Torvalds 				in6_dev_put(idev);
18661da177e4SLinus Torvalds 			}
18675578689aSDaniel Lezcano 			dev = net->loopback_dev;
18681da177e4SLinus Torvalds 			dev_hold(dev);
18691da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
18701da177e4SLinus Torvalds 			if (!idev) {
18711da177e4SLinus Torvalds 				err = -ENODEV;
18721da177e4SLinus Torvalds 				goto out;
18731da177e4SLinus Torvalds 			}
18741da177e4SLinus Torvalds 		}
18751da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1876ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1877ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1878ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1879aad88724SEric Dumazet 			rt->dst.output = dst_discard_sk;
18807150aedeSKamala R 			rt->dst.input = dst_discard;
1881ef2c7d7bSNicolas Dichtel 			break;
1882ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1883ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
18847150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
18857150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1886ef2c7d7bSNicolas Dichtel 			break;
1887b4949ab2SNicolas Dichtel 		case RTN_THROW:
1888ef2c7d7bSNicolas Dichtel 		default:
18897150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
18907150aedeSKamala R 					: -ENETUNREACH;
18917150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
18927150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1893ef2c7d7bSNicolas Dichtel 			break;
1894ef2c7d7bSNicolas Dichtel 		}
18951da177e4SLinus Torvalds 		goto install_route;
18961da177e4SLinus Torvalds 	}
18971da177e4SLinus Torvalds 
189886872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1899b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
19001da177e4SLinus Torvalds 		int gwa_type;
19011da177e4SLinus Torvalds 
190286872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1903330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
190448ed7b26SFlorian Westphal 
190548ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
190648ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
190748ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
190848ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
190948ed7b26SFlorian Westphal 		 */
191048ed7b26SFlorian Westphal 		err = -EINVAL;
1911330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1912330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1913330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
191448ed7b26SFlorian Westphal 			goto out;
191548ed7b26SFlorian Westphal 
19164e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
19171da177e4SLinus Torvalds 
19181da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
19191da177e4SLinus Torvalds 			struct rt6_info *grt;
19201da177e4SLinus Torvalds 
19211da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
19221da177e4SLinus Torvalds 			   addresses as nexthop address.
19231da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
19241da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
19251da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
19261da177e4SLinus Torvalds 			   some exceptions. --ANK
19271da177e4SLinus Torvalds 			 */
19281da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
19291da177e4SLinus Torvalds 				goto out;
19301da177e4SLinus Torvalds 
19315578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
19321da177e4SLinus Torvalds 
19331da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
193438308473SDavid S. Miller 			if (!grt)
19351da177e4SLinus Torvalds 				goto out;
19361da177e4SLinus Torvalds 			if (dev) {
1937d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
193894e187c0SAmerigo Wang 					ip6_rt_put(grt);
19391da177e4SLinus Torvalds 					goto out;
19401da177e4SLinus Torvalds 				}
19411da177e4SLinus Torvalds 			} else {
1942d1918542SDavid S. Miller 				dev = grt->dst.dev;
19431da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
19441da177e4SLinus Torvalds 				dev_hold(dev);
19451da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
19461da177e4SLinus Torvalds 			}
19471da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
19481da177e4SLinus Torvalds 				err = 0;
194994e187c0SAmerigo Wang 			ip6_rt_put(grt);
19501da177e4SLinus Torvalds 
19511da177e4SLinus Torvalds 			if (err)
19521da177e4SLinus Torvalds 				goto out;
19531da177e4SLinus Torvalds 		}
19541da177e4SLinus Torvalds 		err = -EINVAL;
195538308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
19561da177e4SLinus Torvalds 			goto out;
19571da177e4SLinus Torvalds 	}
19581da177e4SLinus Torvalds 
19591da177e4SLinus Torvalds 	err = -ENODEV;
196038308473SDavid S. Miller 	if (!dev)
19611da177e4SLinus Torvalds 		goto out;
19621da177e4SLinus Torvalds 
1963c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1964c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1965c3968a85SDaniel Walter 			err = -EINVAL;
1966c3968a85SDaniel Walter 			goto out;
1967c3968a85SDaniel Walter 		}
19684e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1969c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1970c3968a85SDaniel Walter 	} else
1971c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1972c3968a85SDaniel Walter 
197386872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
19741da177e4SLinus Torvalds 
19751da177e4SLinus Torvalds install_route:
1976d8d1f30bSChangli Gao 	rt->dst.dev = dev;
19771da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1978c71099acSThomas Graf 	rt->rt6i_table = table;
197963152fc0SDaniel Lezcano 
1980c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
198163152fc0SDaniel Lezcano 
19826b9ea5a6SRoopa Prabhu 	*rt_ret = rt;
19831da177e4SLinus Torvalds 
19846b9ea5a6SRoopa Prabhu 	return 0;
19851da177e4SLinus Torvalds out:
19861da177e4SLinus Torvalds 	if (dev)
19871da177e4SLinus Torvalds 		dev_put(dev);
19881da177e4SLinus Torvalds 	if (idev)
19891da177e4SLinus Torvalds 		in6_dev_put(idev);
19901da177e4SLinus Torvalds 	if (rt)
1991d8d1f30bSChangli Gao 		dst_free(&rt->dst);
19926b9ea5a6SRoopa Prabhu 
19936b9ea5a6SRoopa Prabhu 	*rt_ret = NULL;
19946b9ea5a6SRoopa Prabhu 
19956b9ea5a6SRoopa Prabhu 	return err;
19966b9ea5a6SRoopa Prabhu }
19976b9ea5a6SRoopa Prabhu 
19986b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg)
19996b9ea5a6SRoopa Prabhu {
20006b9ea5a6SRoopa Prabhu 	struct mx6_config mxc = { .mx = NULL, };
20016b9ea5a6SRoopa Prabhu 	struct rt6_info *rt = NULL;
20026b9ea5a6SRoopa Prabhu 	int err;
20036b9ea5a6SRoopa Prabhu 
20046b9ea5a6SRoopa Prabhu 	err = ip6_route_info_create(cfg, &rt);
20056b9ea5a6SRoopa Prabhu 	if (err)
20066b9ea5a6SRoopa Prabhu 		goto out;
20076b9ea5a6SRoopa Prabhu 
20086b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&mxc, cfg);
20096b9ea5a6SRoopa Prabhu 	if (err)
20106b9ea5a6SRoopa Prabhu 		goto out;
20116b9ea5a6SRoopa Prabhu 
20126b9ea5a6SRoopa Prabhu 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
20136b9ea5a6SRoopa Prabhu 
20146b9ea5a6SRoopa Prabhu 	kfree(mxc.mx);
20156b9ea5a6SRoopa Prabhu 
20166b9ea5a6SRoopa Prabhu 	return err;
20176b9ea5a6SRoopa Prabhu out:
20186b9ea5a6SRoopa Prabhu 	if (rt)
20196b9ea5a6SRoopa Prabhu 		dst_free(&rt->dst);
20206b9ea5a6SRoopa Prabhu 
20211da177e4SLinus Torvalds 	return err;
20221da177e4SLinus Torvalds }
20231da177e4SLinus Torvalds 
202486872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
20251da177e4SLinus Torvalds {
20261da177e4SLinus Torvalds 	int err;
2027c71099acSThomas Graf 	struct fib6_table *table;
2028d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
20291da177e4SLinus Torvalds 
2030*8e3d5be7SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry ||
2031*8e3d5be7SMartin KaFai Lau 	    rt->dst.flags & DST_NOCACHE) {
20326825a26cSGao feng 		err = -ENOENT;
20336825a26cSGao feng 		goto out;
20346825a26cSGao feng 	}
20356c813a72SPatrick McHardy 
2036c71099acSThomas Graf 	table = rt->rt6i_table;
2037c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
203886872cb5SThomas Graf 	err = fib6_del(rt, info);
2039c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
20401da177e4SLinus Torvalds 
20416825a26cSGao feng out:
204294e187c0SAmerigo Wang 	ip6_rt_put(rt);
20431da177e4SLinus Torvalds 	return err;
20441da177e4SLinus Torvalds }
20451da177e4SLinus Torvalds 
2046e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
2047e0a1ad73SThomas Graf {
20484d1169c1SDenis V. Lunev 	struct nl_info info = {
2049d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
20504d1169c1SDenis V. Lunev 	};
2051528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
2052e0a1ad73SThomas Graf }
2053e0a1ad73SThomas Graf 
205486872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
20551da177e4SLinus Torvalds {
2056c71099acSThomas Graf 	struct fib6_table *table;
20571da177e4SLinus Torvalds 	struct fib6_node *fn;
20581da177e4SLinus Torvalds 	struct rt6_info *rt;
20591da177e4SLinus Torvalds 	int err = -ESRCH;
20601da177e4SLinus Torvalds 
20615578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
206238308473SDavid S. Miller 	if (!table)
2063c71099acSThomas Graf 		return err;
20641da177e4SLinus Torvalds 
2065c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2066c71099acSThomas Graf 
2067c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
206886872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
206986872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
20701da177e4SLinus Torvalds 
20711da177e4SLinus Torvalds 	if (fn) {
2072d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
20731f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
20741f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
20751f56a01fSMartin KaFai Lau 				continue;
207686872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2077d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2078d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
20791da177e4SLinus Torvalds 				continue;
208086872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
208186872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
20821da177e4SLinus Torvalds 				continue;
208386872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
20841da177e4SLinus Torvalds 				continue;
2085d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2086c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
20871da177e4SLinus Torvalds 
208886872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
20891da177e4SLinus Torvalds 		}
20901da177e4SLinus Torvalds 	}
2091c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
20921da177e4SLinus Torvalds 
20931da177e4SLinus Torvalds 	return err;
20941da177e4SLinus Torvalds }
20951da177e4SLinus Torvalds 
20966700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2097a6279458SYOSHIFUJI Hideaki {
2098e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
2099a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2100e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2101e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2102e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2103e8599ff4SDavid S. Miller 	struct neighbour *neigh;
210471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
21056e157b6aSDavid S. Miller 	int optlen, on_link;
21066e157b6aSDavid S. Miller 	u8 *lladdr;
2107e8599ff4SDavid S. Miller 
210829a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
210971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2110e8599ff4SDavid S. Miller 
2111e8599ff4SDavid S. Miller 	if (optlen < 0) {
21126e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2113e8599ff4SDavid S. Miller 		return;
2114e8599ff4SDavid S. Miller 	}
2115e8599ff4SDavid S. Miller 
211671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2117e8599ff4SDavid S. Miller 
211871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
21196e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2120e8599ff4SDavid S. Miller 		return;
2121e8599ff4SDavid S. Miller 	}
2122e8599ff4SDavid S. Miller 
21236e157b6aSDavid S. Miller 	on_link = 0;
212471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2125e8599ff4SDavid S. Miller 		on_link = 1;
212671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2127e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
21286e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2129e8599ff4SDavid S. Miller 		return;
2130e8599ff4SDavid S. Miller 	}
2131e8599ff4SDavid S. Miller 
2132e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2133e8599ff4SDavid S. Miller 	if (!in6_dev)
2134e8599ff4SDavid S. Miller 		return;
2135e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2136e8599ff4SDavid S. Miller 		return;
2137e8599ff4SDavid S. Miller 
2138e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2139e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2140e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2141e8599ff4SDavid S. Miller 	 */
2142e8599ff4SDavid S. Miller 
214371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
2144e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2145e8599ff4SDavid S. Miller 		return;
2146e8599ff4SDavid S. Miller 	}
21476e157b6aSDavid S. Miller 
21486e157b6aSDavid S. Miller 	lladdr = NULL;
2149e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2150e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2151e8599ff4SDavid S. Miller 					     skb->dev);
2152e8599ff4SDavid S. Miller 		if (!lladdr) {
2153e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2154e8599ff4SDavid S. Miller 			return;
2155e8599ff4SDavid S. Miller 		}
2156e8599ff4SDavid S. Miller 	}
2157e8599ff4SDavid S. Miller 
21586e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
21596e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
21606e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
21616e157b6aSDavid S. Miller 		return;
21626e157b6aSDavid S. Miller 	}
21636e157b6aSDavid S. Miller 
21646e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
21656e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
21666e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
21676e157b6aSDavid S. Miller 	 */
21686e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
21696e157b6aSDavid S. Miller 
217071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2171e8599ff4SDavid S. Miller 	if (!neigh)
2172e8599ff4SDavid S. Miller 		return;
2173e8599ff4SDavid S. Miller 
21741da177e4SLinus Torvalds 	/*
21751da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
21761da177e4SLinus Torvalds 	 */
21771da177e4SLinus Torvalds 
21781da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
21791da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
21801da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
21811da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
21821da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
21831da177e4SLinus Torvalds 		     );
21841da177e4SLinus Torvalds 
218583a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
218638308473SDavid S. Miller 	if (!nrt)
21871da177e4SLinus Torvalds 		goto out;
21881da177e4SLinus Torvalds 
21891da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
21901da177e4SLinus Torvalds 	if (on_link)
21911da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
21921da177e4SLinus Torvalds 
21934e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
21941da177e4SLinus Torvalds 
219540e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
21961da177e4SLinus Torvalds 		goto out;
21971da177e4SLinus Torvalds 
2198d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2199d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
220071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
220160592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
22028d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
22038d71740cSTom Tucker 
22041da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
22056e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2206e0a1ad73SThomas Graf 		ip6_del_rt(rt);
22071da177e4SLinus Torvalds 	}
22081da177e4SLinus Torvalds 
22091da177e4SLinus Torvalds out:
2210e8599ff4SDavid S. Miller 	neigh_release(neigh);
22116e157b6aSDavid S. Miller }
22126e157b6aSDavid S. Miller 
22131da177e4SLinus Torvalds /*
22141da177e4SLinus Torvalds  *	Misc support functions
22151da177e4SLinus Torvalds  */
22161da177e4SLinus Torvalds 
22174b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
22184b32b5adSMartin KaFai Lau {
22194b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
22204b32b5adSMartin KaFai Lau 
22214b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
22224b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
22234b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
22244b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
22254b32b5adSMartin KaFai Lau }
22264b32b5adSMartin KaFai Lau 
222783a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
22281da177e4SLinus Torvalds {
2229d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2230d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
223183a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2232d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
22331da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
22341da177e4SLinus Torvalds 	if (rt->rt6i_idev)
22351da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2236d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
22374e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
22381716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
22391716a961SGao feng 	rt6_set_from(rt, ort);
224083a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
22411da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
224283a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
22431da177e4SLinus Torvalds #endif
224483a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2245c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
224661adedf3SJiri Benc 	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
22471da177e4SLinus Torvalds }
22481da177e4SLinus Torvalds 
224970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2250efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2251b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2252b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
225370ceb4f5SYOSHIFUJI Hideaki {
225470ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
225570ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2256c71099acSThomas Graf 	struct fib6_table *table;
225770ceb4f5SYOSHIFUJI Hideaki 
2258efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
225938308473SDavid S. Miller 	if (!table)
2260c71099acSThomas Graf 		return NULL;
2261c71099acSThomas Graf 
22625744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2263c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
226470ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
226570ceb4f5SYOSHIFUJI Hideaki 		goto out;
226670ceb4f5SYOSHIFUJI Hideaki 
2267d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2268d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
226970ceb4f5SYOSHIFUJI Hideaki 			continue;
227070ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
227170ceb4f5SYOSHIFUJI Hideaki 			continue;
227270ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
227370ceb4f5SYOSHIFUJI Hideaki 			continue;
2274d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
227570ceb4f5SYOSHIFUJI Hideaki 		break;
227670ceb4f5SYOSHIFUJI Hideaki 	}
227770ceb4f5SYOSHIFUJI Hideaki out:
22785744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
227970ceb4f5SYOSHIFUJI Hideaki 	return rt;
228070ceb4f5SYOSHIFUJI Hideaki }
228170ceb4f5SYOSHIFUJI Hideaki 
2282efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2283b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2284b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
228595c96174SEric Dumazet 					   unsigned int pref)
228670ceb4f5SYOSHIFUJI Hideaki {
228786872cb5SThomas Graf 	struct fib6_config cfg = {
228886872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
2289238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
229086872cb5SThomas Graf 		.fc_ifindex	= ifindex,
229186872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
229286872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
229386872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
229415e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2295efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2296efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
229786872cb5SThomas Graf 	};
229870ceb4f5SYOSHIFUJI Hideaki 
22994e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
23004e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
230186872cb5SThomas Graf 
2302e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2303e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
230486872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
230570ceb4f5SYOSHIFUJI Hideaki 
230686872cb5SThomas Graf 	ip6_route_add(&cfg);
230770ceb4f5SYOSHIFUJI Hideaki 
2308efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
230970ceb4f5SYOSHIFUJI Hideaki }
231070ceb4f5SYOSHIFUJI Hideaki #endif
231170ceb4f5SYOSHIFUJI Hideaki 
2312b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
23131da177e4SLinus Torvalds {
23141da177e4SLinus Torvalds 	struct rt6_info *rt;
2315c71099acSThomas Graf 	struct fib6_table *table;
23161da177e4SLinus Torvalds 
2317c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
231838308473SDavid S. Miller 	if (!table)
2319c71099acSThomas Graf 		return NULL;
23201da177e4SLinus Torvalds 
23215744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2322d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2323d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2324045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
23251da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
23261da177e4SLinus Torvalds 			break;
23271da177e4SLinus Torvalds 	}
23281da177e4SLinus Torvalds 	if (rt)
2329d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
23305744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
23311da177e4SLinus Torvalds 	return rt;
23321da177e4SLinus Torvalds }
23331da177e4SLinus Torvalds 
2334b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2335ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2336ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
23371da177e4SLinus Torvalds {
233886872cb5SThomas Graf 	struct fib6_config cfg = {
233986872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
2340238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
234186872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
234286872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
234386872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
234415e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
23455578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2346c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
234786872cb5SThomas Graf 	};
23481da177e4SLinus Torvalds 
23494e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
23501da177e4SLinus Torvalds 
235186872cb5SThomas Graf 	ip6_route_add(&cfg);
23521da177e4SLinus Torvalds 
23531da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
23541da177e4SLinus Torvalds }
23551da177e4SLinus Torvalds 
23567b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
23571da177e4SLinus Torvalds {
23581da177e4SLinus Torvalds 	struct rt6_info *rt;
2359c71099acSThomas Graf 	struct fib6_table *table;
2360c71099acSThomas Graf 
2361c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
23627b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
236338308473SDavid S. Miller 	if (!table)
2364c71099acSThomas Graf 		return;
23651da177e4SLinus Torvalds 
23661da177e4SLinus Torvalds restart:
2367c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2368d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
23693e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
23703e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2371d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2372c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2373e0a1ad73SThomas Graf 			ip6_del_rt(rt);
23741da177e4SLinus Torvalds 			goto restart;
23751da177e4SLinus Torvalds 		}
23761da177e4SLinus Torvalds 	}
2377c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
23781da177e4SLinus Torvalds }
23791da177e4SLinus Torvalds 
23805578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
23815578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
238286872cb5SThomas Graf 				 struct fib6_config *cfg)
238386872cb5SThomas Graf {
238486872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
238586872cb5SThomas Graf 
238686872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
238786872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
238886872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
238986872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
239086872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
239186872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
239286872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
239386872cb5SThomas Graf 
23945578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2395f1243c2dSBenjamin Thery 
23964e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
23974e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
23984e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
239986872cb5SThomas Graf }
240086872cb5SThomas Graf 
24015578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
24021da177e4SLinus Torvalds {
240386872cb5SThomas Graf 	struct fib6_config cfg;
24041da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
24051da177e4SLinus Torvalds 	int err;
24061da177e4SLinus Torvalds 
24071da177e4SLinus Torvalds 	switch (cmd) {
24081da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
24091da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2410af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
24111da177e4SLinus Torvalds 			return -EPERM;
24121da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
24131da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
24141da177e4SLinus Torvalds 		if (err)
24151da177e4SLinus Torvalds 			return -EFAULT;
24161da177e4SLinus Torvalds 
24175578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
241886872cb5SThomas Graf 
24191da177e4SLinus Torvalds 		rtnl_lock();
24201da177e4SLinus Torvalds 		switch (cmd) {
24211da177e4SLinus Torvalds 		case SIOCADDRT:
242286872cb5SThomas Graf 			err = ip6_route_add(&cfg);
24231da177e4SLinus Torvalds 			break;
24241da177e4SLinus Torvalds 		case SIOCDELRT:
242586872cb5SThomas Graf 			err = ip6_route_del(&cfg);
24261da177e4SLinus Torvalds 			break;
24271da177e4SLinus Torvalds 		default:
24281da177e4SLinus Torvalds 			err = -EINVAL;
24291da177e4SLinus Torvalds 		}
24301da177e4SLinus Torvalds 		rtnl_unlock();
24311da177e4SLinus Torvalds 
24321da177e4SLinus Torvalds 		return err;
24333ff50b79SStephen Hemminger 	}
24341da177e4SLinus Torvalds 
24351da177e4SLinus Torvalds 	return -EINVAL;
24361da177e4SLinus Torvalds }
24371da177e4SLinus Torvalds 
24381da177e4SLinus Torvalds /*
24391da177e4SLinus Torvalds  *	Drop the packet on the floor
24401da177e4SLinus Torvalds  */
24411da177e4SLinus Torvalds 
2442d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
24431da177e4SLinus Torvalds {
2444612f09e8SYOSHIFUJI Hideaki 	int type;
2445adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2446612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2447612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
24480660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
244945bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
24503bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24513bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2452612f09e8SYOSHIFUJI Hideaki 			break;
2453612f09e8SYOSHIFUJI Hideaki 		}
2454612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2455612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
24563bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24573bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2458612f09e8SYOSHIFUJI Hideaki 		break;
2459612f09e8SYOSHIFUJI Hideaki 	}
24603ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
24611da177e4SLinus Torvalds 	kfree_skb(skb);
24621da177e4SLinus Torvalds 	return 0;
24631da177e4SLinus Torvalds }
24641da177e4SLinus Torvalds 
24659ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
24669ce8ade0SThomas Graf {
2467612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
24689ce8ade0SThomas Graf }
24699ce8ade0SThomas Graf 
2470aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb)
24711da177e4SLinus Torvalds {
2472adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2473612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
24741da177e4SLinus Torvalds }
24751da177e4SLinus Torvalds 
24769ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
24779ce8ade0SThomas Graf {
2478612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
24799ce8ade0SThomas Graf }
24809ce8ade0SThomas Graf 
2481aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb)
24829ce8ade0SThomas Graf {
2483adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2484612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
24859ce8ade0SThomas Graf }
24869ce8ade0SThomas Graf 
24871da177e4SLinus Torvalds /*
24881da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
24891da177e4SLinus Torvalds  */
24901da177e4SLinus Torvalds 
24911da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
24921da177e4SLinus Torvalds 				    const struct in6_addr *addr,
24938f031519SDavid S. Miller 				    bool anycast)
24941da177e4SLinus Torvalds {
2495c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2496a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2497ad706862SMartin KaFai Lau 					    DST_NOCOUNT);
2498a3300ef4SHannes Frederic Sowa 	if (!rt)
24991da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
25001da177e4SLinus Torvalds 
25011da177e4SLinus Torvalds 	in6_dev_hold(idev);
25021da177e4SLinus Torvalds 
250311d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2504d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2505d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
25061da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
25071da177e4SLinus Torvalds 
25081da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
250958c4fb86SYOSHIFUJI Hideaki 	if (anycast)
251058c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
251158c4fb86SYOSHIFUJI Hideaki 	else
25121da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
25131da177e4SLinus Torvalds 
2514550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
25154e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
25161da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
25175578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
2518*8e3d5be7SMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
25191da177e4SLinus Torvalds 
2520d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
25211da177e4SLinus Torvalds 
25221da177e4SLinus Torvalds 	return rt;
25231da177e4SLinus Torvalds }
25241da177e4SLinus Torvalds 
2525c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2526c3968a85SDaniel Walter 			struct rt6_info *rt,
2527b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2528c3968a85SDaniel Walter 			unsigned int prefs,
2529c3968a85SDaniel Walter 			struct in6_addr *saddr)
2530c3968a85SDaniel Walter {
2531e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2532e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2533c3968a85SDaniel Walter 	int err = 0;
2534e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
25354e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2536c3968a85SDaniel Walter 	else
2537c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2538c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2539c3968a85SDaniel Walter 	return err;
2540c3968a85SDaniel Walter }
2541c3968a85SDaniel Walter 
2542c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2543c3968a85SDaniel Walter struct arg_dev_net_ip {
2544c3968a85SDaniel Walter 	struct net_device *dev;
2545c3968a85SDaniel Walter 	struct net *net;
2546c3968a85SDaniel Walter 	struct in6_addr *addr;
2547c3968a85SDaniel Walter };
2548c3968a85SDaniel Walter 
2549c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2550c3968a85SDaniel Walter {
2551c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2552c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2553c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2554c3968a85SDaniel Walter 
2555d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2556c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2557c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2558c3968a85SDaniel Walter 		/* remove prefsrc entry */
2559c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2560c3968a85SDaniel Walter 	}
2561c3968a85SDaniel Walter 	return 0;
2562c3968a85SDaniel Walter }
2563c3968a85SDaniel Walter 
2564c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2565c3968a85SDaniel Walter {
2566c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2567c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2568c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2569c3968a85SDaniel Walter 		.net = net,
2570c3968a85SDaniel Walter 		.addr = &ifp->addr,
2571c3968a85SDaniel Walter 	};
25720c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2573c3968a85SDaniel Walter }
2574c3968a85SDaniel Walter 
2575be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2576be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2577be7a010dSDuan Jiong 
2578be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2579be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2580be7a010dSDuan Jiong {
2581be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2582be7a010dSDuan Jiong 
2583be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2584be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2585be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2586be7a010dSDuan Jiong 		return -1;
2587be7a010dSDuan Jiong 	}
2588be7a010dSDuan Jiong 	return 0;
2589be7a010dSDuan Jiong }
2590be7a010dSDuan Jiong 
2591be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2592be7a010dSDuan Jiong {
2593be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2594be7a010dSDuan Jiong }
2595be7a010dSDuan Jiong 
25968ed67789SDaniel Lezcano struct arg_dev_net {
25978ed67789SDaniel Lezcano 	struct net_device *dev;
25988ed67789SDaniel Lezcano 	struct net *net;
25998ed67789SDaniel Lezcano };
26008ed67789SDaniel Lezcano 
26011da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
26021da177e4SLinus Torvalds {
2603bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2604bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
26058ed67789SDaniel Lezcano 
2606d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2607c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
26081da177e4SLinus Torvalds 		return -1;
2609c159d30cSDavid S. Miller 
26101da177e4SLinus Torvalds 	return 0;
26111da177e4SLinus Torvalds }
26121da177e4SLinus Torvalds 
2613f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
26141da177e4SLinus Torvalds {
26158ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
26168ed67789SDaniel Lezcano 		.dev = dev,
26178ed67789SDaniel Lezcano 		.net = net,
26188ed67789SDaniel Lezcano 	};
26198ed67789SDaniel Lezcano 
26200c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
26211e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
26228d0b94afSMartin KaFai Lau 	rt6_uncached_list_flush_dev(net, dev);
26231da177e4SLinus Torvalds }
26241da177e4SLinus Torvalds 
262595c96174SEric Dumazet struct rt6_mtu_change_arg {
26261da177e4SLinus Torvalds 	struct net_device *dev;
262795c96174SEric Dumazet 	unsigned int mtu;
26281da177e4SLinus Torvalds };
26291da177e4SLinus Torvalds 
26301da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
26311da177e4SLinus Torvalds {
26321da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
26331da177e4SLinus Torvalds 	struct inet6_dev *idev;
26341da177e4SLinus Torvalds 
26351da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
26361da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
26371da177e4SLinus Torvalds 	   We still use this lock to block changes
26381da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
26391da177e4SLinus Torvalds 	*/
26401da177e4SLinus Torvalds 
26411da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
264238308473SDavid S. Miller 	if (!idev)
26431da177e4SLinus Torvalds 		return 0;
26441da177e4SLinus Torvalds 
26451da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
26461da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
26471da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
26481da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
26491da177e4SLinus Torvalds 	 */
26501da177e4SLinus Torvalds 	/*
26511da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
26521da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
26531da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
26541da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
26551da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
26561da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
26571da177e4SLinus Torvalds 	   PMTU discouvery.
26581da177e4SLinus Torvalds 	 */
2659d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
26604b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
26614b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
26624b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
26634b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
26644b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
26654b32b5adSMartin KaFai Lau 			 * been updated.
26664b32b5adSMartin KaFai Lau 			 */
26674b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
26684b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
26694b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2670d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
26714b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2672defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2673566cfd8fSSimon Arlott 		}
26744b32b5adSMartin KaFai Lau 	}
26751da177e4SLinus Torvalds 	return 0;
26761da177e4SLinus Torvalds }
26771da177e4SLinus Torvalds 
267895c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
26791da177e4SLinus Torvalds {
2680c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2681c71099acSThomas Graf 		.dev = dev,
2682c71099acSThomas Graf 		.mtu = mtu,
2683c71099acSThomas Graf 	};
26841da177e4SLinus Torvalds 
26850c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
26861da177e4SLinus Torvalds }
26871da177e4SLinus Torvalds 
2688ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
26895176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
269086872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2691ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
269286872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
269386872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
269451ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2695c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
269619e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
269719e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
269886872cb5SThomas Graf };
269986872cb5SThomas Graf 
270086872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
270186872cb5SThomas Graf 			      struct fib6_config *cfg)
27021da177e4SLinus Torvalds {
270386872cb5SThomas Graf 	struct rtmsg *rtm;
270486872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2705c78ba6d6SLubomir Rintel 	unsigned int pref;
270686872cb5SThomas Graf 	int err;
27071da177e4SLinus Torvalds 
270886872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
270986872cb5SThomas Graf 	if (err < 0)
271086872cb5SThomas Graf 		goto errout;
27111da177e4SLinus Torvalds 
271286872cb5SThomas Graf 	err = -EINVAL;
271386872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
271486872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
271586872cb5SThomas Graf 
271686872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
271786872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
271886872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
271986872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
272086872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2721ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
272286872cb5SThomas Graf 
2723ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2724ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2725b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2726b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
272786872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
272886872cb5SThomas Graf 
2729ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2730ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2731ab79ad14SMaciej Żenczykowski 
27321f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
27331f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
27341f56a01fSMartin KaFai Lau 
273515e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
273686872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
27373b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
273886872cb5SThomas Graf 
273986872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
274067b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
274186872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
27421da177e4SLinus Torvalds 	}
274386872cb5SThomas Graf 
274486872cb5SThomas Graf 	if (tb[RTA_DST]) {
274586872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
274686872cb5SThomas Graf 
274786872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
274886872cb5SThomas Graf 			goto errout;
274986872cb5SThomas Graf 
275086872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
27511da177e4SLinus Torvalds 	}
275286872cb5SThomas Graf 
275386872cb5SThomas Graf 	if (tb[RTA_SRC]) {
275486872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
275586872cb5SThomas Graf 
275686872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
275786872cb5SThomas Graf 			goto errout;
275886872cb5SThomas Graf 
275986872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
27601da177e4SLinus Torvalds 	}
276186872cb5SThomas Graf 
2762c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
276367b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2764c3968a85SDaniel Walter 
276586872cb5SThomas Graf 	if (tb[RTA_OIF])
276686872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
276786872cb5SThomas Graf 
276886872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
276986872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
277086872cb5SThomas Graf 
277186872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
277286872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
277386872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
27741da177e4SLinus Torvalds 	}
277586872cb5SThomas Graf 
277686872cb5SThomas Graf 	if (tb[RTA_TABLE])
277786872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
277886872cb5SThomas Graf 
277951ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
278051ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
278151ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
278251ebd318SNicolas Dichtel 	}
278351ebd318SNicolas Dichtel 
2784c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2785c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2786c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2787c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2788c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2789c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2790c78ba6d6SLubomir Rintel 	}
2791c78ba6d6SLubomir Rintel 
279219e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
279319e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
279419e42e45SRoopa Prabhu 
279519e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP_TYPE])
279619e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
279719e42e45SRoopa Prabhu 
279886872cb5SThomas Graf 	err = 0;
279986872cb5SThomas Graf errout:
280086872cb5SThomas Graf 	return err;
28011da177e4SLinus Torvalds }
28021da177e4SLinus Torvalds 
28036b9ea5a6SRoopa Prabhu struct rt6_nh {
28046b9ea5a6SRoopa Prabhu 	struct rt6_info *rt6_info;
28056b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
28066b9ea5a6SRoopa Prabhu 	struct mx6_config mxc;
28076b9ea5a6SRoopa Prabhu 	struct list_head next;
28086b9ea5a6SRoopa Prabhu };
28096b9ea5a6SRoopa Prabhu 
28106b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
28116b9ea5a6SRoopa Prabhu {
28126b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28136b9ea5a6SRoopa Prabhu 
28146b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28156b9ea5a6SRoopa Prabhu 		pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n",
28166b9ea5a6SRoopa Prabhu 		        &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway,
28176b9ea5a6SRoopa Prabhu 		        nh->r_cfg.fc_ifindex);
28186b9ea5a6SRoopa Prabhu 	}
28196b9ea5a6SRoopa Prabhu }
28206b9ea5a6SRoopa Prabhu 
28216b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list,
28226b9ea5a6SRoopa Prabhu 				 struct rt6_info *rt, struct fib6_config *r_cfg)
28236b9ea5a6SRoopa Prabhu {
28246b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28256b9ea5a6SRoopa Prabhu 	struct rt6_info *rtnh;
28266b9ea5a6SRoopa Prabhu 	int err = -EEXIST;
28276b9ea5a6SRoopa Prabhu 
28286b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28296b9ea5a6SRoopa Prabhu 		/* check if rt6_info already exists */
28306b9ea5a6SRoopa Prabhu 		rtnh = nh->rt6_info;
28316b9ea5a6SRoopa Prabhu 
28326b9ea5a6SRoopa Prabhu 		if (rtnh->dst.dev == rt->dst.dev &&
28336b9ea5a6SRoopa Prabhu 		    rtnh->rt6i_idev == rt->rt6i_idev &&
28346b9ea5a6SRoopa Prabhu 		    ipv6_addr_equal(&rtnh->rt6i_gateway,
28356b9ea5a6SRoopa Prabhu 				    &rt->rt6i_gateway))
28366b9ea5a6SRoopa Prabhu 			return err;
28376b9ea5a6SRoopa Prabhu 	}
28386b9ea5a6SRoopa Prabhu 
28396b9ea5a6SRoopa Prabhu 	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
28406b9ea5a6SRoopa Prabhu 	if (!nh)
28416b9ea5a6SRoopa Prabhu 		return -ENOMEM;
28426b9ea5a6SRoopa Prabhu 	nh->rt6_info = rt;
28436b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&nh->mxc, r_cfg);
28446b9ea5a6SRoopa Prabhu 	if (err) {
28456b9ea5a6SRoopa Prabhu 		kfree(nh);
28466b9ea5a6SRoopa Prabhu 		return err;
28476b9ea5a6SRoopa Prabhu 	}
28486b9ea5a6SRoopa Prabhu 	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
28496b9ea5a6SRoopa Prabhu 	list_add_tail(&nh->next, rt6_nh_list);
28506b9ea5a6SRoopa Prabhu 
28516b9ea5a6SRoopa Prabhu 	return 0;
28526b9ea5a6SRoopa Prabhu }
28536b9ea5a6SRoopa Prabhu 
28546b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg)
285551ebd318SNicolas Dichtel {
285651ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
285751ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
28586b9ea5a6SRoopa Prabhu 	struct rt6_info *rt;
28596b9ea5a6SRoopa Prabhu 	struct rt6_nh *err_nh;
28606b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh, *nh_safe;
286151ebd318SNicolas Dichtel 	int remaining;
286251ebd318SNicolas Dichtel 	int attrlen;
28636b9ea5a6SRoopa Prabhu 	int err = 1;
28646b9ea5a6SRoopa Prabhu 	int nhn = 0;
28656b9ea5a6SRoopa Prabhu 	int replace = (cfg->fc_nlinfo.nlh &&
28666b9ea5a6SRoopa Prabhu 		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
28676b9ea5a6SRoopa Prabhu 	LIST_HEAD(rt6_nh_list);
286851ebd318SNicolas Dichtel 
286935f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
287051ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
287151ebd318SNicolas Dichtel 
28726b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
28736b9ea5a6SRoopa Prabhu 	 * rt6_info structs per nexthop
28746b9ea5a6SRoopa Prabhu 	 */
287551ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
287651ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
287751ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
287851ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
287951ebd318SNicolas Dichtel 
288051ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
288151ebd318SNicolas Dichtel 		if (attrlen > 0) {
288251ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
288351ebd318SNicolas Dichtel 
288451ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
288551ebd318SNicolas Dichtel 			if (nla) {
288667b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
288751ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
288851ebd318SNicolas Dichtel 			}
288919e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
289019e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
289119e42e45SRoopa Prabhu 			if (nla)
289219e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
289351ebd318SNicolas Dichtel 		}
28946b9ea5a6SRoopa Prabhu 
28956b9ea5a6SRoopa Prabhu 		err = ip6_route_info_create(&r_cfg, &rt);
28966b9ea5a6SRoopa Prabhu 		if (err)
28976b9ea5a6SRoopa Prabhu 			goto cleanup;
28986b9ea5a6SRoopa Prabhu 
28996b9ea5a6SRoopa Prabhu 		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
290051ebd318SNicolas Dichtel 		if (err) {
29016b9ea5a6SRoopa Prabhu 			dst_free(&rt->dst);
29026b9ea5a6SRoopa Prabhu 			goto cleanup;
290351ebd318SNicolas Dichtel 		}
29046b9ea5a6SRoopa Prabhu 
29056b9ea5a6SRoopa Prabhu 		rtnh = rtnh_next(rtnh, &remaining);
290651ebd318SNicolas Dichtel 	}
29076b9ea5a6SRoopa Prabhu 
29086b9ea5a6SRoopa Prabhu 	err_nh = NULL;
29096b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29106b9ea5a6SRoopa Prabhu 		err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc);
29116b9ea5a6SRoopa Prabhu 		/* nh->rt6_info is used or freed at this point, reset to NULL*/
29126b9ea5a6SRoopa Prabhu 		nh->rt6_info = NULL;
29136b9ea5a6SRoopa Prabhu 		if (err) {
29146b9ea5a6SRoopa Prabhu 			if (replace && nhn)
29156b9ea5a6SRoopa Prabhu 				ip6_print_replace_route_err(&rt6_nh_list);
29166b9ea5a6SRoopa Prabhu 			err_nh = nh;
29176b9ea5a6SRoopa Prabhu 			goto add_errout;
29186b9ea5a6SRoopa Prabhu 		}
29196b9ea5a6SRoopa Prabhu 
29201a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
292127596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
292227596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
292327596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
292427596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
292527596472SMichal Kubeček 		 * be added to it.
29261a72418bSNicolas Dichtel 		 */
292727596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
292827596472SMichal Kubeček 						     NLM_F_REPLACE);
29296b9ea5a6SRoopa Prabhu 		nhn++;
29306b9ea5a6SRoopa Prabhu 	}
29316b9ea5a6SRoopa Prabhu 
29326b9ea5a6SRoopa Prabhu 	goto cleanup;
29336b9ea5a6SRoopa Prabhu 
29346b9ea5a6SRoopa Prabhu add_errout:
29356b9ea5a6SRoopa Prabhu 	/* Delete routes that were already added */
29366b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29376b9ea5a6SRoopa Prabhu 		if (err_nh == nh)
29386b9ea5a6SRoopa Prabhu 			break;
29396b9ea5a6SRoopa Prabhu 		ip6_route_del(&nh->r_cfg);
29406b9ea5a6SRoopa Prabhu 	}
29416b9ea5a6SRoopa Prabhu 
29426b9ea5a6SRoopa Prabhu cleanup:
29436b9ea5a6SRoopa Prabhu 	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
29446b9ea5a6SRoopa Prabhu 		if (nh->rt6_info)
29456b9ea5a6SRoopa Prabhu 			dst_free(&nh->rt6_info->dst);
29466b9ea5a6SRoopa Prabhu 		kfree(nh->mxc.mx);
29476b9ea5a6SRoopa Prabhu 		list_del(&nh->next);
29486b9ea5a6SRoopa Prabhu 		kfree(nh);
29496b9ea5a6SRoopa Prabhu 	}
29506b9ea5a6SRoopa Prabhu 
29516b9ea5a6SRoopa Prabhu 	return err;
29526b9ea5a6SRoopa Prabhu }
29536b9ea5a6SRoopa Prabhu 
29546b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg)
29556b9ea5a6SRoopa Prabhu {
29566b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
29576b9ea5a6SRoopa Prabhu 	struct rtnexthop *rtnh;
29586b9ea5a6SRoopa Prabhu 	int remaining;
29596b9ea5a6SRoopa Prabhu 	int attrlen;
29606b9ea5a6SRoopa Prabhu 	int err = 1, last_err = 0;
29616b9ea5a6SRoopa Prabhu 
29626b9ea5a6SRoopa Prabhu 	remaining = cfg->fc_mp_len;
29636b9ea5a6SRoopa Prabhu 	rtnh = (struct rtnexthop *)cfg->fc_mp;
29646b9ea5a6SRoopa Prabhu 
29656b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry */
29666b9ea5a6SRoopa Prabhu 	while (rtnh_ok(rtnh, remaining)) {
29676b9ea5a6SRoopa Prabhu 		memcpy(&r_cfg, cfg, sizeof(*cfg));
29686b9ea5a6SRoopa Prabhu 		if (rtnh->rtnh_ifindex)
29696b9ea5a6SRoopa Prabhu 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
29706b9ea5a6SRoopa Prabhu 
29716b9ea5a6SRoopa Prabhu 		attrlen = rtnh_attrlen(rtnh);
29726b9ea5a6SRoopa Prabhu 		if (attrlen > 0) {
29736b9ea5a6SRoopa Prabhu 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
29746b9ea5a6SRoopa Prabhu 
29756b9ea5a6SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
29766b9ea5a6SRoopa Prabhu 			if (nla) {
29776b9ea5a6SRoopa Prabhu 				nla_memcpy(&r_cfg.fc_gateway, nla, 16);
29786b9ea5a6SRoopa Prabhu 				r_cfg.fc_flags |= RTF_GATEWAY;
29796b9ea5a6SRoopa Prabhu 			}
29806b9ea5a6SRoopa Prabhu 		}
29816b9ea5a6SRoopa Prabhu 		err = ip6_route_del(&r_cfg);
29826b9ea5a6SRoopa Prabhu 		if (err)
29836b9ea5a6SRoopa Prabhu 			last_err = err;
29846b9ea5a6SRoopa Prabhu 
298551ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
298651ebd318SNicolas Dichtel 	}
298751ebd318SNicolas Dichtel 
298851ebd318SNicolas Dichtel 	return last_err;
298951ebd318SNicolas Dichtel }
299051ebd318SNicolas Dichtel 
2991661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
29921da177e4SLinus Torvalds {
299386872cb5SThomas Graf 	struct fib6_config cfg;
299486872cb5SThomas Graf 	int err;
29951da177e4SLinus Torvalds 
299686872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
299786872cb5SThomas Graf 	if (err < 0)
299886872cb5SThomas Graf 		return err;
299986872cb5SThomas Graf 
300051ebd318SNicolas Dichtel 	if (cfg.fc_mp)
30016b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_del(&cfg);
300251ebd318SNicolas Dichtel 	else
300386872cb5SThomas Graf 		return ip6_route_del(&cfg);
30041da177e4SLinus Torvalds }
30051da177e4SLinus Torvalds 
3006661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
30071da177e4SLinus Torvalds {
300886872cb5SThomas Graf 	struct fib6_config cfg;
300986872cb5SThomas Graf 	int err;
30101da177e4SLinus Torvalds 
301186872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
301286872cb5SThomas Graf 	if (err < 0)
301386872cb5SThomas Graf 		return err;
301486872cb5SThomas Graf 
301551ebd318SNicolas Dichtel 	if (cfg.fc_mp)
30166b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_add(&cfg);
301751ebd318SNicolas Dichtel 	else
301886872cb5SThomas Graf 		return ip6_route_add(&cfg);
30191da177e4SLinus Torvalds }
30201da177e4SLinus Torvalds 
302119e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
3022339bf98fSThomas Graf {
3023339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
3024339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
3025339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
3026339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
3027339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
3028339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
3029339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
3030339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
3031339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
30326a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
3033ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
3034c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
303519e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
303661adedf3SJiri Benc 	       + lwtunnel_get_encap_size(rt->dst.lwtstate);
3037339bf98fSThomas Graf }
3038339bf98fSThomas Graf 
3039191cd582SBrian Haley static int rt6_fill_node(struct net *net,
3040191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
30410d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
304215e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
30437bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
30441da177e4SLinus Torvalds {
30454b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
30461da177e4SLinus Torvalds 	struct rtmsg *rtm;
30471da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
3048e3703b3dSThomas Graf 	long expires;
30499e762a4aSPatrick McHardy 	u32 table;
30501da177e4SLinus Torvalds 
30511da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
30521da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
30531da177e4SLinus Torvalds 			/* success since this is not a prefix route */
30541da177e4SLinus Torvalds 			return 1;
30551da177e4SLinus Torvalds 		}
30561da177e4SLinus Torvalds 	}
30571da177e4SLinus Torvalds 
305815e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
305938308473SDavid S. Miller 	if (!nlh)
306026932566SPatrick McHardy 		return -EMSGSIZE;
30612d7202bfSThomas Graf 
30622d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
30631da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
30641da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
30651da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
30661da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
3067c71099acSThomas Graf 	if (rt->rt6i_table)
30689e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
3069c71099acSThomas Graf 	else
30709e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
30719e762a4aSPatrick McHardy 	rtm->rtm_table = table;
3072c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
3073c78679e8SDavid S. Miller 		goto nla_put_failure;
3074ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
3075ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
3076ef2c7d7bSNicolas Dichtel 		case -EINVAL:
3077ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
3078ef2c7d7bSNicolas Dichtel 			break;
3079ef2c7d7bSNicolas Dichtel 		case -EACCES:
3080ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
3081ef2c7d7bSNicolas Dichtel 			break;
3082b4949ab2SNicolas Dichtel 		case -EAGAIN:
3083b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
3084b4949ab2SNicolas Dichtel 			break;
3085ef2c7d7bSNicolas Dichtel 		default:
30861da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
3087ef2c7d7bSNicolas Dichtel 			break;
3088ef2c7d7bSNicolas Dichtel 		}
3089ef2c7d7bSNicolas Dichtel 	}
3090ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
3091ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
3092d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
30931da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
30941da177e4SLinus Torvalds 	else
30951da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
30961da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
309735103d11SAndy Gospodarek 	if (!netif_carrier_ok(rt->dst.dev)) {
3098cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
309935103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
310035103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
310135103d11SAndy Gospodarek 	}
31021da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
31031da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
31041da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
31051da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
3106f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
3107f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
31081da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
3109f0396f60SDenis Ovsienko 		else
3110f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
3111f0396f60SDenis Ovsienko 	}
31121da177e4SLinus Torvalds 
31131da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
31141da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
31151da177e4SLinus Torvalds 
31161da177e4SLinus Torvalds 	if (dst) {
3117930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
3118c78679e8SDavid S. Miller 			goto nla_put_failure;
31191da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
31201da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
3121930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
3122c78679e8SDavid S. Miller 			goto nla_put_failure;
31231da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
31241da177e4SLinus Torvalds 	if (src) {
3125930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
3126c78679e8SDavid S. Miller 			goto nla_put_failure;
31271da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
3128c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
3129930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
3130c78679e8SDavid S. Miller 		goto nla_put_failure;
31311da177e4SLinus Torvalds #endif
31327bc570c8SYOSHIFUJI Hideaki 	if (iif) {
31337bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
31347bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
31358229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
31367bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
31377bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
31387bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
31397bc570c8SYOSHIFUJI Hideaki 						return 0;
31407bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
31417bc570c8SYOSHIFUJI Hideaki 				} else {
31427bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
31437bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
31447bc570c8SYOSHIFUJI Hideaki 				}
31457bc570c8SYOSHIFUJI Hideaki 			}
31467bc570c8SYOSHIFUJI Hideaki 		} else
31477bc570c8SYOSHIFUJI Hideaki #endif
3148c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
3149c78679e8SDavid S. Miller 				goto nla_put_failure;
31507bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
31511da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
3152c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
3153930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3154c78679e8SDavid S. Miller 			goto nla_put_failure;
3155c3968a85SDaniel Walter 	}
3156c3968a85SDaniel Walter 
3157c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
3158c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
31594e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
3160930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3161c78679e8SDavid S. Miller 			goto nla_put_failure;
31621da177e4SLinus Torvalds 	}
31632d7202bfSThomas Graf 
31644b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
31654b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
31664b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
31674b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
31682d7202bfSThomas Graf 		goto nla_put_failure;
31692d7202bfSThomas Graf 
3170dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
3171930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
317294f826b8SEric Dumazet 			goto nla_put_failure;
317394f826b8SEric Dumazet 	}
31742d7202bfSThomas Graf 
3175c78679e8SDavid S. Miller 	if (rt->dst.dev &&
3176c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3177c78679e8SDavid S. Miller 		goto nla_put_failure;
3178c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
3179c78679e8SDavid S. Miller 		goto nla_put_failure;
31808253947eSLi Wei 
31818253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
318269cdf8f9SYOSHIFUJI Hideaki 
318387a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
3184e3703b3dSThomas Graf 		goto nla_put_failure;
31851da177e4SLinus Torvalds 
3186c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
3187c78ba6d6SLubomir Rintel 		goto nla_put_failure;
3188c78ba6d6SLubomir Rintel 
318961adedf3SJiri Benc 	lwtunnel_fill_encap(skb, rt->dst.lwtstate);
319019e42e45SRoopa Prabhu 
3191053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
3192053c095aSJohannes Berg 	return 0;
31932d7202bfSThomas Graf 
31942d7202bfSThomas Graf nla_put_failure:
319526932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
319626932566SPatrick McHardy 	return -EMSGSIZE;
31971da177e4SLinus Torvalds }
31981da177e4SLinus Torvalds 
31991b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
32001da177e4SLinus Torvalds {
32011da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
32021da177e4SLinus Torvalds 	int prefix;
32031da177e4SLinus Torvalds 
32042d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
32052d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
32061da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
32071da177e4SLinus Torvalds 	} else
32081da177e4SLinus Torvalds 		prefix = 0;
32091da177e4SLinus Torvalds 
3210191cd582SBrian Haley 	return rt6_fill_node(arg->net,
3211191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
321215e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
32137bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
32141da177e4SLinus Torvalds }
32151da177e4SLinus Torvalds 
3216661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
32171da177e4SLinus Torvalds {
32183b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3219ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
32201da177e4SLinus Torvalds 	struct rt6_info *rt;
3221ab364a6fSThomas Graf 	struct sk_buff *skb;
3222ab364a6fSThomas Graf 	struct rtmsg *rtm;
32234c9483b2SDavid S. Miller 	struct flowi6 fl6;
322472331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3225ab364a6fSThomas Graf 
3226ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3227ab364a6fSThomas Graf 	if (err < 0)
3228ab364a6fSThomas Graf 		goto errout;
3229ab364a6fSThomas Graf 
3230ab364a6fSThomas Graf 	err = -EINVAL;
32314c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
3232ab364a6fSThomas Graf 
3233ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3234ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3235ab364a6fSThomas Graf 			goto errout;
3236ab364a6fSThomas Graf 
32374e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3238ab364a6fSThomas Graf 	}
3239ab364a6fSThomas Graf 
3240ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3241ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3242ab364a6fSThomas Graf 			goto errout;
3243ab364a6fSThomas Graf 
32444e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3245ab364a6fSThomas Graf 	}
3246ab364a6fSThomas Graf 
3247ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3248ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3249ab364a6fSThomas Graf 
3250ab364a6fSThomas Graf 	if (tb[RTA_OIF])
325172331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3252ab364a6fSThomas Graf 
32532e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
32542e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
32552e47b291SLorenzo Colitti 
3256ab364a6fSThomas Graf 	if (iif) {
3257ab364a6fSThomas Graf 		struct net_device *dev;
325872331bc0SShmulik Ladkani 		int flags = 0;
325972331bc0SShmulik Ladkani 
32605578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3261ab364a6fSThomas Graf 		if (!dev) {
3262ab364a6fSThomas Graf 			err = -ENODEV;
3263ab364a6fSThomas Graf 			goto errout;
3264ab364a6fSThomas Graf 		}
326572331bc0SShmulik Ladkani 
326672331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
326772331bc0SShmulik Ladkani 
326872331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
326972331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
327072331bc0SShmulik Ladkani 
327172331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
327272331bc0SShmulik Ladkani 							       flags);
327372331bc0SShmulik Ladkani 	} else {
327472331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
327572331bc0SShmulik Ladkani 
327672331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3277ab364a6fSThomas Graf 	}
32781da177e4SLinus Torvalds 
32791da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
328038308473SDavid S. Miller 	if (!skb) {
328194e187c0SAmerigo Wang 		ip6_rt_put(rt);
3282ab364a6fSThomas Graf 		err = -ENOBUFS;
3283ab364a6fSThomas Graf 		goto errout;
3284ab364a6fSThomas Graf 	}
32851da177e4SLinus Torvalds 
32861da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
32871da177e4SLinus Torvalds 	   through good chunk of routing engine.
32881da177e4SLinus Torvalds 	 */
3289459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
32901da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
32911da177e4SLinus Torvalds 
3292d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
32931da177e4SLinus Torvalds 
32944c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
329515e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
32967bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
32971da177e4SLinus Torvalds 	if (err < 0) {
3298ab364a6fSThomas Graf 		kfree_skb(skb);
3299ab364a6fSThomas Graf 		goto errout;
33001da177e4SLinus Torvalds 	}
33011da177e4SLinus Torvalds 
330215e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3303ab364a6fSThomas Graf errout:
33041da177e4SLinus Torvalds 	return err;
33051da177e4SLinus Torvalds }
33061da177e4SLinus Torvalds 
330786872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
33081da177e4SLinus Torvalds {
33091da177e4SLinus Torvalds 	struct sk_buff *skb;
33105578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3311528c4cebSDenis V. Lunev 	u32 seq;
3312528c4cebSDenis V. Lunev 	int err;
33130d51aa80SJamal Hadi Salim 
3314528c4cebSDenis V. Lunev 	err = -ENOBUFS;
331538308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
331686872cb5SThomas Graf 
331719e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
331838308473SDavid S. Miller 	if (!skb)
331921713ebcSThomas Graf 		goto errout;
33201da177e4SLinus Torvalds 
3321191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
332215e47304SEric W. Biederman 				event, info->portid, seq, 0, 0, 0);
332326932566SPatrick McHardy 	if (err < 0) {
332426932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
332526932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
332626932566SPatrick McHardy 		kfree_skb(skb);
332726932566SPatrick McHardy 		goto errout;
332826932566SPatrick McHardy 	}
332915e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
33305578689aSDaniel Lezcano 		    info->nlh, gfp_any());
33311ce85fe4SPablo Neira Ayuso 	return;
333221713ebcSThomas Graf errout:
333321713ebcSThomas Graf 	if (err < 0)
33345578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
33351da177e4SLinus Torvalds }
33361da177e4SLinus Torvalds 
33378ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3338351638e7SJiri Pirko 				unsigned long event, void *ptr)
33398ed67789SDaniel Lezcano {
3340351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3341c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
33428ed67789SDaniel Lezcano 
33438ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3344d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
33458ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
33468ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3347d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
33488ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3349d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
33508ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
33518ed67789SDaniel Lezcano #endif
33528ed67789SDaniel Lezcano 	}
33538ed67789SDaniel Lezcano 
33548ed67789SDaniel Lezcano 	return NOTIFY_OK;
33558ed67789SDaniel Lezcano }
33568ed67789SDaniel Lezcano 
33571da177e4SLinus Torvalds /*
33581da177e4SLinus Torvalds  *	/proc
33591da177e4SLinus Torvalds  */
33601da177e4SLinus Torvalds 
33611da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
33621da177e4SLinus Torvalds 
336333120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
336433120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
336533120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
336633120b30SAlexey Dobriyan 	.read		= seq_read,
336733120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
33688d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
336933120b30SAlexey Dobriyan };
337033120b30SAlexey Dobriyan 
33711da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
33721da177e4SLinus Torvalds {
337369ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
33741da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
337569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
337669ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
337769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
337869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
337969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3380fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
338169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
33821da177e4SLinus Torvalds 
33831da177e4SLinus Torvalds 	return 0;
33841da177e4SLinus Torvalds }
33851da177e4SLinus Torvalds 
33861da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
33871da177e4SLinus Torvalds {
3388de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
338969ddb805SDaniel Lezcano }
339069ddb805SDaniel Lezcano 
33919a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
33921da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
33931da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
33941da177e4SLinus Torvalds 	.read	 = seq_read,
33951da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3396b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
33971da177e4SLinus Torvalds };
33981da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
33991da177e4SLinus Torvalds 
34001da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
34011da177e4SLinus Torvalds 
34021da177e4SLinus Torvalds static
3403fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
34041da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
34051da177e4SLinus Torvalds {
3406c486da34SLucian Adrian Grijincu 	struct net *net;
3407c486da34SLucian Adrian Grijincu 	int delay;
3408c486da34SLucian Adrian Grijincu 	if (!write)
3409c486da34SLucian Adrian Grijincu 		return -EINVAL;
3410c486da34SLucian Adrian Grijincu 
3411c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3412c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
34138d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
34142ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
34151da177e4SLinus Torvalds 	return 0;
34161da177e4SLinus Torvalds }
34171da177e4SLinus Torvalds 
3418fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
34191da177e4SLinus Torvalds 	{
34201da177e4SLinus Torvalds 		.procname	=	"flush",
34214990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
34221da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
342389c8b3a1SDave Jones 		.mode		=	0200,
34246d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
34251da177e4SLinus Torvalds 	},
34261da177e4SLinus Torvalds 	{
34271da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
34289a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
34291da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34301da177e4SLinus Torvalds 		.mode		=	0644,
34316d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34321da177e4SLinus Torvalds 	},
34331da177e4SLinus Torvalds 	{
34341da177e4SLinus Torvalds 		.procname	=	"max_size",
34354990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
34361da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34371da177e4SLinus Torvalds 		.mode		=	0644,
34386d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34391da177e4SLinus Torvalds 	},
34401da177e4SLinus Torvalds 	{
34411da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
34424990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34431da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34441da177e4SLinus Torvalds 		.mode		=	0644,
34456d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34461da177e4SLinus Torvalds 	},
34471da177e4SLinus Torvalds 	{
34481da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
34494990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
34501da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34511da177e4SLinus Torvalds 		.mode		=	0644,
34526d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34531da177e4SLinus Torvalds 	},
34541da177e4SLinus Torvalds 	{
34551da177e4SLinus Torvalds 		.procname	=	"gc_interval",
34564990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
34571da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34581da177e4SLinus Torvalds 		.mode		=	0644,
34596d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34601da177e4SLinus Torvalds 	},
34611da177e4SLinus Torvalds 	{
34621da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
34634990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
34641da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34651da177e4SLinus Torvalds 		.mode		=	0644,
3466f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34671da177e4SLinus Torvalds 	},
34681da177e4SLinus Torvalds 	{
34691da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
34704990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
34711da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34721da177e4SLinus Torvalds 		.mode		=	0644,
34736d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34741da177e4SLinus Torvalds 	},
34751da177e4SLinus Torvalds 	{
34761da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
34774990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
34781da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34791da177e4SLinus Torvalds 		.mode		=	0644,
3480f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34811da177e4SLinus Torvalds 	},
34821da177e4SLinus Torvalds 	{
34831da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
34844990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34851da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34861da177e4SLinus Torvalds 		.mode		=	0644,
34876d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
34881da177e4SLinus Torvalds 	},
3489f8572d8fSEric W. Biederman 	{ }
34901da177e4SLinus Torvalds };
34911da177e4SLinus Torvalds 
34922c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3493760f2d01SDaniel Lezcano {
3494760f2d01SDaniel Lezcano 	struct ctl_table *table;
3495760f2d01SDaniel Lezcano 
3496760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3497760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3498760f2d01SDaniel Lezcano 			GFP_KERNEL);
34995ee09105SYOSHIFUJI Hideaki 
35005ee09105SYOSHIFUJI Hideaki 	if (table) {
35015ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3502c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
350386393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
35045ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
35055ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
35065ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
35075ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
35085ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
35095ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
35105ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
35119c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3512464dc801SEric W. Biederman 
3513464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3514464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3515464dc801SEric W. Biederman 			table[0].procname = NULL;
35165ee09105SYOSHIFUJI Hideaki 	}
35175ee09105SYOSHIFUJI Hideaki 
3518760f2d01SDaniel Lezcano 	return table;
3519760f2d01SDaniel Lezcano }
35201da177e4SLinus Torvalds #endif
35211da177e4SLinus Torvalds 
35222c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3523cdb18761SDaniel Lezcano {
3524633d424bSPavel Emelyanov 	int ret = -ENOMEM;
35258ed67789SDaniel Lezcano 
352686393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
352786393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3528f2fc6a54SBenjamin Thery 
3529fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3530fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3531fc66f95cSEric Dumazet 
35328ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
35338ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
35348ed67789SDaniel Lezcano 					   GFP_KERNEL);
35358ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3536fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3537d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
35388ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3539d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
354062fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
354162fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35428ed67789SDaniel Lezcano 
35438ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
35448ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
35458ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
35468ed67789SDaniel Lezcano 					       GFP_KERNEL);
354768fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
354868fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3549d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
35508ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3551d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
355262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
355362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35548ed67789SDaniel Lezcano 
35558ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
35568ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
35578ed67789SDaniel Lezcano 					       GFP_KERNEL);
355868fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
355968fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3560d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
35618ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3562d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
356362fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
356462fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35658ed67789SDaniel Lezcano #endif
35668ed67789SDaniel Lezcano 
3567b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3568b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3569b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3570b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3571b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3572b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3573b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3574b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3575b339a47cSPeter Zijlstra 
35766891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
35776891a346SBenjamin Thery 
35788ed67789SDaniel Lezcano 	ret = 0;
35798ed67789SDaniel Lezcano out:
35808ed67789SDaniel Lezcano 	return ret;
3581f2fc6a54SBenjamin Thery 
358268fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
358368fffc67SPeter Zijlstra out_ip6_prohibit_entry:
358468fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
358568fffc67SPeter Zijlstra out_ip6_null_entry:
358668fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
358768fffc67SPeter Zijlstra #endif
3588fc66f95cSEric Dumazet out_ip6_dst_entries:
3589fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3590f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3591f2fc6a54SBenjamin Thery 	goto out;
3592cdb18761SDaniel Lezcano }
3593cdb18761SDaniel Lezcano 
35942c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3595cdb18761SDaniel Lezcano {
35968ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
35978ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
35988ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
35998ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
36008ed67789SDaniel Lezcano #endif
360141bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3602cdb18761SDaniel Lezcano }
3603cdb18761SDaniel Lezcano 
3604d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3605d189634eSThomas Graf {
3606d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3607d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3608d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3609d189634eSThomas Graf #endif
3610d189634eSThomas Graf 	return 0;
3611d189634eSThomas Graf }
3612d189634eSThomas Graf 
3613d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3614d189634eSThomas Graf {
3615d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3616ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3617ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3618d189634eSThomas Graf #endif
3619d189634eSThomas Graf }
3620d189634eSThomas Graf 
3621cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3622cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3623cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3624cdb18761SDaniel Lezcano };
3625cdb18761SDaniel Lezcano 
3626c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3627c3426b47SDavid S. Miller {
3628c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3629c3426b47SDavid S. Miller 
3630c3426b47SDavid S. Miller 	if (!bp)
3631c3426b47SDavid S. Miller 		return -ENOMEM;
3632c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3633c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3634c3426b47SDavid S. Miller 	return 0;
3635c3426b47SDavid S. Miller }
3636c3426b47SDavid S. Miller 
3637c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3638c3426b47SDavid S. Miller {
3639c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3640c3426b47SDavid S. Miller 
3641c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
364256a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3643c3426b47SDavid S. Miller 	kfree(bp);
3644c3426b47SDavid S. Miller }
3645c3426b47SDavid S. Miller 
36462b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3647c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3648c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3649c3426b47SDavid S. Miller };
3650c3426b47SDavid S. Miller 
3651d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3652d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3653d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3654d189634eSThomas Graf };
3655d189634eSThomas Graf 
36568ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
36578ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
36588ed67789SDaniel Lezcano 	.priority = 0,
36598ed67789SDaniel Lezcano };
36608ed67789SDaniel Lezcano 
3661433d49c3SDaniel Lezcano int __init ip6_route_init(void)
36621da177e4SLinus Torvalds {
3663433d49c3SDaniel Lezcano 	int ret;
36648d0b94afSMartin KaFai Lau 	int cpu;
3665433d49c3SDaniel Lezcano 
36669a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
36679a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
36689a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
36699a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
36709a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3671c19a28e1SFernando Carrijo 		goto out;
367214e50e57SDavid S. Miller 
3673fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
36748ed67789SDaniel Lezcano 	if (ret)
3675bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3676bdb3289fSDaniel Lezcano 
3677c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3678c3426b47SDavid S. Miller 	if (ret)
3679e8803b6cSDavid S. Miller 		goto out_dst_entries;
36802a0c451aSThomas Graf 
36817e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
36827e52b33bSDavid S. Miller 	if (ret)
36837e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3684c3426b47SDavid S. Miller 
36855dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
36865dc121e9SArnaud Ebalard 
36878ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
36888ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
36898ed67789SDaniel Lezcano 	 * manually for init_net */
3690d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
36918ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3692bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3693d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
36948ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3695d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
36968ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3697bdb3289fSDaniel Lezcano   #endif
3698e8803b6cSDavid S. Miller 	ret = fib6_init();
3699433d49c3SDaniel Lezcano 	if (ret)
37008ed67789SDaniel Lezcano 		goto out_register_subsys;
3701433d49c3SDaniel Lezcano 
3702433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3703433d49c3SDaniel Lezcano 	if (ret)
3704e8803b6cSDavid S. Miller 		goto out_fib6_init;
3705c35b7e72SDaniel Lezcano 
3706433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3707433d49c3SDaniel Lezcano 	if (ret)
3708433d49c3SDaniel Lezcano 		goto xfrm6_init;
37097e5449c2SDaniel Lezcano 
3710d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3711d189634eSThomas Graf 	if (ret)
3712d189634eSThomas Graf 		goto fib6_rules_init;
3713d189634eSThomas Graf 
3714433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3715c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3716c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3717c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3718d189634eSThomas Graf 		goto out_register_late_subsys;
3719433d49c3SDaniel Lezcano 
37208ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3721cdb18761SDaniel Lezcano 	if (ret)
3722d189634eSThomas Graf 		goto out_register_late_subsys;
37238ed67789SDaniel Lezcano 
37248d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
37258d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
37268d0b94afSMartin KaFai Lau 
37278d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
37288d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
37298d0b94afSMartin KaFai Lau 	}
37308d0b94afSMartin KaFai Lau 
3731433d49c3SDaniel Lezcano out:
3732433d49c3SDaniel Lezcano 	return ret;
3733433d49c3SDaniel Lezcano 
3734d189634eSThomas Graf out_register_late_subsys:
3735d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3736433d49c3SDaniel Lezcano fib6_rules_init:
3737433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3738433d49c3SDaniel Lezcano xfrm6_init:
3739433d49c3SDaniel Lezcano 	xfrm6_fini();
37402a0c451aSThomas Graf out_fib6_init:
37412a0c451aSThomas Graf 	fib6_gc_cleanup();
37428ed67789SDaniel Lezcano out_register_subsys:
37438ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
37447e52b33bSDavid S. Miller out_register_inetpeer:
37457e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3746fc66f95cSEric Dumazet out_dst_entries:
3747fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3748433d49c3SDaniel Lezcano out_kmem_cache:
3749f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3750433d49c3SDaniel Lezcano 	goto out;
37511da177e4SLinus Torvalds }
37521da177e4SLinus Torvalds 
37531da177e4SLinus Torvalds void ip6_route_cleanup(void)
37541da177e4SLinus Torvalds {
37558ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3756d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3757101367c2SThomas Graf 	fib6_rules_cleanup();
37581da177e4SLinus Torvalds 	xfrm6_fini();
37591da177e4SLinus Torvalds 	fib6_gc_cleanup();
3760c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
37618ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
376241bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3763f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
37641da177e4SLinus Torvalds }
3765