xref: /openbmc/linux/net/ipv6/route.c (revision ede2059dbaf9c6557a49d466c8c7778343b208ff)
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);
89*ede2059dSEric W. Biederman static int		ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
907150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
91*ede2059dSEric W. Biederman static int		ip6_pkt_prohibit_out(struct net *net, 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,
2299f8955ccSEric W. Biederman 	.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,
311*ede2059dSEric W. Biederman 		.output		= dst_discard_out,
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 {
424644d0e65STom Herbert 	return get_hash_from_flowi6(fl6) % candidate_count;
42551ebd318SNicolas Dichtel }
42651ebd318SNicolas Dichtel 
42751ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
42852bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
42952bd4c0cSNicolas Dichtel 					     int strict)
43051ebd318SNicolas Dichtel {
43151ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
43251ebd318SNicolas Dichtel 	int route_choosen;
43351ebd318SNicolas Dichtel 
43451ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
43551ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
43651ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
43751ebd318SNicolas Dichtel 	 */
43851ebd318SNicolas Dichtel 	if (route_choosen)
43951ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
44051ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
44151ebd318SNicolas Dichtel 			route_choosen--;
44251ebd318SNicolas Dichtel 			if (route_choosen == 0) {
44352bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
44452bd4c0cSNicolas Dichtel 					break;
44551ebd318SNicolas Dichtel 				match = sibling;
44651ebd318SNicolas Dichtel 				break;
44751ebd318SNicolas Dichtel 			}
44851ebd318SNicolas Dichtel 		}
44951ebd318SNicolas Dichtel 	return match;
45051ebd318SNicolas Dichtel }
45151ebd318SNicolas Dichtel 
4521da177e4SLinus Torvalds /*
453c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4541da177e4SLinus Torvalds  */
4551da177e4SLinus Torvalds 
4568ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4578ed67789SDaniel Lezcano 						    struct rt6_info *rt,
458b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4591da177e4SLinus Torvalds 						    int oif,
460d420895eSYOSHIFUJI Hideaki 						    int flags)
4611da177e4SLinus Torvalds {
4621da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4631da177e4SLinus Torvalds 	struct rt6_info *sprt;
4641da177e4SLinus Torvalds 
465dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
466dd3abc4eSYOSHIFUJI Hideaki 		goto out;
467dd3abc4eSYOSHIFUJI Hideaki 
468d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
469d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
470dd3abc4eSYOSHIFUJI Hideaki 
471dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4721da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4731da177e4SLinus Torvalds 				return sprt;
4741da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
47538308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4761da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
47717fb0b2bSDavid Ahern 					if (flags & RT6_LOOKUP_F_IFACE)
4781da177e4SLinus Torvalds 						continue;
47917fb0b2bSDavid Ahern 					if (local &&
48017fb0b2bSDavid Ahern 					    local->rt6i_idev->dev->ifindex == oif)
4811da177e4SLinus Torvalds 						continue;
4821da177e4SLinus Torvalds 				}
4831da177e4SLinus Torvalds 				local = sprt;
4841da177e4SLinus Torvalds 			}
485dd3abc4eSYOSHIFUJI Hideaki 		} else {
486dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
487dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
488dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
489dd3abc4eSYOSHIFUJI Hideaki 		}
4901da177e4SLinus Torvalds 	}
4911da177e4SLinus Torvalds 
492dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
4931da177e4SLinus Torvalds 		if (local)
4941da177e4SLinus Torvalds 			return local;
4951da177e4SLinus Torvalds 
496d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
4978ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
4981da177e4SLinus Torvalds 	}
499dd3abc4eSYOSHIFUJI Hideaki out:
5001da177e4SLinus Torvalds 	return rt;
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds 
50327097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
504c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
505c2f17e82SHannes Frederic Sowa 	struct work_struct work;
506c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
507c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
508c2f17e82SHannes Frederic Sowa };
509c2f17e82SHannes Frederic Sowa 
510c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
511c2f17e82SHannes Frederic Sowa {
512c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
513c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
514c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
515c2f17e82SHannes Frederic Sowa 
516c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
51738cf595bSJiri Benc 	ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, NULL);
518c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
519662f5533SMichael Büsch 	kfree(work);
520c2f17e82SHannes Frederic Sowa }
521c2f17e82SHannes Frederic Sowa 
52227097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
52327097255SYOSHIFUJI Hideaki {
524990edb42SMartin KaFai Lau 	struct __rt6_probe_work *work;
525f2c31e32SEric Dumazet 	struct neighbour *neigh;
52627097255SYOSHIFUJI Hideaki 	/*
52727097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
52827097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
52927097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
53027097255SYOSHIFUJI Hideaki 	 *
53127097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
53227097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
53327097255SYOSHIFUJI Hideaki 	 */
5342152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
535fdd6681dSAmerigo Wang 		return;
5362152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5372152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5382152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5398d6c31bfSMartin KaFai Lau 		if (neigh->nud_state & NUD_VALID)
5408d6c31bfSMartin KaFai Lau 			goto out;
5418d6c31bfSMartin KaFai Lau 
542990edb42SMartin KaFai Lau 		work = NULL;
5432152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
544990edb42SMartin KaFai Lau 		if (!(neigh->nud_state & NUD_VALID) &&
545990edb42SMartin KaFai Lau 		    time_after(jiffies,
546990edb42SMartin KaFai Lau 			       neigh->updated +
547990edb42SMartin KaFai Lau 			       rt->rt6i_idev->cnf.rtr_probe_interval)) {
548c2f17e82SHannes Frederic Sowa 			work = kmalloc(sizeof(*work), GFP_ATOMIC);
549990edb42SMartin KaFai Lau 			if (work)
5507e980569SJiri Benc 				__neigh_set_probe_once(neigh);
551990edb42SMartin KaFai Lau 		}
552c2f17e82SHannes Frederic Sowa 		write_unlock(&neigh->lock);
553990edb42SMartin KaFai Lau 	} else {
554990edb42SMartin KaFai Lau 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
555990edb42SMartin KaFai Lau 	}
556c2f17e82SHannes Frederic Sowa 
557c2f17e82SHannes Frederic Sowa 	if (work) {
558c2f17e82SHannes Frederic Sowa 		INIT_WORK(&work->work, rt6_probe_deferred);
559c2f17e82SHannes Frederic Sowa 		work->target = rt->rt6i_gateway;
560c2f17e82SHannes Frederic Sowa 		dev_hold(rt->dst.dev);
561c2f17e82SHannes Frederic Sowa 		work->dev = rt->dst.dev;
562c2f17e82SHannes Frederic Sowa 		schedule_work(&work->work);
563c2f17e82SHannes Frederic Sowa 	}
564990edb42SMartin KaFai Lau 
5658d6c31bfSMartin KaFai Lau out:
5662152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
567f2c31e32SEric Dumazet }
56827097255SYOSHIFUJI Hideaki #else
56927097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
57027097255SYOSHIFUJI Hideaki {
57127097255SYOSHIFUJI Hideaki }
57227097255SYOSHIFUJI Hideaki #endif
57327097255SYOSHIFUJI Hideaki 
5741da177e4SLinus Torvalds /*
575554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5761da177e4SLinus Torvalds  */
577b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5781da177e4SLinus Torvalds {
579d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
580161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
581554cfb7eSYOSHIFUJI Hideaki 		return 2;
582161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
583161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
584161980f4SDavid S. Miller 		return 1;
585554cfb7eSYOSHIFUJI Hideaki 	return 0;
5861da177e4SLinus Torvalds }
5871da177e4SLinus Torvalds 
588afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
5891da177e4SLinus Torvalds {
590f2c31e32SEric Dumazet 	struct neighbour *neigh;
591afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
592f2c31e32SEric Dumazet 
5934d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
5944d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
595afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
596145a3621SYOSHIFUJI Hideaki / 吉藤英明 
597145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
598145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
599145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
600145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
601554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
602afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
603398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
604a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
605afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
6067e980569SJiri Benc 		else
6077e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
608398bcbebSYOSHIFUJI Hideaki #endif
609145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
610afc154e9SHannes Frederic Sowa 	} else {
611afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6127e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
613a5a81f0bSPaul Marks 	}
614145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
615145a3621SYOSHIFUJI Hideaki / 吉藤英明 
616a5a81f0bSPaul Marks 	return ret;
6171da177e4SLinus Torvalds }
6181da177e4SLinus Torvalds 
619554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
620554cfb7eSYOSHIFUJI Hideaki 			   int strict)
621554cfb7eSYOSHIFUJI Hideaki {
622a5a81f0bSPaul Marks 	int m;
6234d0c5911SYOSHIFUJI Hideaki 
6244d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
62577d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
626afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
627ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
628ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
629ebacaaa0SYOSHIFUJI Hideaki #endif
630afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
631afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
632afc154e9SHannes Frederic Sowa 		if (n < 0)
633afc154e9SHannes Frederic Sowa 			return n;
634afc154e9SHannes Frederic Sowa 	}
635554cfb7eSYOSHIFUJI Hideaki 	return m;
636554cfb7eSYOSHIFUJI Hideaki }
637554cfb7eSYOSHIFUJI Hideaki 
638f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
639afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
640afc154e9SHannes Frederic Sowa 				   bool *do_rr)
641554cfb7eSYOSHIFUJI Hideaki {
642554cfb7eSYOSHIFUJI Hideaki 	int m;
643afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
64435103d11SAndy Gospodarek 	struct inet6_dev *idev = rt->rt6i_idev;
64535103d11SAndy Gospodarek 	struct net_device *dev = rt->dst.dev;
64635103d11SAndy Gospodarek 
64735103d11SAndy Gospodarek 	if (dev && !netif_carrier_ok(dev) &&
64835103d11SAndy Gospodarek 	    idev->cnf.ignore_routes_with_linkdown)
64935103d11SAndy Gospodarek 		goto out;
650554cfb7eSYOSHIFUJI Hideaki 
651554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
652f11e6659SDavid S. Miller 		goto out;
653554cfb7eSYOSHIFUJI Hideaki 
654554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6557e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
656afc154e9SHannes Frederic Sowa 		match_do_rr = true;
657afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6587e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
659f11e6659SDavid S. Miller 		goto out;
6601da177e4SLinus Torvalds 	}
661f11e6659SDavid S. Miller 
662afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
663afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
664afc154e9SHannes Frederic Sowa 
6657e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
666afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
667afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
668afc154e9SHannes Frederic Sowa 		*mpri = m;
669afc154e9SHannes Frederic Sowa 		match = rt;
670afc154e9SHannes Frederic Sowa 	}
671f11e6659SDavid S. Miller out:
672f11e6659SDavid S. Miller 	return match;
6731da177e4SLinus Torvalds }
6741da177e4SLinus Torvalds 
675f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
676f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
677afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
678afc154e9SHannes Frederic Sowa 				     bool *do_rr)
679f11e6659SDavid S. Miller {
6809fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
681f11e6659SDavid S. Miller 	int mpri = -1;
682f11e6659SDavid S. Miller 
683f11e6659SDavid S. Miller 	match = NULL;
6849fbdcfafSSteffen Klassert 	cont = NULL;
6859fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
6869fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6879fbdcfafSSteffen Klassert 			cont = rt;
6889fbdcfafSSteffen Klassert 			break;
6899fbdcfafSSteffen Klassert 		}
6909fbdcfafSSteffen Klassert 
691afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6929fbdcfafSSteffen Klassert 	}
6939fbdcfafSSteffen Klassert 
6949fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
6959fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6969fbdcfafSSteffen Klassert 			cont = rt;
6979fbdcfafSSteffen Klassert 			break;
6989fbdcfafSSteffen Klassert 		}
6999fbdcfafSSteffen Klassert 
7009fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
7019fbdcfafSSteffen Klassert 	}
7029fbdcfafSSteffen Klassert 
7039fbdcfafSSteffen Klassert 	if (match || !cont)
7049fbdcfafSSteffen Klassert 		return match;
7059fbdcfafSSteffen Klassert 
7069fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
707afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
708f11e6659SDavid S. Miller 
709f11e6659SDavid S. Miller 	return match;
710f11e6659SDavid S. Miller }
711f11e6659SDavid S. Miller 
712f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
713f11e6659SDavid S. Miller {
714f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
7158ed67789SDaniel Lezcano 	struct net *net;
716afc154e9SHannes Frederic Sowa 	bool do_rr = false;
717f11e6659SDavid S. Miller 
718f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
719f11e6659SDavid S. Miller 	if (!rt0)
720f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
721f11e6659SDavid S. Miller 
722afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
723afc154e9SHannes Frederic Sowa 			     &do_rr);
724f11e6659SDavid S. Miller 
725afc154e9SHannes Frederic Sowa 	if (do_rr) {
726d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
727f11e6659SDavid S. Miller 
728554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
729f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
730f11e6659SDavid S. Miller 			next = fn->leaf;
731f11e6659SDavid S. Miller 
732f11e6659SDavid S. Miller 		if (next != rt0)
733f11e6659SDavid S. Miller 			fn->rr_ptr = next;
734554cfb7eSYOSHIFUJI Hideaki 	}
735554cfb7eSYOSHIFUJI Hideaki 
736d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
737a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds 
7408b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7418b9df265SMartin KaFai Lau {
7428b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7438b9df265SMartin KaFai Lau }
7448b9df265SMartin KaFai Lau 
74570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
74670ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
747b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
74870ceb4f5SYOSHIFUJI Hideaki {
749c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
75070ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
75170ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
75270ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7534bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
75470ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
75570ceb4f5SYOSHIFUJI Hideaki 
75670ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
75770ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
75870ceb4f5SYOSHIFUJI Hideaki 	}
75970ceb4f5SYOSHIFUJI Hideaki 
76070ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
76170ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
76270ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
76370ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
76470ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
76570ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
76670ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
76770ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
76870ceb4f5SYOSHIFUJI Hideaki 		}
76970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
77070ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
77170ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
77270ceb4f5SYOSHIFUJI Hideaki 		}
77370ceb4f5SYOSHIFUJI Hideaki 	}
77470ceb4f5SYOSHIFUJI Hideaki 
77570ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
77670ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
7773933fc95SJens Rosenboom 		return -EINVAL;
77870ceb4f5SYOSHIFUJI Hideaki 
7794bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
78070ceb4f5SYOSHIFUJI Hideaki 
78170ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
78270ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
78370ceb4f5SYOSHIFUJI Hideaki 	else {
78470ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
78570ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
78670ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
78770ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
78870ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
78970ceb4f5SYOSHIFUJI Hideaki 	}
79070ceb4f5SYOSHIFUJI Hideaki 
791f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
792f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
793f104a567SDuan Jiong 	else
794f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
795f104a567SDuan Jiong 					gwaddr, dev->ifindex);
79670ceb4f5SYOSHIFUJI Hideaki 
79770ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
798e0a1ad73SThomas Graf 		ip6_del_rt(rt);
79970ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
80070ceb4f5SYOSHIFUJI Hideaki 	}
80170ceb4f5SYOSHIFUJI Hideaki 
80270ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
803efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
80470ceb4f5SYOSHIFUJI Hideaki 					pref);
80570ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
80670ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
80770ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
80870ceb4f5SYOSHIFUJI Hideaki 
80970ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
8101716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
8111716a961SGao feng 			rt6_clean_expires(rt);
8121716a961SGao feng 		else
8131716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
8141716a961SGao feng 
81594e187c0SAmerigo Wang 		ip6_rt_put(rt);
81670ceb4f5SYOSHIFUJI Hideaki 	}
81770ceb4f5SYOSHIFUJI Hideaki 	return 0;
81870ceb4f5SYOSHIFUJI Hideaki }
81970ceb4f5SYOSHIFUJI Hideaki #endif
82070ceb4f5SYOSHIFUJI Hideaki 
821a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
822a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
823a3c00e46SMartin KaFai Lau {
824a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
825a3c00e46SMartin KaFai Lau 	while (1) {
826a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
827a3c00e46SMartin KaFai Lau 			return NULL;
828a3c00e46SMartin KaFai Lau 		pn = fn->parent;
829a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
830a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
831a3c00e46SMartin KaFai Lau 		else
832a3c00e46SMartin KaFai Lau 			fn = pn;
833a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
834a3c00e46SMartin KaFai Lau 			return fn;
835a3c00e46SMartin KaFai Lau 	}
836a3c00e46SMartin KaFai Lau }
837c71099acSThomas Graf 
8388ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8398ed67789SDaniel Lezcano 					     struct fib6_table *table,
8404c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8411da177e4SLinus Torvalds {
8421da177e4SLinus Torvalds 	struct fib6_node *fn;
8431da177e4SLinus Torvalds 	struct rt6_info *rt;
8441da177e4SLinus Torvalds 
845c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8464c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
847c71099acSThomas Graf restart:
848c71099acSThomas Graf 	rt = fn->leaf;
8494c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
85051ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
85152bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
852a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
853a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
854a3c00e46SMartin KaFai Lau 		if (fn)
855a3c00e46SMartin KaFai Lau 			goto restart;
856a3c00e46SMartin KaFai Lau 	}
857d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
858c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8591da177e4SLinus Torvalds 	return rt;
860c71099acSThomas Graf 
861c71099acSThomas Graf }
862c71099acSThomas Graf 
863ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
864ea6e574eSFlorian Westphal 				    int flags)
865ea6e574eSFlorian Westphal {
866ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
867ea6e574eSFlorian Westphal }
868ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
869ea6e574eSFlorian Westphal 
8709acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8719acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
872c71099acSThomas Graf {
8734c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8744c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8754c9483b2SDavid S. Miller 		.daddr = *daddr,
876c71099acSThomas Graf 	};
877c71099acSThomas Graf 	struct dst_entry *dst;
87877d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
879c71099acSThomas Graf 
880adaa70bbSThomas Graf 	if (saddr) {
8814c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
882adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
883adaa70bbSThomas Graf 	}
884adaa70bbSThomas Graf 
8854c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
886c71099acSThomas Graf 	if (dst->error == 0)
887c71099acSThomas Graf 		return (struct rt6_info *) dst;
888c71099acSThomas Graf 
889c71099acSThomas Graf 	dst_release(dst);
890c71099acSThomas Graf 
8911da177e4SLinus Torvalds 	return NULL;
8921da177e4SLinus Torvalds }
8937159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
8947159039aSYOSHIFUJI Hideaki 
895c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
8961da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
8971da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
8981da177e4SLinus Torvalds    be destroyed.
8991da177e4SLinus Torvalds  */
9001da177e4SLinus Torvalds 
901e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
902e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
9031da177e4SLinus Torvalds {
9041da177e4SLinus Torvalds 	int err;
905c71099acSThomas Graf 	struct fib6_table *table;
9061da177e4SLinus Torvalds 
907c71099acSThomas Graf 	table = rt->rt6i_table;
908c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
909e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
910c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds 	return err;
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds 
91540e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
91640e22e8fSThomas Graf {
917e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
918e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
919e715b6d3SFlorian Westphal 
920e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
92140e22e8fSThomas Graf }
92240e22e8fSThomas Graf 
9238b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
92421efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
925b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9261da177e4SLinus Torvalds {
9271da177e4SLinus Torvalds 	struct rt6_info *rt;
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 	/*
9301da177e4SLinus Torvalds 	 *	Clone the route.
9311da177e4SLinus Torvalds 	 */
9321da177e4SLinus Torvalds 
933d52d3997SMartin KaFai Lau 	if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU))
93483a09abdSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
9351da177e4SLinus Torvalds 
936ad706862SMartin KaFai Lau 	rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0);
93783a09abdSMartin KaFai Lau 
93883a09abdSMartin KaFai Lau 	if (!rt)
93983a09abdSMartin KaFai Lau 		return NULL;
94083a09abdSMartin KaFai Lau 
94183a09abdSMartin KaFai Lau 	ip6_rt_copy_init(rt, ort);
9428b9df265SMartin KaFai Lau 	rt->rt6i_flags |= RTF_CACHE;
94383a09abdSMartin KaFai Lau 	rt->rt6i_metric = 0;
94483a09abdSMartin KaFai Lau 	rt->dst.flags |= DST_HOST;
94583a09abdSMartin KaFai Lau 	rt->rt6i_dst.addr = *daddr;
94683a09abdSMartin KaFai Lau 	rt->rt6i_dst.plen = 128;
9478b9df265SMartin KaFai Lau 
9488b9df265SMartin KaFai Lau 	if (!rt6_is_gw_or_nonexthop(ort)) {
949bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
95021efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
95158c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
9521da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9531da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
9544e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
9551da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
9561da177e4SLinus Torvalds 		}
9571da177e4SLinus Torvalds #endif
95895a9a5baSYOSHIFUJI Hideaki 	}
95995a9a5baSYOSHIFUJI Hideaki 
960299d9939SYOSHIFUJI Hideaki 	return rt;
961299d9939SYOSHIFUJI Hideaki }
962299d9939SYOSHIFUJI Hideaki 
963d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt)
964d52d3997SMartin KaFai Lau {
965d52d3997SMartin KaFai Lau 	struct rt6_info *pcpu_rt;
966d52d3997SMartin KaFai Lau 
967d52d3997SMartin KaFai Lau 	pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev),
968ad706862SMartin KaFai Lau 				  rt->dst.dev, rt->dst.flags);
969d52d3997SMartin KaFai Lau 
970d52d3997SMartin KaFai Lau 	if (!pcpu_rt)
971d52d3997SMartin KaFai Lau 		return NULL;
972d52d3997SMartin KaFai Lau 	ip6_rt_copy_init(pcpu_rt, rt);
973d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_protocol = rt->rt6i_protocol;
974d52d3997SMartin KaFai Lau 	pcpu_rt->rt6i_flags |= RTF_PCPU;
975d52d3997SMartin KaFai Lau 	return pcpu_rt;
976d52d3997SMartin KaFai Lau }
977d52d3997SMartin KaFai Lau 
978d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */
979d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
980d52d3997SMartin KaFai Lau {
981a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, **p;
982d52d3997SMartin KaFai Lau 
983d52d3997SMartin KaFai Lau 	p = this_cpu_ptr(rt->rt6i_pcpu);
984d52d3997SMartin KaFai Lau 	pcpu_rt = *p;
985d52d3997SMartin KaFai Lau 
986a73e4195SMartin KaFai Lau 	if (pcpu_rt) {
987a73e4195SMartin KaFai Lau 		dst_hold(&pcpu_rt->dst);
988a73e4195SMartin KaFai Lau 		rt6_dst_from_metrics_check(pcpu_rt);
989a73e4195SMartin KaFai Lau 	}
990a73e4195SMartin KaFai Lau 	return pcpu_rt;
991a73e4195SMartin KaFai Lau }
992a73e4195SMartin KaFai Lau 
993a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
994a73e4195SMartin KaFai Lau {
9959c7370a1SMartin KaFai Lau 	struct fib6_table *table = rt->rt6i_table;
996a73e4195SMartin KaFai Lau 	struct rt6_info *pcpu_rt, *prev, **p;
997d52d3997SMartin KaFai Lau 
998d52d3997SMartin KaFai Lau 	pcpu_rt = ip6_rt_pcpu_alloc(rt);
999d52d3997SMartin KaFai Lau 	if (!pcpu_rt) {
1000d52d3997SMartin KaFai Lau 		struct net *net = dev_net(rt->dst.dev);
1001d52d3997SMartin KaFai Lau 
10029c7370a1SMartin KaFai Lau 		dst_hold(&net->ipv6.ip6_null_entry->dst);
10039c7370a1SMartin KaFai Lau 		return net->ipv6.ip6_null_entry;
1004d52d3997SMartin KaFai Lau 	}
1005d52d3997SMartin KaFai Lau 
10069c7370a1SMartin KaFai Lau 	read_lock_bh(&table->tb6_lock);
10079c7370a1SMartin KaFai Lau 	if (rt->rt6i_pcpu) {
1008a73e4195SMartin KaFai Lau 		p = this_cpu_ptr(rt->rt6i_pcpu);
1009d52d3997SMartin KaFai Lau 		prev = cmpxchg(p, NULL, pcpu_rt);
1010d52d3997SMartin KaFai Lau 		if (prev) {
1011d52d3997SMartin KaFai Lau 			/* If someone did it before us, return prev instead */
1012d52d3997SMartin KaFai Lau 			dst_destroy(&pcpu_rt->dst);
1013d52d3997SMartin KaFai Lau 			pcpu_rt = prev;
1014d52d3997SMartin KaFai Lau 		}
10159c7370a1SMartin KaFai Lau 	} else {
10169c7370a1SMartin KaFai Lau 		/* rt has been removed from the fib6 tree
10179c7370a1SMartin KaFai Lau 		 * before we have a chance to acquire the read_lock.
10189c7370a1SMartin KaFai Lau 		 * In this case, don't brother to create a pcpu rt
10199c7370a1SMartin KaFai Lau 		 * since rt is going away anyway.  The next
10209c7370a1SMartin KaFai Lau 		 * dst_check() will trigger a re-lookup.
10219c7370a1SMartin KaFai Lau 		 */
10229c7370a1SMartin KaFai Lau 		dst_destroy(&pcpu_rt->dst);
10239c7370a1SMartin KaFai Lau 		pcpu_rt = rt;
10249c7370a1SMartin KaFai Lau 	}
1025d52d3997SMartin KaFai Lau 	dst_hold(&pcpu_rt->dst);
1026d52d3997SMartin KaFai Lau 	rt6_dst_from_metrics_check(pcpu_rt);
10279c7370a1SMartin KaFai Lau 	read_unlock_bh(&table->tb6_lock);
1028d52d3997SMartin KaFai Lau 	return pcpu_rt;
1029d52d3997SMartin KaFai Lau }
1030d52d3997SMartin KaFai Lau 
10318ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
10324c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
10331da177e4SLinus Torvalds {
1034367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
103545e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
1036c71099acSThomas Graf 	int strict = 0;
10371da177e4SLinus Torvalds 
103877d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
1039367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
1040367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
10411da177e4SLinus Torvalds 
1042c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
10431da177e4SLinus Torvalds 
10444c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1045367efcb9SMartin KaFai Lau 	saved_fn = fn;
10461da177e4SLinus Torvalds 
1047a3c00e46SMartin KaFai Lau redo_rt6_select:
1048367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
104952bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
1050367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
1051a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1052a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1053a3c00e46SMartin KaFai Lau 		if (fn)
1054a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
1055367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
1056367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
1057367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
1058367efcb9SMartin KaFai Lau 			fn = saved_fn;
1059367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
1060367efcb9SMartin KaFai Lau 		}
1061a3c00e46SMartin KaFai Lau 	}
1062a3c00e46SMartin KaFai Lau 
1063d52d3997SMartin KaFai Lau 
1064d52d3997SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
10653da59bd9SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1066c71099acSThomas Graf 		read_unlock_bh(&table->tb6_lock);
10671da177e4SLinus Torvalds 
1068d52d3997SMartin KaFai Lau 		rt6_dst_from_metrics_check(rt);
1069d52d3997SMartin KaFai Lau 		return rt;
10703da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
10713da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
10723da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
10733da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
10743da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
10753da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
10763da59bd9SMartin KaFai Lau 		 */
1077c71099acSThomas Graf 
10783da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
10793da59bd9SMartin KaFai Lau 
1080d52d3997SMartin KaFai Lau 		dst_use(&rt->dst, jiffies);
1081d52d3997SMartin KaFai Lau 		read_unlock_bh(&table->tb6_lock);
1082d52d3997SMartin KaFai Lau 
10833da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
10843da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
10853da59bd9SMartin KaFai Lau 
10863da59bd9SMartin KaFai Lau 		if (uncached_rt)
10878d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
10883da59bd9SMartin KaFai Lau 		else
10893da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
1090d52d3997SMartin KaFai Lau 
10913da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
10923da59bd9SMartin KaFai Lau 		return uncached_rt;
10933da59bd9SMartin KaFai Lau 
1094d52d3997SMartin KaFai Lau 	} else {
1095d52d3997SMartin KaFai Lau 		/* Get a percpu copy */
1096d52d3997SMartin KaFai Lau 
1097d52d3997SMartin KaFai Lau 		struct rt6_info *pcpu_rt;
1098d52d3997SMartin KaFai Lau 
1099d52d3997SMartin KaFai Lau 		rt->dst.lastuse = jiffies;
1100d52d3997SMartin KaFai Lau 		rt->dst.__use++;
1101d52d3997SMartin KaFai Lau 		pcpu_rt = rt6_get_pcpu_route(rt);
1102d52d3997SMartin KaFai Lau 
11039c7370a1SMartin KaFai Lau 		if (pcpu_rt) {
1104d52d3997SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11059c7370a1SMartin KaFai Lau 		} else {
11069c7370a1SMartin KaFai Lau 			/* We have to do the read_unlock first
11079c7370a1SMartin KaFai Lau 			 * because rt6_make_pcpu_route() may trigger
11089c7370a1SMartin KaFai Lau 			 * ip6_dst_gc() which will take the write_lock.
11099c7370a1SMartin KaFai Lau 			 */
11109c7370a1SMartin KaFai Lau 			dst_hold(&rt->dst);
11119c7370a1SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
11129c7370a1SMartin KaFai Lau 			pcpu_rt = rt6_make_pcpu_route(rt);
11139c7370a1SMartin KaFai Lau 			dst_release(&rt->dst);
11149c7370a1SMartin KaFai Lau 		}
1115d52d3997SMartin KaFai Lau 
1116d52d3997SMartin KaFai Lau 		return pcpu_rt;
11179c7370a1SMartin KaFai Lau 
1118d52d3997SMartin KaFai Lau 	}
1119c71099acSThomas Graf }
1120c71099acSThomas Graf 
11218ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11224c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
11234acad72dSPavel Emelyanov {
11244c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
11254acad72dSPavel Emelyanov }
11264acad72dSPavel Emelyanov 
112772331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
112872331bc0SShmulik Ladkani 						struct net_device *dev,
112972331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
113072331bc0SShmulik Ladkani {
113172331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
113272331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
113372331bc0SShmulik Ladkani 
113472331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
113572331bc0SShmulik Ladkani }
113672331bc0SShmulik Ladkani 
1137c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1138c71099acSThomas Graf {
1139b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1140c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1141adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1142904af04dSJiri Benc 	struct ip_tunnel_info *tun_info;
11434c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
11444c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
11454c9483b2SDavid S. Miller 		.daddr = iph->daddr,
11464c9483b2SDavid S. Miller 		.saddr = iph->saddr,
11476502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
11484c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
11494c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1150c71099acSThomas Graf 	};
1151adaa70bbSThomas Graf 
1152904af04dSJiri Benc 	tun_info = skb_tunnel_info(skb);
115346fa062aSJiri Benc 	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
1154904af04dSJiri Benc 		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
115506e9d040SJiri Benc 	skb_dst_drop(skb);
115672331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1157c71099acSThomas Graf }
1158c71099acSThomas Graf 
11598ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
11604c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1161c71099acSThomas Graf {
11624c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1163c71099acSThomas Graf }
1164c71099acSThomas Graf 
11659c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
11664c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1167c71099acSThomas Graf {
1168c71099acSThomas Graf 	int flags = 0;
1169c71099acSThomas Graf 
11701fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
11714dc27d1cSDavid McCullough 
1172741a11d9SDavid Ahern 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
1173741a11d9SDavid Ahern 	    fl6->flowi6_oif)
117477d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1175c71099acSThomas Graf 
11764c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
1177adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
11780c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
11790c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1180adaa70bbSThomas Graf 
11814c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
11821da177e4SLinus Torvalds }
11837159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
11841da177e4SLinus Torvalds 
11852774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
118614e50e57SDavid S. Miller {
11875c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
118814e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
118914e50e57SDavid S. Miller 
1190f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
119114e50e57SDavid S. Miller 	if (rt) {
1192d8d1f30bSChangli Gao 		new = &rt->dst;
119314e50e57SDavid S. Miller 
11948104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
11958104891bSSteffen Klassert 
119614e50e57SDavid S. Miller 		new->__use = 1;
1197352e512cSHerbert Xu 		new->input = dst_discard;
1198*ede2059dSEric W. Biederman 		new->output = dst_discard_out;
119914e50e57SDavid S. Miller 
120021efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
120121efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
120221efcfa0SEric Dumazet 		else
1203defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
120414e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
120514e50e57SDavid S. Miller 		if (rt->rt6i_idev)
120614e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
120714e50e57SDavid S. Miller 
12084e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
12091716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
121014e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
121114e50e57SDavid S. Miller 
121214e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
121314e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
121414e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
121514e50e57SDavid S. Miller #endif
121614e50e57SDavid S. Miller 
121714e50e57SDavid S. Miller 		dst_free(new);
121814e50e57SDavid S. Miller 	}
121914e50e57SDavid S. Miller 
122069ead7afSDavid S. Miller 	dst_release(dst_orig);
122169ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
122214e50e57SDavid S. Miller }
122314e50e57SDavid S. Miller 
12241da177e4SLinus Torvalds /*
12251da177e4SLinus Torvalds  *	Destination cache support functions
12261da177e4SLinus Torvalds  */
12271da177e4SLinus Torvalds 
12284b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
12294b32b5adSMartin KaFai Lau {
12304b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
12314b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
12324b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
12334b32b5adSMartin KaFai Lau }
12344b32b5adSMartin KaFai Lau 
12353da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
12363da59bd9SMartin KaFai Lau {
12373da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
12383da59bd9SMartin KaFai Lau 		return NULL;
12393da59bd9SMartin KaFai Lau 
12403da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
12413da59bd9SMartin KaFai Lau 		return NULL;
12423da59bd9SMartin KaFai Lau 
12433da59bd9SMartin KaFai Lau 	return &rt->dst;
12443da59bd9SMartin KaFai Lau }
12453da59bd9SMartin KaFai Lau 
12463da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
12473da59bd9SMartin KaFai Lau {
12483da59bd9SMartin KaFai Lau 	if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
12493da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
12503da59bd9SMartin KaFai Lau 		return &rt->dst;
12513da59bd9SMartin KaFai Lau 	else
12523da59bd9SMartin KaFai Lau 		return NULL;
12533da59bd9SMartin KaFai Lau }
12543da59bd9SMartin KaFai Lau 
12551da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
12561da177e4SLinus Torvalds {
12571da177e4SLinus Torvalds 	struct rt6_info *rt;
12581da177e4SLinus Torvalds 
12591da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
12601da177e4SLinus Torvalds 
12616f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
12626f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
12636f3118b5SNicolas Dichtel 	 * into this function always.
12646f3118b5SNicolas Dichtel 	 */
1265e3bc10bdSHannes Frederic Sowa 
12664b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
12674b32b5adSMartin KaFai Lau 
1268d52d3997SMartin KaFai Lau 	if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE))
12693da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
12703da59bd9SMartin KaFai Lau 	else
12713da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
12721da177e4SLinus Torvalds }
12731da177e4SLinus Torvalds 
12741da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
12751da177e4SLinus Torvalds {
12761da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds 	if (rt) {
127954c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
128054c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1281e0a1ad73SThomas Graf 				ip6_del_rt(rt);
128254c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
12831da177e4SLinus Torvalds 			}
128454c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
128554c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
128654c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
128754c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
128854c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
128954c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
12901da177e4SLinus Torvalds }
12911da177e4SLinus Torvalds 
12921da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
12931da177e4SLinus Torvalds {
12941da177e4SLinus Torvalds 	struct rt6_info *rt;
12951da177e4SLinus Torvalds 
12963ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
12971da177e4SLinus Torvalds 
1298adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
12991da177e4SLinus Torvalds 	if (rt) {
13001eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
13011eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
13028e3d5be7SMartin KaFai Lau 			ip6_del_rt(rt);
13031eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
13041da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
13051da177e4SLinus Torvalds 		}
13061da177e4SLinus Torvalds 	}
13071eb4f758SHannes Frederic Sowa }
13081da177e4SLinus Torvalds 
130945e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
131045e4fd26SMartin KaFai Lau {
131145e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
131245e4fd26SMartin KaFai Lau 
131345e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
131445e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
131545e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
131645e4fd26SMartin KaFai Lau }
131745e4fd26SMartin KaFai Lau 
131845e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
131945e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
13201da177e4SLinus Torvalds {
13211da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
13221da177e4SLinus Torvalds 
132345e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
132445e4fd26SMartin KaFai Lau 		return;
132545e4fd26SMartin KaFai Lau 
132681aded24SDavid S. Miller 	dst_confirm(dst);
132745e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
132845e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
132945e4fd26SMartin KaFai Lau 		return;
133081aded24SDavid S. Miller 
133145e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_CACHE) {
133245e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
133345e4fd26SMartin KaFai Lau 	} else {
133445e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
133545e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
13369d289715SHagen Paul Pfeifer 
133745e4fd26SMartin KaFai Lau 		if (iph) {
133845e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
133945e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
134045e4fd26SMartin KaFai Lau 		} else if (sk) {
134145e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
134245e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
134345e4fd26SMartin KaFai Lau 		} else {
134445e4fd26SMartin KaFai Lau 			return;
13451da177e4SLinus Torvalds 		}
134645e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
134745e4fd26SMartin KaFai Lau 		if (nrt6) {
134845e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
134945e4fd26SMartin KaFai Lau 
135045e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
135145e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
135245e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
135345e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
135445e4fd26SMartin KaFai Lau 			 */
135545e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
135645e4fd26SMartin KaFai Lau 		}
135745e4fd26SMartin KaFai Lau 	}
135845e4fd26SMartin KaFai Lau }
135945e4fd26SMartin KaFai Lau 
136045e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
136145e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
136245e4fd26SMartin KaFai Lau {
136345e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
13641da177e4SLinus Torvalds }
13651da177e4SLinus Torvalds 
136642ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
136742ae66c8SDavid S. Miller 		     int oif, u32 mark)
136881aded24SDavid S. Miller {
136981aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
137081aded24SDavid S. Miller 	struct dst_entry *dst;
137181aded24SDavid S. Miller 	struct flowi6 fl6;
137281aded24SDavid S. Miller 
137381aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
137481aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
13751b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
137681aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
137781aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
13786502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
137981aded24SDavid S. Miller 
138081aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
138181aded24SDavid S. Miller 	if (!dst->error)
138245e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
138381aded24SDavid S. Miller 	dst_release(dst);
138481aded24SDavid S. Miller }
138581aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
138681aded24SDavid S. Miller 
138781aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
138881aded24SDavid S. Miller {
138981aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
139081aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
139181aded24SDavid S. Miller }
139281aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
139381aded24SDavid S. Miller 
1394b55b76b2SDuan Jiong /* Handle redirects */
1395b55b76b2SDuan Jiong struct ip6rd_flowi {
1396b55b76b2SDuan Jiong 	struct flowi6 fl6;
1397b55b76b2SDuan Jiong 	struct in6_addr gateway;
1398b55b76b2SDuan Jiong };
1399b55b76b2SDuan Jiong 
1400b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1401b55b76b2SDuan Jiong 					     struct fib6_table *table,
1402b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1403b55b76b2SDuan Jiong 					     int flags)
1404b55b76b2SDuan Jiong {
1405b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1406b55b76b2SDuan Jiong 	struct rt6_info *rt;
1407b55b76b2SDuan Jiong 	struct fib6_node *fn;
1408b55b76b2SDuan Jiong 
1409b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1410b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1411b55b76b2SDuan Jiong 	 *
1412b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1413b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1414b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1415b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1416b55b76b2SDuan Jiong 	 * routes.
1417b55b76b2SDuan Jiong 	 */
1418b55b76b2SDuan Jiong 
1419b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1420b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1421b55b76b2SDuan Jiong restart:
1422b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1423b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1424b55b76b2SDuan Jiong 			continue;
1425b55b76b2SDuan Jiong 		if (rt->dst.error)
1426b55b76b2SDuan Jiong 			break;
1427b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1428b55b76b2SDuan Jiong 			continue;
1429b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1430b55b76b2SDuan Jiong 			continue;
1431b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1432b55b76b2SDuan Jiong 			continue;
1433b55b76b2SDuan Jiong 		break;
1434b55b76b2SDuan Jiong 	}
1435b55b76b2SDuan Jiong 
1436b55b76b2SDuan Jiong 	if (!rt)
1437b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1438b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1439b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1440b0a1ba59SMartin KaFai Lau 		goto out;
1441b0a1ba59SMartin KaFai Lau 	}
1442b0a1ba59SMartin KaFai Lau 
1443b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1444a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1445a3c00e46SMartin KaFai Lau 		if (fn)
1446a3c00e46SMartin KaFai Lau 			goto restart;
1447b55b76b2SDuan Jiong 	}
1448a3c00e46SMartin KaFai Lau 
1449b0a1ba59SMartin KaFai Lau out:
1450b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1451b55b76b2SDuan Jiong 
1452b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1453b55b76b2SDuan Jiong 
1454b55b76b2SDuan Jiong 	return rt;
1455b55b76b2SDuan Jiong };
1456b55b76b2SDuan Jiong 
1457b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1458b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1459b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1460b55b76b2SDuan Jiong {
1461b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1462b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1463b55b76b2SDuan Jiong 
1464b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1465b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1466b55b76b2SDuan Jiong 
1467b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1468b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1469b55b76b2SDuan Jiong }
1470b55b76b2SDuan Jiong 
14713a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
14723a5ad2eeSDavid S. Miller {
14733a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
14743a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
14753a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
14763a5ad2eeSDavid S. Miller 
14773a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1478e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
14793a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
14803a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
14813a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
14823a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
14836502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
14843a5ad2eeSDavid S. Miller 
1485b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
14866700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
14873a5ad2eeSDavid S. Miller 	dst_release(dst);
14883a5ad2eeSDavid S. Miller }
14893a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
14903a5ad2eeSDavid S. Miller 
1491c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1492c92a59ecSDuan Jiong 			    u32 mark)
1493c92a59ecSDuan Jiong {
1494c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1495c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1496c92a59ecSDuan Jiong 	struct dst_entry *dst;
1497c92a59ecSDuan Jiong 	struct flowi6 fl6;
1498c92a59ecSDuan Jiong 
1499c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1500e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1501c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1502c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1503c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1504c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1505c92a59ecSDuan Jiong 
1506b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1507c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1508c92a59ecSDuan Jiong 	dst_release(dst);
1509c92a59ecSDuan Jiong }
1510c92a59ecSDuan Jiong 
15113a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
15123a5ad2eeSDavid S. Miller {
15133a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
15143a5ad2eeSDavid S. Miller }
15153a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
15163a5ad2eeSDavid S. Miller 
15170dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
15181da177e4SLinus Torvalds {
15190dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
15200dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
15210dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
15220dbaee3bSDavid S. Miller 
15231da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
15241da177e4SLinus Torvalds 
15255578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
15265578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
15271da177e4SLinus Torvalds 
15281da177e4SLinus Torvalds 	/*
15291da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
15301da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
15311da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
15321da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
15331da177e4SLinus Torvalds 	 */
15341da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
15351da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
15361da177e4SLinus Torvalds 	return mtu;
15371da177e4SLinus Torvalds }
15381da177e4SLinus Torvalds 
1539ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1540d33e4553SDavid S. Miller {
15414b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
15424b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1543d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1544618f9bc7SSteffen Klassert 
1545618f9bc7SSteffen Klassert 	if (mtu)
154630f78d8eSEric Dumazet 		goto out;
1547618f9bc7SSteffen Klassert 
15484b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
15494b32b5adSMartin KaFai Lau 	if (mtu)
15504b32b5adSMartin KaFai Lau 		goto out;
15514b32b5adSMartin KaFai Lau 
1552618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1553d33e4553SDavid S. Miller 
1554d33e4553SDavid S. Miller 	rcu_read_lock();
1555d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1556d33e4553SDavid S. Miller 	if (idev)
1557d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1558d33e4553SDavid S. Miller 	rcu_read_unlock();
1559d33e4553SDavid S. Miller 
156030f78d8eSEric Dumazet out:
156130f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1562d33e4553SDavid S. Miller }
1563d33e4553SDavid S. Miller 
15643b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
15653b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
15665d0bbeebSThomas Graf 
15673b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
156887a11578SDavid S. Miller 				  struct flowi6 *fl6)
15691da177e4SLinus Torvalds {
157087a11578SDavid S. Miller 	struct dst_entry *dst;
15711da177e4SLinus Torvalds 	struct rt6_info *rt;
15721da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1573c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
15741da177e4SLinus Torvalds 
157538308473SDavid S. Miller 	if (unlikely(!idev))
1576122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
15771da177e4SLinus Torvalds 
1578ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, dev, 0);
157938308473SDavid S. Miller 	if (unlikely(!rt)) {
15801da177e4SLinus Torvalds 		in6_dev_put(idev);
158187a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
15821da177e4SLinus Torvalds 		goto out;
15831da177e4SLinus Torvalds 	}
15841da177e4SLinus Torvalds 
15858e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
15868e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1587d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1588550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
158987a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
15908e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
15918e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
159214edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
15931da177e4SLinus Torvalds 
15943b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1595d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1596d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
15973b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
15981da177e4SLinus Torvalds 
15995578689aSDaniel Lezcano 	fib6_force_start_gc(net);
16001da177e4SLinus Torvalds 
160187a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
160287a11578SDavid S. Miller 
16031da177e4SLinus Torvalds out:
160487a11578SDavid S. Miller 	return dst;
16051da177e4SLinus Torvalds }
16061da177e4SLinus Torvalds 
16073d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
16081da177e4SLinus Torvalds {
1609e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
16103d0f24a7SStephen Hemminger 	int more = 0;
16111da177e4SLinus Torvalds 
16123b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
16133b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
16145d0bbeebSThomas Graf 
16151da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
16161da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
16171da177e4SLinus Torvalds 			*pprev = dst->next;
16181da177e4SLinus Torvalds 			dst_free(dst);
16191da177e4SLinus Torvalds 		} else {
16201da177e4SLinus Torvalds 			pprev = &dst->next;
16213d0f24a7SStephen Hemminger 			++more;
16221da177e4SLinus Torvalds 		}
16231da177e4SLinus Torvalds 	}
16241da177e4SLinus Torvalds 
16253b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
16265d0bbeebSThomas Graf 
16273d0f24a7SStephen Hemminger 	return more;
16281da177e4SLinus Torvalds }
16291da177e4SLinus Torvalds 
16301e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
16311e493d19SDavid S. Miller 			    void *arg)
16321e493d19SDavid S. Miller {
16331e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
16341e493d19SDavid S. Miller 
16351e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
16361e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
16371e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
16381e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
16391e493d19SDavid S. Miller 		if (func(rt, arg)) {
16401e493d19SDavid S. Miller 			*pprev = dst->next;
16411e493d19SDavid S. Miller 			dst_free(dst);
16421e493d19SDavid S. Miller 		} else {
16431e493d19SDavid S. Miller 			pprev = &dst->next;
16441e493d19SDavid S. Miller 		}
16451e493d19SDavid S. Miller 	}
16461e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
16471e493d19SDavid S. Miller }
16481e493d19SDavid S. Miller 
1649569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
16501da177e4SLinus Torvalds {
165186393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
16527019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
16537019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
16547019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
16557019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
16567019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1657fc66f95cSEric Dumazet 	int entries;
16581da177e4SLinus Torvalds 
1659fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
166049a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1661fc66f95cSEric Dumazet 	    entries <= rt_max_size)
16621da177e4SLinus Torvalds 		goto out;
16631da177e4SLinus Torvalds 
16646891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
166514956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1666fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1667fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
16687019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
16691da177e4SLinus Torvalds out:
16707019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1671fc66f95cSEric Dumazet 	return entries > rt_max_size;
16721da177e4SLinus Torvalds }
16731da177e4SLinus Torvalds 
1674e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1675e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1676e715b6d3SFlorian Westphal {
1677c3a8d947SDaniel Borkmann 	bool ecn_ca = false;
1678e715b6d3SFlorian Westphal 	struct nlattr *nla;
1679e715b6d3SFlorian Westphal 	int remaining;
1680e715b6d3SFlorian Westphal 	u32 *mp;
1681e715b6d3SFlorian Westphal 
168263159f29SIan Morris 	if (!cfg->fc_mx)
1683e715b6d3SFlorian Westphal 		return 0;
1684e715b6d3SFlorian Westphal 
1685e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1686e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1687e715b6d3SFlorian Westphal 		return -ENOMEM;
1688e715b6d3SFlorian Westphal 
1689e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1690e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1691ea697639SDaniel Borkmann 		u32 val;
1692ea697639SDaniel Borkmann 
16931bb14807SDaniel Borkmann 		if (!type)
16941bb14807SDaniel Borkmann 			continue;
1695e715b6d3SFlorian Westphal 		if (unlikely(type > RTAX_MAX))
1696e715b6d3SFlorian Westphal 			goto err;
16971bb14807SDaniel Borkmann 
1698ea697639SDaniel Borkmann 		if (type == RTAX_CC_ALGO) {
1699ea697639SDaniel Borkmann 			char tmp[TCP_CA_NAME_MAX];
1700e715b6d3SFlorian Westphal 
1701ea697639SDaniel Borkmann 			nla_strlcpy(tmp, nla, sizeof(tmp));
1702c3a8d947SDaniel Borkmann 			val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
1703ea697639SDaniel Borkmann 			if (val == TCP_CA_UNSPEC)
1704ea697639SDaniel Borkmann 				goto err;
1705ea697639SDaniel Borkmann 		} else {
1706ea697639SDaniel Borkmann 			val = nla_get_u32(nla);
1707ea697639SDaniel Borkmann 		}
1708b8d3e416SDaniel Borkmann 		if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
1709b8d3e416SDaniel Borkmann 			goto err;
1710ea697639SDaniel Borkmann 
1711ea697639SDaniel Borkmann 		mp[type - 1] = val;
1712e715b6d3SFlorian Westphal 		__set_bit(type - 1, mxc->mx_valid);
1713e715b6d3SFlorian Westphal 	}
1714e715b6d3SFlorian Westphal 
1715c3a8d947SDaniel Borkmann 	if (ecn_ca) {
1716c3a8d947SDaniel Borkmann 		__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
1717c3a8d947SDaniel Borkmann 		mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
1718c3a8d947SDaniel Borkmann 	}
1719e715b6d3SFlorian Westphal 
1720c3a8d947SDaniel Borkmann 	mxc->mx = mp;
1721e715b6d3SFlorian Westphal 	return 0;
1722e715b6d3SFlorian Westphal  err:
1723e715b6d3SFlorian Westphal 	kfree(mp);
1724e715b6d3SFlorian Westphal 	return -EINVAL;
1725e715b6d3SFlorian Westphal }
17261da177e4SLinus Torvalds 
17276b9ea5a6SRoopa Prabhu int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret)
17281da177e4SLinus Torvalds {
17291da177e4SLinus Torvalds 	int err;
17305578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
17311da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
17321da177e4SLinus Torvalds 	struct net_device *dev = NULL;
17331da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1734c71099acSThomas Graf 	struct fib6_table *table;
17351da177e4SLinus Torvalds 	int addr_type;
17361da177e4SLinus Torvalds 
173786872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
17381da177e4SLinus Torvalds 		return -EINVAL;
17391da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
174086872cb5SThomas Graf 	if (cfg->fc_src_len)
17411da177e4SLinus Torvalds 		return -EINVAL;
17421da177e4SLinus Torvalds #endif
174386872cb5SThomas Graf 	if (cfg->fc_ifindex) {
17441da177e4SLinus Torvalds 		err = -ENODEV;
17455578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
17461da177e4SLinus Torvalds 		if (!dev)
17471da177e4SLinus Torvalds 			goto out;
17481da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
17491da177e4SLinus Torvalds 		if (!idev)
17501da177e4SLinus Torvalds 			goto out;
17511da177e4SLinus Torvalds 	}
17521da177e4SLinus Torvalds 
175386872cb5SThomas Graf 	if (cfg->fc_metric == 0)
175486872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
17551da177e4SLinus Torvalds 
1756c71099acSThomas Graf 	err = -ENOBUFS;
175738308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1758d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1759d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
176038308473SDavid S. Miller 		if (!table) {
1761f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1762d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1763d71314b4SMatti Vaittinen 		}
1764d71314b4SMatti Vaittinen 	} else {
1765d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1766d71314b4SMatti Vaittinen 	}
176738308473SDavid S. Miller 
176838308473SDavid S. Miller 	if (!table)
1769c71099acSThomas Graf 		goto out;
1770c71099acSThomas Graf 
1771ad706862SMartin KaFai Lau 	rt = ip6_dst_alloc(net, NULL,
1772ad706862SMartin KaFai Lau 			   (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT);
17731da177e4SLinus Torvalds 
177438308473SDavid S. Miller 	if (!rt) {
17751da177e4SLinus Torvalds 		err = -ENOMEM;
17761da177e4SLinus Torvalds 		goto out;
17771da177e4SLinus Torvalds 	}
17781da177e4SLinus Torvalds 
17791716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
17801716a961SGao feng 		rt6_set_expires(rt, jiffies +
17811716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
17821716a961SGao feng 	else
17831716a961SGao feng 		rt6_clean_expires(rt);
17841da177e4SLinus Torvalds 
178586872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
178686872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
178786872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
178886872cb5SThomas Graf 
178986872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
17901da177e4SLinus Torvalds 
17911da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1792d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1793ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1794ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
17951da177e4SLinus Torvalds 	else
1796d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
17971da177e4SLinus Torvalds 
1798d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
17991da177e4SLinus Torvalds 
180019e42e45SRoopa Prabhu 	if (cfg->fc_encap) {
180119e42e45SRoopa Prabhu 		struct lwtunnel_state *lwtstate;
180219e42e45SRoopa Prabhu 
180319e42e45SRoopa Prabhu 		err = lwtunnel_build_state(dev, cfg->fc_encap_type,
1804127eb7cdSTom Herbert 					   cfg->fc_encap, AF_INET6, cfg,
1805127eb7cdSTom Herbert 					   &lwtstate);
180619e42e45SRoopa Prabhu 		if (err)
180719e42e45SRoopa Prabhu 			goto out;
180861adedf3SJiri Benc 		rt->dst.lwtstate = lwtstate_get(lwtstate);
180961adedf3SJiri Benc 		if (lwtunnel_output_redirect(rt->dst.lwtstate)) {
181061adedf3SJiri Benc 			rt->dst.lwtstate->orig_output = rt->dst.output;
181161adedf3SJiri Benc 			rt->dst.output = lwtunnel_output;
181219e42e45SRoopa Prabhu 		}
181361adedf3SJiri Benc 		if (lwtunnel_input_redirect(rt->dst.lwtstate)) {
181461adedf3SJiri Benc 			rt->dst.lwtstate->orig_input = rt->dst.input;
181561adedf3SJiri Benc 			rt->dst.input = lwtunnel_input;
181625368623STom Herbert 		}
181725368623STom Herbert 	}
181819e42e45SRoopa Prabhu 
181986872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
182086872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1821afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
182211d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
18231da177e4SLinus Torvalds 
18241da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
182586872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
182686872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
18271da177e4SLinus Torvalds #endif
18281da177e4SLinus Torvalds 
182986872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
18301da177e4SLinus Torvalds 
18311da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
18321da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
18331da177e4SLinus Torvalds 	 */
183486872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
183538308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
183638308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
183738308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
18381da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
18395578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
18401da177e4SLinus Torvalds 			if (dev) {
18411da177e4SLinus Torvalds 				dev_put(dev);
18421da177e4SLinus Torvalds 				in6_dev_put(idev);
18431da177e4SLinus Torvalds 			}
18445578689aSDaniel Lezcano 			dev = net->loopback_dev;
18451da177e4SLinus Torvalds 			dev_hold(dev);
18461da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
18471da177e4SLinus Torvalds 			if (!idev) {
18481da177e4SLinus Torvalds 				err = -ENODEV;
18491da177e4SLinus Torvalds 				goto out;
18501da177e4SLinus Torvalds 			}
18511da177e4SLinus Torvalds 		}
18521da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1853ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1854ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1855ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1856*ede2059dSEric W. Biederman 			rt->dst.output = dst_discard_out;
18577150aedeSKamala R 			rt->dst.input = dst_discard;
1858ef2c7d7bSNicolas Dichtel 			break;
1859ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1860ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
18617150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
18627150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1863ef2c7d7bSNicolas Dichtel 			break;
1864b4949ab2SNicolas Dichtel 		case RTN_THROW:
18650315e382SNikola Forró 		case RTN_UNREACHABLE:
1866ef2c7d7bSNicolas Dichtel 		default:
18677150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
18680315e382SNikola Forró 					: (cfg->fc_type == RTN_UNREACHABLE)
18690315e382SNikola Forró 					? -EHOSTUNREACH : -ENETUNREACH;
18707150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
18717150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1872ef2c7d7bSNicolas Dichtel 			break;
1873ef2c7d7bSNicolas Dichtel 		}
18741da177e4SLinus Torvalds 		goto install_route;
18751da177e4SLinus Torvalds 	}
18761da177e4SLinus Torvalds 
187786872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1878b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
18791da177e4SLinus Torvalds 		int gwa_type;
18801da177e4SLinus Torvalds 
188186872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1882330567b7SFlorian Westphal 		gwa_type = ipv6_addr_type(gw_addr);
188348ed7b26SFlorian Westphal 
188448ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
188548ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
188648ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
188748ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
188848ed7b26SFlorian Westphal 		 */
188948ed7b26SFlorian Westphal 		err = -EINVAL;
1890330567b7SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr,
1891330567b7SFlorian Westphal 					    gwa_type & IPV6_ADDR_LINKLOCAL ?
1892330567b7SFlorian Westphal 					    dev : NULL, 0, 0))
189348ed7b26SFlorian Westphal 			goto out;
189448ed7b26SFlorian Westphal 
18954e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
18961da177e4SLinus Torvalds 
18971da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
18981da177e4SLinus Torvalds 			struct rt6_info *grt;
18991da177e4SLinus Torvalds 
19001da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
19011da177e4SLinus Torvalds 			   addresses as nexthop address.
19021da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
19031da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
19041da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
19051da177e4SLinus Torvalds 			   some exceptions. --ANK
19061da177e4SLinus Torvalds 			 */
19071da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
19081da177e4SLinus Torvalds 				goto out;
19091da177e4SLinus Torvalds 
19105578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
19111da177e4SLinus Torvalds 
19121da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
191338308473SDavid S. Miller 			if (!grt)
19141da177e4SLinus Torvalds 				goto out;
19151da177e4SLinus Torvalds 			if (dev) {
1916d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
191794e187c0SAmerigo Wang 					ip6_rt_put(grt);
19181da177e4SLinus Torvalds 					goto out;
19191da177e4SLinus Torvalds 				}
19201da177e4SLinus Torvalds 			} else {
1921d1918542SDavid S. Miller 				dev = grt->dst.dev;
19221da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
19231da177e4SLinus Torvalds 				dev_hold(dev);
19241da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
19251da177e4SLinus Torvalds 			}
19261da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
19271da177e4SLinus Torvalds 				err = 0;
192894e187c0SAmerigo Wang 			ip6_rt_put(grt);
19291da177e4SLinus Torvalds 
19301da177e4SLinus Torvalds 			if (err)
19311da177e4SLinus Torvalds 				goto out;
19321da177e4SLinus Torvalds 		}
19331da177e4SLinus Torvalds 		err = -EINVAL;
193438308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
19351da177e4SLinus Torvalds 			goto out;
19361da177e4SLinus Torvalds 	}
19371da177e4SLinus Torvalds 
19381da177e4SLinus Torvalds 	err = -ENODEV;
193938308473SDavid S. Miller 	if (!dev)
19401da177e4SLinus Torvalds 		goto out;
19411da177e4SLinus Torvalds 
1942c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1943c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1944c3968a85SDaniel Walter 			err = -EINVAL;
1945c3968a85SDaniel Walter 			goto out;
1946c3968a85SDaniel Walter 		}
19474e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1948c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1949c3968a85SDaniel Walter 	} else
1950c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1951c3968a85SDaniel Walter 
195286872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
19531da177e4SLinus Torvalds 
19541da177e4SLinus Torvalds install_route:
1955d8d1f30bSChangli Gao 	rt->dst.dev = dev;
19561da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1957c71099acSThomas Graf 	rt->rt6i_table = table;
195863152fc0SDaniel Lezcano 
1959c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
196063152fc0SDaniel Lezcano 
19616b9ea5a6SRoopa Prabhu 	*rt_ret = rt;
19621da177e4SLinus Torvalds 
19636b9ea5a6SRoopa Prabhu 	return 0;
19641da177e4SLinus Torvalds out:
19651da177e4SLinus Torvalds 	if (dev)
19661da177e4SLinus Torvalds 		dev_put(dev);
19671da177e4SLinus Torvalds 	if (idev)
19681da177e4SLinus Torvalds 		in6_dev_put(idev);
19691da177e4SLinus Torvalds 	if (rt)
1970d8d1f30bSChangli Gao 		dst_free(&rt->dst);
19716b9ea5a6SRoopa Prabhu 
19726b9ea5a6SRoopa Prabhu 	*rt_ret = NULL;
19736b9ea5a6SRoopa Prabhu 
19746b9ea5a6SRoopa Prabhu 	return err;
19756b9ea5a6SRoopa Prabhu }
19766b9ea5a6SRoopa Prabhu 
19776b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg)
19786b9ea5a6SRoopa Prabhu {
19796b9ea5a6SRoopa Prabhu 	struct mx6_config mxc = { .mx = NULL, };
19806b9ea5a6SRoopa Prabhu 	struct rt6_info *rt = NULL;
19816b9ea5a6SRoopa Prabhu 	int err;
19826b9ea5a6SRoopa Prabhu 
19836b9ea5a6SRoopa Prabhu 	err = ip6_route_info_create(cfg, &rt);
19846b9ea5a6SRoopa Prabhu 	if (err)
19856b9ea5a6SRoopa Prabhu 		goto out;
19866b9ea5a6SRoopa Prabhu 
19876b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&mxc, cfg);
19886b9ea5a6SRoopa Prabhu 	if (err)
19896b9ea5a6SRoopa Prabhu 		goto out;
19906b9ea5a6SRoopa Prabhu 
19916b9ea5a6SRoopa Prabhu 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
19926b9ea5a6SRoopa Prabhu 
19936b9ea5a6SRoopa Prabhu 	kfree(mxc.mx);
19946b9ea5a6SRoopa Prabhu 
19956b9ea5a6SRoopa Prabhu 	return err;
19966b9ea5a6SRoopa Prabhu out:
19976b9ea5a6SRoopa Prabhu 	if (rt)
19986b9ea5a6SRoopa Prabhu 		dst_free(&rt->dst);
19996b9ea5a6SRoopa Prabhu 
20001da177e4SLinus Torvalds 	return err;
20011da177e4SLinus Torvalds }
20021da177e4SLinus Torvalds 
200386872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
20041da177e4SLinus Torvalds {
20051da177e4SLinus Torvalds 	int err;
2006c71099acSThomas Graf 	struct fib6_table *table;
2007d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
20081da177e4SLinus Torvalds 
20098e3d5be7SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry ||
20108e3d5be7SMartin KaFai Lau 	    rt->dst.flags & DST_NOCACHE) {
20116825a26cSGao feng 		err = -ENOENT;
20126825a26cSGao feng 		goto out;
20136825a26cSGao feng 	}
20146c813a72SPatrick McHardy 
2015c71099acSThomas Graf 	table = rt->rt6i_table;
2016c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
201786872cb5SThomas Graf 	err = fib6_del(rt, info);
2018c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
20191da177e4SLinus Torvalds 
20206825a26cSGao feng out:
202194e187c0SAmerigo Wang 	ip6_rt_put(rt);
20221da177e4SLinus Torvalds 	return err;
20231da177e4SLinus Torvalds }
20241da177e4SLinus Torvalds 
2025e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
2026e0a1ad73SThomas Graf {
20274d1169c1SDenis V. Lunev 	struct nl_info info = {
2028d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
20294d1169c1SDenis V. Lunev 	};
2030528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
2031e0a1ad73SThomas Graf }
2032e0a1ad73SThomas Graf 
203386872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
20341da177e4SLinus Torvalds {
2035c71099acSThomas Graf 	struct fib6_table *table;
20361da177e4SLinus Torvalds 	struct fib6_node *fn;
20371da177e4SLinus Torvalds 	struct rt6_info *rt;
20381da177e4SLinus Torvalds 	int err = -ESRCH;
20391da177e4SLinus Torvalds 
20405578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
204138308473SDavid S. Miller 	if (!table)
2042c71099acSThomas Graf 		return err;
20431da177e4SLinus Torvalds 
2044c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2045c71099acSThomas Graf 
2046c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
204786872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
204886872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
20491da177e4SLinus Torvalds 
20501da177e4SLinus Torvalds 	if (fn) {
2051d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
20521f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
20531f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
20541f56a01fSMartin KaFai Lau 				continue;
205586872cb5SThomas Graf 			if (cfg->fc_ifindex &&
2056d1918542SDavid S. Miller 			    (!rt->dst.dev ||
2057d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
20581da177e4SLinus Torvalds 				continue;
205986872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
206086872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
20611da177e4SLinus Torvalds 				continue;
206286872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
20631da177e4SLinus Torvalds 				continue;
2064d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2065c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
20661da177e4SLinus Torvalds 
206786872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
20681da177e4SLinus Torvalds 		}
20691da177e4SLinus Torvalds 	}
2070c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
20711da177e4SLinus Torvalds 
20721da177e4SLinus Torvalds 	return err;
20731da177e4SLinus Torvalds }
20741da177e4SLinus Torvalds 
20756700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
2076a6279458SYOSHIFUJI Hideaki {
2077e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
2078a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
2079e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
2080e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
2081e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
2082e8599ff4SDavid S. Miller 	struct neighbour *neigh;
208371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
20846e157b6aSDavid S. Miller 	int optlen, on_link;
20856e157b6aSDavid S. Miller 	u8 *lladdr;
2086e8599ff4SDavid S. Miller 
208729a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
208871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
2089e8599ff4SDavid S. Miller 
2090e8599ff4SDavid S. Miller 	if (optlen < 0) {
20916e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
2092e8599ff4SDavid S. Miller 		return;
2093e8599ff4SDavid S. Miller 	}
2094e8599ff4SDavid S. Miller 
209571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
2096e8599ff4SDavid S. Miller 
209771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
20986e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
2099e8599ff4SDavid S. Miller 		return;
2100e8599ff4SDavid S. Miller 	}
2101e8599ff4SDavid S. Miller 
21026e157b6aSDavid S. Miller 	on_link = 0;
210371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
2104e8599ff4SDavid S. Miller 		on_link = 1;
210571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
2106e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
21076e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
2108e8599ff4SDavid S. Miller 		return;
2109e8599ff4SDavid S. Miller 	}
2110e8599ff4SDavid S. Miller 
2111e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
2112e8599ff4SDavid S. Miller 	if (!in6_dev)
2113e8599ff4SDavid S. Miller 		return;
2114e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
2115e8599ff4SDavid S. Miller 		return;
2116e8599ff4SDavid S. Miller 
2117e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
2118e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
2119e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
2120e8599ff4SDavid S. Miller 	 */
2121e8599ff4SDavid S. Miller 
212271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
2123e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
2124e8599ff4SDavid S. Miller 		return;
2125e8599ff4SDavid S. Miller 	}
21266e157b6aSDavid S. Miller 
21276e157b6aSDavid S. Miller 	lladdr = NULL;
2128e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
2129e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
2130e8599ff4SDavid S. Miller 					     skb->dev);
2131e8599ff4SDavid S. Miller 		if (!lladdr) {
2132e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
2133e8599ff4SDavid S. Miller 			return;
2134e8599ff4SDavid S. Miller 		}
2135e8599ff4SDavid S. Miller 	}
2136e8599ff4SDavid S. Miller 
21376e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
21386e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
21396e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
21406e157b6aSDavid S. Miller 		return;
21416e157b6aSDavid S. Miller 	}
21426e157b6aSDavid S. Miller 
21436e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
21446e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
21456e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
21466e157b6aSDavid S. Miller 	 */
21476e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
21486e157b6aSDavid S. Miller 
214971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
2150e8599ff4SDavid S. Miller 	if (!neigh)
2151e8599ff4SDavid S. Miller 		return;
2152e8599ff4SDavid S. Miller 
21531da177e4SLinus Torvalds 	/*
21541da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
21551da177e4SLinus Torvalds 	 */
21561da177e4SLinus Torvalds 
21571da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
21581da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
21591da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
21601da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
21611da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
21621da177e4SLinus Torvalds 		     );
21631da177e4SLinus Torvalds 
216483a09abdSMartin KaFai Lau 	nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL);
216538308473SDavid S. Miller 	if (!nrt)
21661da177e4SLinus Torvalds 		goto out;
21671da177e4SLinus Torvalds 
21681da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
21691da177e4SLinus Torvalds 	if (on_link)
21701da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
21711da177e4SLinus Torvalds 
21724e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
21731da177e4SLinus Torvalds 
217440e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
21751da177e4SLinus Torvalds 		goto out;
21761da177e4SLinus Torvalds 
2177d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
2178d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
217971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
218060592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
21818d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
21828d71740cSTom Tucker 
21831da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
21846e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2185e0a1ad73SThomas Graf 		ip6_del_rt(rt);
21861da177e4SLinus Torvalds 	}
21871da177e4SLinus Torvalds 
21881da177e4SLinus Torvalds out:
2189e8599ff4SDavid S. Miller 	neigh_release(neigh);
21906e157b6aSDavid S. Miller }
21916e157b6aSDavid S. Miller 
21921da177e4SLinus Torvalds /*
21931da177e4SLinus Torvalds  *	Misc support functions
21941da177e4SLinus Torvalds  */
21951da177e4SLinus Torvalds 
21964b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
21974b32b5adSMartin KaFai Lau {
21984b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
21994b32b5adSMartin KaFai Lau 
22004b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
22014b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
22024b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
22034b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
22044b32b5adSMartin KaFai Lau }
22054b32b5adSMartin KaFai Lau 
220683a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
22071da177e4SLinus Torvalds {
2208d8d1f30bSChangli Gao 	rt->dst.input = ort->dst.input;
2209d8d1f30bSChangli Gao 	rt->dst.output = ort->dst.output;
221083a09abdSMartin KaFai Lau 	rt->rt6i_dst = ort->rt6i_dst;
2211d8d1f30bSChangli Gao 	rt->dst.error = ort->dst.error;
22121da177e4SLinus Torvalds 	rt->rt6i_idev = ort->rt6i_idev;
22131da177e4SLinus Torvalds 	if (rt->rt6i_idev)
22141da177e4SLinus Torvalds 		in6_dev_hold(rt->rt6i_idev);
2215d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
22164e3fd7a0SAlexey Dobriyan 	rt->rt6i_gateway = ort->rt6i_gateway;
22171716a961SGao feng 	rt->rt6i_flags = ort->rt6i_flags;
22181716a961SGao feng 	rt6_set_from(rt, ort);
221983a09abdSMartin KaFai Lau 	rt->rt6i_metric = ort->rt6i_metric;
22201da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
222183a09abdSMartin KaFai Lau 	rt->rt6i_src = ort->rt6i_src;
22221da177e4SLinus Torvalds #endif
222383a09abdSMartin KaFai Lau 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
2224c71099acSThomas Graf 	rt->rt6i_table = ort->rt6i_table;
222561adedf3SJiri Benc 	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
22261da177e4SLinus Torvalds }
22271da177e4SLinus Torvalds 
222870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2229efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2230b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2231b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
223270ceb4f5SYOSHIFUJI Hideaki {
223370ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
223470ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2235c71099acSThomas Graf 	struct fib6_table *table;
223670ceb4f5SYOSHIFUJI Hideaki 
2237efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
223838308473SDavid S. Miller 	if (!table)
2239c71099acSThomas Graf 		return NULL;
2240c71099acSThomas Graf 
22415744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2242c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
224370ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
224470ceb4f5SYOSHIFUJI Hideaki 		goto out;
224570ceb4f5SYOSHIFUJI Hideaki 
2246d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2247d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
224870ceb4f5SYOSHIFUJI Hideaki 			continue;
224970ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
225070ceb4f5SYOSHIFUJI Hideaki 			continue;
225170ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
225270ceb4f5SYOSHIFUJI Hideaki 			continue;
2253d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
225470ceb4f5SYOSHIFUJI Hideaki 		break;
225570ceb4f5SYOSHIFUJI Hideaki 	}
225670ceb4f5SYOSHIFUJI Hideaki out:
22575744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
225870ceb4f5SYOSHIFUJI Hideaki 	return rt;
225970ceb4f5SYOSHIFUJI Hideaki }
226070ceb4f5SYOSHIFUJI Hideaki 
2261efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2262b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2263b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
226495c96174SEric Dumazet 					   unsigned int pref)
226570ceb4f5SYOSHIFUJI Hideaki {
226686872cb5SThomas Graf 	struct fib6_config cfg = {
226786872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
2268238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
226986872cb5SThomas Graf 		.fc_ifindex	= ifindex,
227086872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
227186872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
227286872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
227315e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2274efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2275efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
227686872cb5SThomas Graf 	};
227770ceb4f5SYOSHIFUJI Hideaki 
22784e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
22794e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
228086872cb5SThomas Graf 
2281e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2282e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
228386872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
228470ceb4f5SYOSHIFUJI Hideaki 
228586872cb5SThomas Graf 	ip6_route_add(&cfg);
228670ceb4f5SYOSHIFUJI Hideaki 
2287efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
228870ceb4f5SYOSHIFUJI Hideaki }
228970ceb4f5SYOSHIFUJI Hideaki #endif
229070ceb4f5SYOSHIFUJI Hideaki 
2291b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
22921da177e4SLinus Torvalds {
22931da177e4SLinus Torvalds 	struct rt6_info *rt;
2294c71099acSThomas Graf 	struct fib6_table *table;
22951da177e4SLinus Torvalds 
2296c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
229738308473SDavid S. Miller 	if (!table)
2298c71099acSThomas Graf 		return NULL;
22991da177e4SLinus Torvalds 
23005744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2301d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2302d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2303045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
23041da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
23051da177e4SLinus Torvalds 			break;
23061da177e4SLinus Torvalds 	}
23071da177e4SLinus Torvalds 	if (rt)
2308d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
23095744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
23101da177e4SLinus Torvalds 	return rt;
23111da177e4SLinus Torvalds }
23121da177e4SLinus Torvalds 
2313b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2314ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2315ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
23161da177e4SLinus Torvalds {
231786872cb5SThomas Graf 	struct fib6_config cfg = {
231886872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
2319238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
232086872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
232186872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
232286872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
232315e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
23245578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2325c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
232686872cb5SThomas Graf 	};
23271da177e4SLinus Torvalds 
23284e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
23291da177e4SLinus Torvalds 
233086872cb5SThomas Graf 	ip6_route_add(&cfg);
23311da177e4SLinus Torvalds 
23321da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
23331da177e4SLinus Torvalds }
23341da177e4SLinus Torvalds 
23357b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
23361da177e4SLinus Torvalds {
23371da177e4SLinus Torvalds 	struct rt6_info *rt;
2338c71099acSThomas Graf 	struct fib6_table *table;
2339c71099acSThomas Graf 
2340c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
23417b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
234238308473SDavid S. Miller 	if (!table)
2343c71099acSThomas Graf 		return;
23441da177e4SLinus Torvalds 
23451da177e4SLinus Torvalds restart:
2346c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2347d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
23483e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
23493e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2350d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2351c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2352e0a1ad73SThomas Graf 			ip6_del_rt(rt);
23531da177e4SLinus Torvalds 			goto restart;
23541da177e4SLinus Torvalds 		}
23551da177e4SLinus Torvalds 	}
2356c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
23571da177e4SLinus Torvalds }
23581da177e4SLinus Torvalds 
23595578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
23605578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
236186872cb5SThomas Graf 				 struct fib6_config *cfg)
236286872cb5SThomas Graf {
236386872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
236486872cb5SThomas Graf 
236586872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
236686872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
236786872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
236886872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
236986872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
237086872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
237186872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
237286872cb5SThomas Graf 
23735578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2374f1243c2dSBenjamin Thery 
23754e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
23764e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
23774e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
237886872cb5SThomas Graf }
237986872cb5SThomas Graf 
23805578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
23811da177e4SLinus Torvalds {
238286872cb5SThomas Graf 	struct fib6_config cfg;
23831da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
23841da177e4SLinus Torvalds 	int err;
23851da177e4SLinus Torvalds 
23861da177e4SLinus Torvalds 	switch (cmd) {
23871da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
23881da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2389af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
23901da177e4SLinus Torvalds 			return -EPERM;
23911da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
23921da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
23931da177e4SLinus Torvalds 		if (err)
23941da177e4SLinus Torvalds 			return -EFAULT;
23951da177e4SLinus Torvalds 
23965578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
239786872cb5SThomas Graf 
23981da177e4SLinus Torvalds 		rtnl_lock();
23991da177e4SLinus Torvalds 		switch (cmd) {
24001da177e4SLinus Torvalds 		case SIOCADDRT:
240186872cb5SThomas Graf 			err = ip6_route_add(&cfg);
24021da177e4SLinus Torvalds 			break;
24031da177e4SLinus Torvalds 		case SIOCDELRT:
240486872cb5SThomas Graf 			err = ip6_route_del(&cfg);
24051da177e4SLinus Torvalds 			break;
24061da177e4SLinus Torvalds 		default:
24071da177e4SLinus Torvalds 			err = -EINVAL;
24081da177e4SLinus Torvalds 		}
24091da177e4SLinus Torvalds 		rtnl_unlock();
24101da177e4SLinus Torvalds 
24111da177e4SLinus Torvalds 		return err;
24123ff50b79SStephen Hemminger 	}
24131da177e4SLinus Torvalds 
24141da177e4SLinus Torvalds 	return -EINVAL;
24151da177e4SLinus Torvalds }
24161da177e4SLinus Torvalds 
24171da177e4SLinus Torvalds /*
24181da177e4SLinus Torvalds  *	Drop the packet on the floor
24191da177e4SLinus Torvalds  */
24201da177e4SLinus Torvalds 
2421d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
24221da177e4SLinus Torvalds {
2423612f09e8SYOSHIFUJI Hideaki 	int type;
2424adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2425612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2426612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
24270660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
242845bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
24293bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24303bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2431612f09e8SYOSHIFUJI Hideaki 			break;
2432612f09e8SYOSHIFUJI Hideaki 		}
2433612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2434612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
24353bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
24363bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2437612f09e8SYOSHIFUJI Hideaki 		break;
2438612f09e8SYOSHIFUJI Hideaki 	}
24393ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
24401da177e4SLinus Torvalds 	kfree_skb(skb);
24411da177e4SLinus Torvalds 	return 0;
24421da177e4SLinus Torvalds }
24431da177e4SLinus Torvalds 
24449ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
24459ce8ade0SThomas Graf {
2446612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
24479ce8ade0SThomas Graf }
24489ce8ade0SThomas Graf 
2449*ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
24501da177e4SLinus Torvalds {
2451adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2452612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
24531da177e4SLinus Torvalds }
24541da177e4SLinus Torvalds 
24559ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
24569ce8ade0SThomas Graf {
2457612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
24589ce8ade0SThomas Graf }
24599ce8ade0SThomas Graf 
2460*ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
24619ce8ade0SThomas Graf {
2462adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2463612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
24649ce8ade0SThomas Graf }
24659ce8ade0SThomas Graf 
24661da177e4SLinus Torvalds /*
24671da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
24681da177e4SLinus Torvalds  */
24691da177e4SLinus Torvalds 
24701da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
24711da177e4SLinus Torvalds 				    const struct in6_addr *addr,
24728f031519SDavid S. Miller 				    bool anycast)
24731da177e4SLinus Torvalds {
2474c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2475a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2476ad706862SMartin KaFai Lau 					    DST_NOCOUNT);
2477a3300ef4SHannes Frederic Sowa 	if (!rt)
24781da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
24791da177e4SLinus Torvalds 
24801da177e4SLinus Torvalds 	in6_dev_hold(idev);
24811da177e4SLinus Torvalds 
248211d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2483d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2484d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
24851da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
24861da177e4SLinus Torvalds 
24871da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
248858c4fb86SYOSHIFUJI Hideaki 	if (anycast)
248958c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
249058c4fb86SYOSHIFUJI Hideaki 	else
24911da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
24921da177e4SLinus Torvalds 
2493550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
24944e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
24951da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
24965578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
24978e3d5be7SMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
24981da177e4SLinus Torvalds 
2499d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
25001da177e4SLinus Torvalds 
25011da177e4SLinus Torvalds 	return rt;
25021da177e4SLinus Torvalds }
25031da177e4SLinus Torvalds 
2504c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2505c3968a85SDaniel Walter 			struct rt6_info *rt,
2506b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2507c3968a85SDaniel Walter 			unsigned int prefs,
2508c3968a85SDaniel Walter 			struct in6_addr *saddr)
2509c3968a85SDaniel Walter {
2510e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2511e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2512c3968a85SDaniel Walter 	int err = 0;
2513e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
25144e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2515c3968a85SDaniel Walter 	else
2516c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2517c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2518c3968a85SDaniel Walter 	return err;
2519c3968a85SDaniel Walter }
2520c3968a85SDaniel Walter 
2521c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2522c3968a85SDaniel Walter struct arg_dev_net_ip {
2523c3968a85SDaniel Walter 	struct net_device *dev;
2524c3968a85SDaniel Walter 	struct net *net;
2525c3968a85SDaniel Walter 	struct in6_addr *addr;
2526c3968a85SDaniel Walter };
2527c3968a85SDaniel Walter 
2528c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2529c3968a85SDaniel Walter {
2530c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2531c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2532c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2533c3968a85SDaniel Walter 
2534d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2535c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2536c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2537c3968a85SDaniel Walter 		/* remove prefsrc entry */
2538c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2539c3968a85SDaniel Walter 	}
2540c3968a85SDaniel Walter 	return 0;
2541c3968a85SDaniel Walter }
2542c3968a85SDaniel Walter 
2543c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2544c3968a85SDaniel Walter {
2545c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2546c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2547c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2548c3968a85SDaniel Walter 		.net = net,
2549c3968a85SDaniel Walter 		.addr = &ifp->addr,
2550c3968a85SDaniel Walter 	};
25510c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2552c3968a85SDaniel Walter }
2553c3968a85SDaniel Walter 
2554be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2555be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2556be7a010dSDuan Jiong 
2557be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2558be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2559be7a010dSDuan Jiong {
2560be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2561be7a010dSDuan Jiong 
2562be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2563be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2564be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2565be7a010dSDuan Jiong 		return -1;
2566be7a010dSDuan Jiong 	}
2567be7a010dSDuan Jiong 	return 0;
2568be7a010dSDuan Jiong }
2569be7a010dSDuan Jiong 
2570be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2571be7a010dSDuan Jiong {
2572be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2573be7a010dSDuan Jiong }
2574be7a010dSDuan Jiong 
25758ed67789SDaniel Lezcano struct arg_dev_net {
25768ed67789SDaniel Lezcano 	struct net_device *dev;
25778ed67789SDaniel Lezcano 	struct net *net;
25788ed67789SDaniel Lezcano };
25798ed67789SDaniel Lezcano 
25801da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
25811da177e4SLinus Torvalds {
2582bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2583bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
25848ed67789SDaniel Lezcano 
2585d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2586c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
25871da177e4SLinus Torvalds 		return -1;
2588c159d30cSDavid S. Miller 
25891da177e4SLinus Torvalds 	return 0;
25901da177e4SLinus Torvalds }
25911da177e4SLinus Torvalds 
2592f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
25931da177e4SLinus Torvalds {
25948ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
25958ed67789SDaniel Lezcano 		.dev = dev,
25968ed67789SDaniel Lezcano 		.net = net,
25978ed67789SDaniel Lezcano 	};
25988ed67789SDaniel Lezcano 
25990c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
26001e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
26018d0b94afSMartin KaFai Lau 	rt6_uncached_list_flush_dev(net, dev);
26021da177e4SLinus Torvalds }
26031da177e4SLinus Torvalds 
260495c96174SEric Dumazet struct rt6_mtu_change_arg {
26051da177e4SLinus Torvalds 	struct net_device *dev;
260695c96174SEric Dumazet 	unsigned int mtu;
26071da177e4SLinus Torvalds };
26081da177e4SLinus Torvalds 
26091da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
26101da177e4SLinus Torvalds {
26111da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
26121da177e4SLinus Torvalds 	struct inet6_dev *idev;
26131da177e4SLinus Torvalds 
26141da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
26151da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
26161da177e4SLinus Torvalds 	   We still use this lock to block changes
26171da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
26181da177e4SLinus Torvalds 	*/
26191da177e4SLinus Torvalds 
26201da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
262138308473SDavid S. Miller 	if (!idev)
26221da177e4SLinus Torvalds 		return 0;
26231da177e4SLinus Torvalds 
26241da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
26251da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
26261da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
26271da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
26281da177e4SLinus Torvalds 	 */
26291da177e4SLinus Torvalds 	/*
26301da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
26311da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
26321da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
26331da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
26341da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
26351da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
26361da177e4SLinus Torvalds 	   PMTU discouvery.
26371da177e4SLinus Torvalds 	 */
2638d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
26394b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
26404b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
26414b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
26424b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
26434b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
26444b32b5adSMartin KaFai Lau 			 * been updated.
26454b32b5adSMartin KaFai Lau 			 */
26464b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
26474b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
26484b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2649d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
26504b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2651defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2652566cfd8fSSimon Arlott 		}
26534b32b5adSMartin KaFai Lau 	}
26541da177e4SLinus Torvalds 	return 0;
26551da177e4SLinus Torvalds }
26561da177e4SLinus Torvalds 
265795c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
26581da177e4SLinus Torvalds {
2659c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2660c71099acSThomas Graf 		.dev = dev,
2661c71099acSThomas Graf 		.mtu = mtu,
2662c71099acSThomas Graf 	};
26631da177e4SLinus Torvalds 
26640c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
26651da177e4SLinus Torvalds }
26661da177e4SLinus Torvalds 
2667ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
26685176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
266986872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2670ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
267186872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
267286872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
267351ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2674c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
267519e42e45SRoopa Prabhu 	[RTA_ENCAP_TYPE]	= { .type = NLA_U16 },
267619e42e45SRoopa Prabhu 	[RTA_ENCAP]		= { .type = NLA_NESTED },
267786872cb5SThomas Graf };
267886872cb5SThomas Graf 
267986872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
268086872cb5SThomas Graf 			      struct fib6_config *cfg)
26811da177e4SLinus Torvalds {
268286872cb5SThomas Graf 	struct rtmsg *rtm;
268386872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2684c78ba6d6SLubomir Rintel 	unsigned int pref;
268586872cb5SThomas Graf 	int err;
26861da177e4SLinus Torvalds 
268786872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
268886872cb5SThomas Graf 	if (err < 0)
268986872cb5SThomas Graf 		goto errout;
26901da177e4SLinus Torvalds 
269186872cb5SThomas Graf 	err = -EINVAL;
269286872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
269386872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
269486872cb5SThomas Graf 
269586872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
269686872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
269786872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
269886872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
269986872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2700ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
270186872cb5SThomas Graf 
2702ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2703ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2704b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2705b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
270686872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
270786872cb5SThomas Graf 
2708ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2709ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2710ab79ad14SMaciej Żenczykowski 
27111f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
27121f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
27131f56a01fSMartin KaFai Lau 
271415e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
271586872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
27163b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
271786872cb5SThomas Graf 
271886872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
271967b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
272086872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
27211da177e4SLinus Torvalds 	}
272286872cb5SThomas Graf 
272386872cb5SThomas Graf 	if (tb[RTA_DST]) {
272486872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
272586872cb5SThomas Graf 
272686872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
272786872cb5SThomas Graf 			goto errout;
272886872cb5SThomas Graf 
272986872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
27301da177e4SLinus Torvalds 	}
273186872cb5SThomas Graf 
273286872cb5SThomas Graf 	if (tb[RTA_SRC]) {
273386872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
273486872cb5SThomas Graf 
273586872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
273686872cb5SThomas Graf 			goto errout;
273786872cb5SThomas Graf 
273886872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
27391da177e4SLinus Torvalds 	}
274086872cb5SThomas Graf 
2741c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
274267b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2743c3968a85SDaniel Walter 
274486872cb5SThomas Graf 	if (tb[RTA_OIF])
274586872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
274686872cb5SThomas Graf 
274786872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
274886872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
274986872cb5SThomas Graf 
275086872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
275186872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
275286872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
27531da177e4SLinus Torvalds 	}
275486872cb5SThomas Graf 
275586872cb5SThomas Graf 	if (tb[RTA_TABLE])
275686872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
275786872cb5SThomas Graf 
275851ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
275951ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
276051ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
276151ebd318SNicolas Dichtel 	}
276251ebd318SNicolas Dichtel 
2763c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2764c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2765c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2766c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2767c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2768c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2769c78ba6d6SLubomir Rintel 	}
2770c78ba6d6SLubomir Rintel 
277119e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP])
277219e42e45SRoopa Prabhu 		cfg->fc_encap = tb[RTA_ENCAP];
277319e42e45SRoopa Prabhu 
277419e42e45SRoopa Prabhu 	if (tb[RTA_ENCAP_TYPE])
277519e42e45SRoopa Prabhu 		cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
277619e42e45SRoopa Prabhu 
277786872cb5SThomas Graf 	err = 0;
277886872cb5SThomas Graf errout:
277986872cb5SThomas Graf 	return err;
27801da177e4SLinus Torvalds }
27811da177e4SLinus Torvalds 
27826b9ea5a6SRoopa Prabhu struct rt6_nh {
27836b9ea5a6SRoopa Prabhu 	struct rt6_info *rt6_info;
27846b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
27856b9ea5a6SRoopa Prabhu 	struct mx6_config mxc;
27866b9ea5a6SRoopa Prabhu 	struct list_head next;
27876b9ea5a6SRoopa Prabhu };
27886b9ea5a6SRoopa Prabhu 
27896b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
27906b9ea5a6SRoopa Prabhu {
27916b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
27926b9ea5a6SRoopa Prabhu 
27936b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
27946b9ea5a6SRoopa Prabhu 		pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n",
27956b9ea5a6SRoopa Prabhu 		        &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway,
27966b9ea5a6SRoopa Prabhu 		        nh->r_cfg.fc_ifindex);
27976b9ea5a6SRoopa Prabhu 	}
27986b9ea5a6SRoopa Prabhu }
27996b9ea5a6SRoopa Prabhu 
28006b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list,
28016b9ea5a6SRoopa Prabhu 				 struct rt6_info *rt, struct fib6_config *r_cfg)
28026b9ea5a6SRoopa Prabhu {
28036b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh;
28046b9ea5a6SRoopa Prabhu 	struct rt6_info *rtnh;
28056b9ea5a6SRoopa Prabhu 	int err = -EEXIST;
28066b9ea5a6SRoopa Prabhu 
28076b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, rt6_nh_list, next) {
28086b9ea5a6SRoopa Prabhu 		/* check if rt6_info already exists */
28096b9ea5a6SRoopa Prabhu 		rtnh = nh->rt6_info;
28106b9ea5a6SRoopa Prabhu 
28116b9ea5a6SRoopa Prabhu 		if (rtnh->dst.dev == rt->dst.dev &&
28126b9ea5a6SRoopa Prabhu 		    rtnh->rt6i_idev == rt->rt6i_idev &&
28136b9ea5a6SRoopa Prabhu 		    ipv6_addr_equal(&rtnh->rt6i_gateway,
28146b9ea5a6SRoopa Prabhu 				    &rt->rt6i_gateway))
28156b9ea5a6SRoopa Prabhu 			return err;
28166b9ea5a6SRoopa Prabhu 	}
28176b9ea5a6SRoopa Prabhu 
28186b9ea5a6SRoopa Prabhu 	nh = kzalloc(sizeof(*nh), GFP_KERNEL);
28196b9ea5a6SRoopa Prabhu 	if (!nh)
28206b9ea5a6SRoopa Prabhu 		return -ENOMEM;
28216b9ea5a6SRoopa Prabhu 	nh->rt6_info = rt;
28226b9ea5a6SRoopa Prabhu 	err = ip6_convert_metrics(&nh->mxc, r_cfg);
28236b9ea5a6SRoopa Prabhu 	if (err) {
28246b9ea5a6SRoopa Prabhu 		kfree(nh);
28256b9ea5a6SRoopa Prabhu 		return err;
28266b9ea5a6SRoopa Prabhu 	}
28276b9ea5a6SRoopa Prabhu 	memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
28286b9ea5a6SRoopa Prabhu 	list_add_tail(&nh->next, rt6_nh_list);
28296b9ea5a6SRoopa Prabhu 
28306b9ea5a6SRoopa Prabhu 	return 0;
28316b9ea5a6SRoopa Prabhu }
28326b9ea5a6SRoopa Prabhu 
28336b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg)
283451ebd318SNicolas Dichtel {
283551ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
283651ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
28376b9ea5a6SRoopa Prabhu 	struct rt6_info *rt;
28386b9ea5a6SRoopa Prabhu 	struct rt6_nh *err_nh;
28396b9ea5a6SRoopa Prabhu 	struct rt6_nh *nh, *nh_safe;
284051ebd318SNicolas Dichtel 	int remaining;
284151ebd318SNicolas Dichtel 	int attrlen;
28426b9ea5a6SRoopa Prabhu 	int err = 1;
28436b9ea5a6SRoopa Prabhu 	int nhn = 0;
28446b9ea5a6SRoopa Prabhu 	int replace = (cfg->fc_nlinfo.nlh &&
28456b9ea5a6SRoopa Prabhu 		       (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
28466b9ea5a6SRoopa Prabhu 	LIST_HEAD(rt6_nh_list);
284751ebd318SNicolas Dichtel 
284835f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
284951ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
285051ebd318SNicolas Dichtel 
28516b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry and build a list (rt6_nh_list) of
28526b9ea5a6SRoopa Prabhu 	 * rt6_info structs per nexthop
28536b9ea5a6SRoopa Prabhu 	 */
285451ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
285551ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
285651ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
285751ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
285851ebd318SNicolas Dichtel 
285951ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
286051ebd318SNicolas Dichtel 		if (attrlen > 0) {
286151ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
286251ebd318SNicolas Dichtel 
286351ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
286451ebd318SNicolas Dichtel 			if (nla) {
286567b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
286651ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
286751ebd318SNicolas Dichtel 			}
286819e42e45SRoopa Prabhu 			r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
286919e42e45SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
287019e42e45SRoopa Prabhu 			if (nla)
287119e42e45SRoopa Prabhu 				r_cfg.fc_encap_type = nla_get_u16(nla);
287251ebd318SNicolas Dichtel 		}
28736b9ea5a6SRoopa Prabhu 
28746b9ea5a6SRoopa Prabhu 		err = ip6_route_info_create(&r_cfg, &rt);
28756b9ea5a6SRoopa Prabhu 		if (err)
28766b9ea5a6SRoopa Prabhu 			goto cleanup;
28776b9ea5a6SRoopa Prabhu 
28786b9ea5a6SRoopa Prabhu 		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
287951ebd318SNicolas Dichtel 		if (err) {
28806b9ea5a6SRoopa Prabhu 			dst_free(&rt->dst);
28816b9ea5a6SRoopa Prabhu 			goto cleanup;
288251ebd318SNicolas Dichtel 		}
28836b9ea5a6SRoopa Prabhu 
28846b9ea5a6SRoopa Prabhu 		rtnh = rtnh_next(rtnh, &remaining);
288551ebd318SNicolas Dichtel 	}
28866b9ea5a6SRoopa Prabhu 
28876b9ea5a6SRoopa Prabhu 	err_nh = NULL;
28886b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
28896b9ea5a6SRoopa Prabhu 		err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc);
28906b9ea5a6SRoopa Prabhu 		/* nh->rt6_info is used or freed at this point, reset to NULL*/
28916b9ea5a6SRoopa Prabhu 		nh->rt6_info = NULL;
28926b9ea5a6SRoopa Prabhu 		if (err) {
28936b9ea5a6SRoopa Prabhu 			if (replace && nhn)
28946b9ea5a6SRoopa Prabhu 				ip6_print_replace_route_err(&rt6_nh_list);
28956b9ea5a6SRoopa Prabhu 			err_nh = nh;
28966b9ea5a6SRoopa Prabhu 			goto add_errout;
28976b9ea5a6SRoopa Prabhu 		}
28986b9ea5a6SRoopa Prabhu 
28991a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
290027596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
290127596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
290227596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
290327596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
290427596472SMichal Kubeček 		 * be added to it.
29051a72418bSNicolas Dichtel 		 */
290627596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
290727596472SMichal Kubeček 						     NLM_F_REPLACE);
29086b9ea5a6SRoopa Prabhu 		nhn++;
29096b9ea5a6SRoopa Prabhu 	}
29106b9ea5a6SRoopa Prabhu 
29116b9ea5a6SRoopa Prabhu 	goto cleanup;
29126b9ea5a6SRoopa Prabhu 
29136b9ea5a6SRoopa Prabhu add_errout:
29146b9ea5a6SRoopa Prabhu 	/* Delete routes that were already added */
29156b9ea5a6SRoopa Prabhu 	list_for_each_entry(nh, &rt6_nh_list, next) {
29166b9ea5a6SRoopa Prabhu 		if (err_nh == nh)
29176b9ea5a6SRoopa Prabhu 			break;
29186b9ea5a6SRoopa Prabhu 		ip6_route_del(&nh->r_cfg);
29196b9ea5a6SRoopa Prabhu 	}
29206b9ea5a6SRoopa Prabhu 
29216b9ea5a6SRoopa Prabhu cleanup:
29226b9ea5a6SRoopa Prabhu 	list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
29236b9ea5a6SRoopa Prabhu 		if (nh->rt6_info)
29246b9ea5a6SRoopa Prabhu 			dst_free(&nh->rt6_info->dst);
29256b9ea5a6SRoopa Prabhu 		kfree(nh->mxc.mx);
29266b9ea5a6SRoopa Prabhu 		list_del(&nh->next);
29276b9ea5a6SRoopa Prabhu 		kfree(nh);
29286b9ea5a6SRoopa Prabhu 	}
29296b9ea5a6SRoopa Prabhu 
29306b9ea5a6SRoopa Prabhu 	return err;
29316b9ea5a6SRoopa Prabhu }
29326b9ea5a6SRoopa Prabhu 
29336b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg)
29346b9ea5a6SRoopa Prabhu {
29356b9ea5a6SRoopa Prabhu 	struct fib6_config r_cfg;
29366b9ea5a6SRoopa Prabhu 	struct rtnexthop *rtnh;
29376b9ea5a6SRoopa Prabhu 	int remaining;
29386b9ea5a6SRoopa Prabhu 	int attrlen;
29396b9ea5a6SRoopa Prabhu 	int err = 1, last_err = 0;
29406b9ea5a6SRoopa Prabhu 
29416b9ea5a6SRoopa Prabhu 	remaining = cfg->fc_mp_len;
29426b9ea5a6SRoopa Prabhu 	rtnh = (struct rtnexthop *)cfg->fc_mp;
29436b9ea5a6SRoopa Prabhu 
29446b9ea5a6SRoopa Prabhu 	/* Parse a Multipath Entry */
29456b9ea5a6SRoopa Prabhu 	while (rtnh_ok(rtnh, remaining)) {
29466b9ea5a6SRoopa Prabhu 		memcpy(&r_cfg, cfg, sizeof(*cfg));
29476b9ea5a6SRoopa Prabhu 		if (rtnh->rtnh_ifindex)
29486b9ea5a6SRoopa Prabhu 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
29496b9ea5a6SRoopa Prabhu 
29506b9ea5a6SRoopa Prabhu 		attrlen = rtnh_attrlen(rtnh);
29516b9ea5a6SRoopa Prabhu 		if (attrlen > 0) {
29526b9ea5a6SRoopa Prabhu 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
29536b9ea5a6SRoopa Prabhu 
29546b9ea5a6SRoopa Prabhu 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
29556b9ea5a6SRoopa Prabhu 			if (nla) {
29566b9ea5a6SRoopa Prabhu 				nla_memcpy(&r_cfg.fc_gateway, nla, 16);
29576b9ea5a6SRoopa Prabhu 				r_cfg.fc_flags |= RTF_GATEWAY;
29586b9ea5a6SRoopa Prabhu 			}
29596b9ea5a6SRoopa Prabhu 		}
29606b9ea5a6SRoopa Prabhu 		err = ip6_route_del(&r_cfg);
29616b9ea5a6SRoopa Prabhu 		if (err)
29626b9ea5a6SRoopa Prabhu 			last_err = err;
29636b9ea5a6SRoopa Prabhu 
296451ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
296551ebd318SNicolas Dichtel 	}
296651ebd318SNicolas Dichtel 
296751ebd318SNicolas Dichtel 	return last_err;
296851ebd318SNicolas Dichtel }
296951ebd318SNicolas Dichtel 
2970661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
29711da177e4SLinus Torvalds {
297286872cb5SThomas Graf 	struct fib6_config cfg;
297386872cb5SThomas Graf 	int err;
29741da177e4SLinus Torvalds 
297586872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
297686872cb5SThomas Graf 	if (err < 0)
297786872cb5SThomas Graf 		return err;
297886872cb5SThomas Graf 
297951ebd318SNicolas Dichtel 	if (cfg.fc_mp)
29806b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_del(&cfg);
298151ebd318SNicolas Dichtel 	else
298286872cb5SThomas Graf 		return ip6_route_del(&cfg);
29831da177e4SLinus Torvalds }
29841da177e4SLinus Torvalds 
2985661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
29861da177e4SLinus Torvalds {
298786872cb5SThomas Graf 	struct fib6_config cfg;
298886872cb5SThomas Graf 	int err;
29891da177e4SLinus Torvalds 
299086872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
299186872cb5SThomas Graf 	if (err < 0)
299286872cb5SThomas Graf 		return err;
299386872cb5SThomas Graf 
299451ebd318SNicolas Dichtel 	if (cfg.fc_mp)
29956b9ea5a6SRoopa Prabhu 		return ip6_route_multipath_add(&cfg);
299651ebd318SNicolas Dichtel 	else
299786872cb5SThomas Graf 		return ip6_route_add(&cfg);
29981da177e4SLinus Torvalds }
29991da177e4SLinus Torvalds 
300019e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
3001339bf98fSThomas Graf {
3002339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
3003339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
3004339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
3005339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
3006339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
3007339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
3008339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
3009339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
3010339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
30116a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
3012ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
3013c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
301419e42e45SRoopa Prabhu 	       + nla_total_size(1) /* RTA_PREF */
301561adedf3SJiri Benc 	       + lwtunnel_get_encap_size(rt->dst.lwtstate);
3016339bf98fSThomas Graf }
3017339bf98fSThomas Graf 
3018191cd582SBrian Haley static int rt6_fill_node(struct net *net,
3019191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
30200d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
302115e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
30227bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
30231da177e4SLinus Torvalds {
30244b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
30251da177e4SLinus Torvalds 	struct rtmsg *rtm;
30261da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
3027e3703b3dSThomas Graf 	long expires;
30289e762a4aSPatrick McHardy 	u32 table;
30291da177e4SLinus Torvalds 
30301da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
30311da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
30321da177e4SLinus Torvalds 			/* success since this is not a prefix route */
30331da177e4SLinus Torvalds 			return 1;
30341da177e4SLinus Torvalds 		}
30351da177e4SLinus Torvalds 	}
30361da177e4SLinus Torvalds 
303715e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
303838308473SDavid S. Miller 	if (!nlh)
303926932566SPatrick McHardy 		return -EMSGSIZE;
30402d7202bfSThomas Graf 
30412d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
30421da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
30431da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
30441da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
30451da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
3046c71099acSThomas Graf 	if (rt->rt6i_table)
30479e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
3048c71099acSThomas Graf 	else
30499e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
30509e762a4aSPatrick McHardy 	rtm->rtm_table = table;
3051c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
3052c78679e8SDavid S. Miller 		goto nla_put_failure;
3053ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
3054ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
3055ef2c7d7bSNicolas Dichtel 		case -EINVAL:
3056ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
3057ef2c7d7bSNicolas Dichtel 			break;
3058ef2c7d7bSNicolas Dichtel 		case -EACCES:
3059ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
3060ef2c7d7bSNicolas Dichtel 			break;
3061b4949ab2SNicolas Dichtel 		case -EAGAIN:
3062b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
3063b4949ab2SNicolas Dichtel 			break;
3064ef2c7d7bSNicolas Dichtel 		default:
30651da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
3066ef2c7d7bSNicolas Dichtel 			break;
3067ef2c7d7bSNicolas Dichtel 		}
3068ef2c7d7bSNicolas Dichtel 	}
3069ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
3070ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
3071d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
30721da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
30731da177e4SLinus Torvalds 	else
30741da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
30751da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
307635103d11SAndy Gospodarek 	if (!netif_carrier_ok(rt->dst.dev)) {
3077cea45e20SAndy Gospodarek 		rtm->rtm_flags |= RTNH_F_LINKDOWN;
307835103d11SAndy Gospodarek 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
307935103d11SAndy Gospodarek 			rtm->rtm_flags |= RTNH_F_DEAD;
308035103d11SAndy Gospodarek 	}
30811da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
30821da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
30831da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
30841da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
3085f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
3086f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
30871da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
3088f0396f60SDenis Ovsienko 		else
3089f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
3090f0396f60SDenis Ovsienko 	}
30911da177e4SLinus Torvalds 
30921da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
30931da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
30941da177e4SLinus Torvalds 
30951da177e4SLinus Torvalds 	if (dst) {
3096930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
3097c78679e8SDavid S. Miller 			goto nla_put_failure;
30981da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
30991da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
3100930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
3101c78679e8SDavid S. Miller 			goto nla_put_failure;
31021da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
31031da177e4SLinus Torvalds 	if (src) {
3104930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
3105c78679e8SDavid S. Miller 			goto nla_put_failure;
31061da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
3107c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
3108930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
3109c78679e8SDavid S. Miller 		goto nla_put_failure;
31101da177e4SLinus Torvalds #endif
31117bc570c8SYOSHIFUJI Hideaki 	if (iif) {
31127bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
31137bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
31148229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
31157bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
31167bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
31177bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
31187bc570c8SYOSHIFUJI Hideaki 						return 0;
31197bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
31207bc570c8SYOSHIFUJI Hideaki 				} else {
31217bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
31227bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
31237bc570c8SYOSHIFUJI Hideaki 				}
31247bc570c8SYOSHIFUJI Hideaki 			}
31257bc570c8SYOSHIFUJI Hideaki 		} else
31267bc570c8SYOSHIFUJI Hideaki #endif
3127c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
3128c78679e8SDavid S. Miller 				goto nla_put_failure;
31297bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
31301da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
3131c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
3132930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3133c78679e8SDavid S. Miller 			goto nla_put_failure;
3134c3968a85SDaniel Walter 	}
3135c3968a85SDaniel Walter 
3136c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
3137c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
31384e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
3139930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
3140c78679e8SDavid S. Miller 			goto nla_put_failure;
31411da177e4SLinus Torvalds 	}
31422d7202bfSThomas Graf 
31434b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
31444b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
31454b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
31464b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
31472d7202bfSThomas Graf 		goto nla_put_failure;
31482d7202bfSThomas Graf 
3149dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
3150930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
315194f826b8SEric Dumazet 			goto nla_put_failure;
315294f826b8SEric Dumazet 	}
31532d7202bfSThomas Graf 
3154c78679e8SDavid S. Miller 	if (rt->dst.dev &&
3155c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3156c78679e8SDavid S. Miller 		goto nla_put_failure;
3157c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
3158c78679e8SDavid S. Miller 		goto nla_put_failure;
31598253947eSLi Wei 
31608253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
316169cdf8f9SYOSHIFUJI Hideaki 
316287a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
3163e3703b3dSThomas Graf 		goto nla_put_failure;
31641da177e4SLinus Torvalds 
3165c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
3166c78ba6d6SLubomir Rintel 		goto nla_put_failure;
3167c78ba6d6SLubomir Rintel 
316861adedf3SJiri Benc 	lwtunnel_fill_encap(skb, rt->dst.lwtstate);
316919e42e45SRoopa Prabhu 
3170053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
3171053c095aSJohannes Berg 	return 0;
31722d7202bfSThomas Graf 
31732d7202bfSThomas Graf nla_put_failure:
317426932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
317526932566SPatrick McHardy 	return -EMSGSIZE;
31761da177e4SLinus Torvalds }
31771da177e4SLinus Torvalds 
31781b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
31791da177e4SLinus Torvalds {
31801da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
31811da177e4SLinus Torvalds 	int prefix;
31821da177e4SLinus Torvalds 
31832d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
31842d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
31851da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
31861da177e4SLinus Torvalds 	} else
31871da177e4SLinus Torvalds 		prefix = 0;
31881da177e4SLinus Torvalds 
3189191cd582SBrian Haley 	return rt6_fill_node(arg->net,
3190191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
319115e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
31927bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
31931da177e4SLinus Torvalds }
31941da177e4SLinus Torvalds 
3195661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
31961da177e4SLinus Torvalds {
31973b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
3198ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
31991da177e4SLinus Torvalds 	struct rt6_info *rt;
3200ab364a6fSThomas Graf 	struct sk_buff *skb;
3201ab364a6fSThomas Graf 	struct rtmsg *rtm;
32024c9483b2SDavid S. Miller 	struct flowi6 fl6;
320372331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
3204ab364a6fSThomas Graf 
3205ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
3206ab364a6fSThomas Graf 	if (err < 0)
3207ab364a6fSThomas Graf 		goto errout;
3208ab364a6fSThomas Graf 
3209ab364a6fSThomas Graf 	err = -EINVAL;
32104c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
3211ab364a6fSThomas Graf 
3212ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
3213ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
3214ab364a6fSThomas Graf 			goto errout;
3215ab364a6fSThomas Graf 
32164e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
3217ab364a6fSThomas Graf 	}
3218ab364a6fSThomas Graf 
3219ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
3220ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
3221ab364a6fSThomas Graf 			goto errout;
3222ab364a6fSThomas Graf 
32234e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
3224ab364a6fSThomas Graf 	}
3225ab364a6fSThomas Graf 
3226ab364a6fSThomas Graf 	if (tb[RTA_IIF])
3227ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
3228ab364a6fSThomas Graf 
3229ab364a6fSThomas Graf 	if (tb[RTA_OIF])
323072331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
3231ab364a6fSThomas Graf 
32322e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
32332e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
32342e47b291SLorenzo Colitti 
3235ab364a6fSThomas Graf 	if (iif) {
3236ab364a6fSThomas Graf 		struct net_device *dev;
323772331bc0SShmulik Ladkani 		int flags = 0;
323872331bc0SShmulik Ladkani 
32395578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
3240ab364a6fSThomas Graf 		if (!dev) {
3241ab364a6fSThomas Graf 			err = -ENODEV;
3242ab364a6fSThomas Graf 			goto errout;
3243ab364a6fSThomas Graf 		}
324472331bc0SShmulik Ladkani 
324572331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
324672331bc0SShmulik Ladkani 
324772331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
324872331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
324972331bc0SShmulik Ladkani 
325072331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
325172331bc0SShmulik Ladkani 							       flags);
325272331bc0SShmulik Ladkani 	} else {
325372331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
325472331bc0SShmulik Ladkani 
325572331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
3256ab364a6fSThomas Graf 	}
32571da177e4SLinus Torvalds 
32581da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
325938308473SDavid S. Miller 	if (!skb) {
326094e187c0SAmerigo Wang 		ip6_rt_put(rt);
3261ab364a6fSThomas Graf 		err = -ENOBUFS;
3262ab364a6fSThomas Graf 		goto errout;
3263ab364a6fSThomas Graf 	}
32641da177e4SLinus Torvalds 
32651da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
32661da177e4SLinus Torvalds 	   through good chunk of routing engine.
32671da177e4SLinus Torvalds 	 */
3268459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
32691da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
32701da177e4SLinus Torvalds 
3271d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
32721da177e4SLinus Torvalds 
32734c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
327415e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
32757bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
32761da177e4SLinus Torvalds 	if (err < 0) {
3277ab364a6fSThomas Graf 		kfree_skb(skb);
3278ab364a6fSThomas Graf 		goto errout;
32791da177e4SLinus Torvalds 	}
32801da177e4SLinus Torvalds 
328115e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
3282ab364a6fSThomas Graf errout:
32831da177e4SLinus Torvalds 	return err;
32841da177e4SLinus Torvalds }
32851da177e4SLinus Torvalds 
328637a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
328737a1d361SRoopa Prabhu 		     unsigned int nlm_flags)
32881da177e4SLinus Torvalds {
32891da177e4SLinus Torvalds 	struct sk_buff *skb;
32905578689aSDaniel Lezcano 	struct net *net = info->nl_net;
3291528c4cebSDenis V. Lunev 	u32 seq;
3292528c4cebSDenis V. Lunev 	int err;
32930d51aa80SJamal Hadi Salim 
3294528c4cebSDenis V. Lunev 	err = -ENOBUFS;
329538308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
329686872cb5SThomas Graf 
329719e42e45SRoopa Prabhu 	skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
329838308473SDavid S. Miller 	if (!skb)
329921713ebcSThomas Graf 		goto errout;
33001da177e4SLinus Torvalds 
3301191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
330237a1d361SRoopa Prabhu 				event, info->portid, seq, 0, 0, nlm_flags);
330326932566SPatrick McHardy 	if (err < 0) {
330426932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
330526932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
330626932566SPatrick McHardy 		kfree_skb(skb);
330726932566SPatrick McHardy 		goto errout;
330826932566SPatrick McHardy 	}
330915e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
33105578689aSDaniel Lezcano 		    info->nlh, gfp_any());
33111ce85fe4SPablo Neira Ayuso 	return;
331221713ebcSThomas Graf errout:
331321713ebcSThomas Graf 	if (err < 0)
33145578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
33151da177e4SLinus Torvalds }
33161da177e4SLinus Torvalds 
33178ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3318351638e7SJiri Pirko 				unsigned long event, void *ptr)
33198ed67789SDaniel Lezcano {
3320351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3321c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
33228ed67789SDaniel Lezcano 
33238ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3324d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
33258ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
33268ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3327d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
33288ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3329d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
33308ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
33318ed67789SDaniel Lezcano #endif
33328ed67789SDaniel Lezcano 	}
33338ed67789SDaniel Lezcano 
33348ed67789SDaniel Lezcano 	return NOTIFY_OK;
33358ed67789SDaniel Lezcano }
33368ed67789SDaniel Lezcano 
33371da177e4SLinus Torvalds /*
33381da177e4SLinus Torvalds  *	/proc
33391da177e4SLinus Torvalds  */
33401da177e4SLinus Torvalds 
33411da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
33421da177e4SLinus Torvalds 
334333120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
334433120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
334533120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
334633120b30SAlexey Dobriyan 	.read		= seq_read,
334733120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
33488d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
334933120b30SAlexey Dobriyan };
335033120b30SAlexey Dobriyan 
33511da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
33521da177e4SLinus Torvalds {
335369ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
33541da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
335569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
335669ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
335769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
335869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
335969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3360fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
336169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
33621da177e4SLinus Torvalds 
33631da177e4SLinus Torvalds 	return 0;
33641da177e4SLinus Torvalds }
33651da177e4SLinus Torvalds 
33661da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
33671da177e4SLinus Torvalds {
3368de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
336969ddb805SDaniel Lezcano }
337069ddb805SDaniel Lezcano 
33719a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
33721da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
33731da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
33741da177e4SLinus Torvalds 	.read	 = seq_read,
33751da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3376b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
33771da177e4SLinus Torvalds };
33781da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
33791da177e4SLinus Torvalds 
33801da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
33811da177e4SLinus Torvalds 
33821da177e4SLinus Torvalds static
3383fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
33841da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
33851da177e4SLinus Torvalds {
3386c486da34SLucian Adrian Grijincu 	struct net *net;
3387c486da34SLucian Adrian Grijincu 	int delay;
3388c486da34SLucian Adrian Grijincu 	if (!write)
3389c486da34SLucian Adrian Grijincu 		return -EINVAL;
3390c486da34SLucian Adrian Grijincu 
3391c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3392c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
33938d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
33942ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
33951da177e4SLinus Torvalds 	return 0;
33961da177e4SLinus Torvalds }
33971da177e4SLinus Torvalds 
3398fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
33991da177e4SLinus Torvalds 	{
34001da177e4SLinus Torvalds 		.procname	=	"flush",
34014990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
34021da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
340389c8b3a1SDave Jones 		.mode		=	0200,
34046d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
34051da177e4SLinus Torvalds 	},
34061da177e4SLinus Torvalds 	{
34071da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
34089a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
34091da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34101da177e4SLinus Torvalds 		.mode		=	0644,
34116d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34121da177e4SLinus Torvalds 	},
34131da177e4SLinus Torvalds 	{
34141da177e4SLinus Torvalds 		.procname	=	"max_size",
34154990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
34161da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34171da177e4SLinus Torvalds 		.mode		=	0644,
34186d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
34191da177e4SLinus Torvalds 	},
34201da177e4SLinus Torvalds 	{
34211da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
34224990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34231da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34241da177e4SLinus Torvalds 		.mode		=	0644,
34256d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34261da177e4SLinus Torvalds 	},
34271da177e4SLinus Torvalds 	{
34281da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
34294990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
34301da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34311da177e4SLinus Torvalds 		.mode		=	0644,
34326d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34331da177e4SLinus Torvalds 	},
34341da177e4SLinus Torvalds 	{
34351da177e4SLinus Torvalds 		.procname	=	"gc_interval",
34364990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
34371da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34381da177e4SLinus Torvalds 		.mode		=	0644,
34396d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34401da177e4SLinus Torvalds 	},
34411da177e4SLinus Torvalds 	{
34421da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
34434990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
34441da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34451da177e4SLinus Torvalds 		.mode		=	0644,
3446f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34471da177e4SLinus Torvalds 	},
34481da177e4SLinus Torvalds 	{
34491da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
34504990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
34511da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34521da177e4SLinus Torvalds 		.mode		=	0644,
34536d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
34541da177e4SLinus Torvalds 	},
34551da177e4SLinus Torvalds 	{
34561da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
34574990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
34581da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34591da177e4SLinus Torvalds 		.mode		=	0644,
3460f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
34611da177e4SLinus Torvalds 	},
34621da177e4SLinus Torvalds 	{
34631da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
34644990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
34651da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
34661da177e4SLinus Torvalds 		.mode		=	0644,
34676d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
34681da177e4SLinus Torvalds 	},
3469f8572d8fSEric W. Biederman 	{ }
34701da177e4SLinus Torvalds };
34711da177e4SLinus Torvalds 
34722c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3473760f2d01SDaniel Lezcano {
3474760f2d01SDaniel Lezcano 	struct ctl_table *table;
3475760f2d01SDaniel Lezcano 
3476760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3477760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3478760f2d01SDaniel Lezcano 			GFP_KERNEL);
34795ee09105SYOSHIFUJI Hideaki 
34805ee09105SYOSHIFUJI Hideaki 	if (table) {
34815ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3482c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
348386393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
34845ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
34855ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
34865ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
34875ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
34885ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
34895ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
34905ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
34919c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3492464dc801SEric W. Biederman 
3493464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3494464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3495464dc801SEric W. Biederman 			table[0].procname = NULL;
34965ee09105SYOSHIFUJI Hideaki 	}
34975ee09105SYOSHIFUJI Hideaki 
3498760f2d01SDaniel Lezcano 	return table;
3499760f2d01SDaniel Lezcano }
35001da177e4SLinus Torvalds #endif
35011da177e4SLinus Torvalds 
35022c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3503cdb18761SDaniel Lezcano {
3504633d424bSPavel Emelyanov 	int ret = -ENOMEM;
35058ed67789SDaniel Lezcano 
350686393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
350786393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3508f2fc6a54SBenjamin Thery 
3509fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3510fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3511fc66f95cSEric Dumazet 
35128ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
35138ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
35148ed67789SDaniel Lezcano 					   GFP_KERNEL);
35158ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3516fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3517d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
35188ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3519d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
352062fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
352162fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35228ed67789SDaniel Lezcano 
35238ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
35248ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
35258ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
35268ed67789SDaniel Lezcano 					       GFP_KERNEL);
352768fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
352868fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3529d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
35308ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3531d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
353262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
353362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35348ed67789SDaniel Lezcano 
35358ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
35368ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
35378ed67789SDaniel Lezcano 					       GFP_KERNEL);
353868fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
353968fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3540d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
35418ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3542d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
354362fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
354462fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
35458ed67789SDaniel Lezcano #endif
35468ed67789SDaniel Lezcano 
3547b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3548b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3549b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3550b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3551b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3552b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3553b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3554b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3555b339a47cSPeter Zijlstra 
35566891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
35576891a346SBenjamin Thery 
35588ed67789SDaniel Lezcano 	ret = 0;
35598ed67789SDaniel Lezcano out:
35608ed67789SDaniel Lezcano 	return ret;
3561f2fc6a54SBenjamin Thery 
356268fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
356368fffc67SPeter Zijlstra out_ip6_prohibit_entry:
356468fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
356568fffc67SPeter Zijlstra out_ip6_null_entry:
356668fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
356768fffc67SPeter Zijlstra #endif
3568fc66f95cSEric Dumazet out_ip6_dst_entries:
3569fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3570f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3571f2fc6a54SBenjamin Thery 	goto out;
3572cdb18761SDaniel Lezcano }
3573cdb18761SDaniel Lezcano 
35742c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3575cdb18761SDaniel Lezcano {
35768ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
35778ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
35788ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
35798ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
35808ed67789SDaniel Lezcano #endif
358141bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3582cdb18761SDaniel Lezcano }
3583cdb18761SDaniel Lezcano 
3584d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3585d189634eSThomas Graf {
3586d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3587d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3588d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3589d189634eSThomas Graf #endif
3590d189634eSThomas Graf 	return 0;
3591d189634eSThomas Graf }
3592d189634eSThomas Graf 
3593d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3594d189634eSThomas Graf {
3595d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3596ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3597ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3598d189634eSThomas Graf #endif
3599d189634eSThomas Graf }
3600d189634eSThomas Graf 
3601cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3602cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3603cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3604cdb18761SDaniel Lezcano };
3605cdb18761SDaniel Lezcano 
3606c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3607c3426b47SDavid S. Miller {
3608c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3609c3426b47SDavid S. Miller 
3610c3426b47SDavid S. Miller 	if (!bp)
3611c3426b47SDavid S. Miller 		return -ENOMEM;
3612c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3613c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3614c3426b47SDavid S. Miller 	return 0;
3615c3426b47SDavid S. Miller }
3616c3426b47SDavid S. Miller 
3617c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3618c3426b47SDavid S. Miller {
3619c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3620c3426b47SDavid S. Miller 
3621c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
362256a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3623c3426b47SDavid S. Miller 	kfree(bp);
3624c3426b47SDavid S. Miller }
3625c3426b47SDavid S. Miller 
36262b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3627c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3628c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3629c3426b47SDavid S. Miller };
3630c3426b47SDavid S. Miller 
3631d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3632d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3633d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3634d189634eSThomas Graf };
3635d189634eSThomas Graf 
36368ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
36378ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
36388ed67789SDaniel Lezcano 	.priority = 0,
36398ed67789SDaniel Lezcano };
36408ed67789SDaniel Lezcano 
3641433d49c3SDaniel Lezcano int __init ip6_route_init(void)
36421da177e4SLinus Torvalds {
3643433d49c3SDaniel Lezcano 	int ret;
36448d0b94afSMartin KaFai Lau 	int cpu;
3645433d49c3SDaniel Lezcano 
36469a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
36479a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
36489a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
36499a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
36509a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3651c19a28e1SFernando Carrijo 		goto out;
365214e50e57SDavid S. Miller 
3653fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
36548ed67789SDaniel Lezcano 	if (ret)
3655bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3656bdb3289fSDaniel Lezcano 
3657c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3658c3426b47SDavid S. Miller 	if (ret)
3659e8803b6cSDavid S. Miller 		goto out_dst_entries;
36602a0c451aSThomas Graf 
36617e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
36627e52b33bSDavid S. Miller 	if (ret)
36637e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3664c3426b47SDavid S. Miller 
36655dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
36665dc121e9SArnaud Ebalard 
36678ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
36688ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
36698ed67789SDaniel Lezcano 	 * manually for init_net */
3670d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
36718ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3672bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3673d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
36748ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3675d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
36768ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3677bdb3289fSDaniel Lezcano   #endif
3678e8803b6cSDavid S. Miller 	ret = fib6_init();
3679433d49c3SDaniel Lezcano 	if (ret)
36808ed67789SDaniel Lezcano 		goto out_register_subsys;
3681433d49c3SDaniel Lezcano 
3682433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3683433d49c3SDaniel Lezcano 	if (ret)
3684e8803b6cSDavid S. Miller 		goto out_fib6_init;
3685c35b7e72SDaniel Lezcano 
3686433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3687433d49c3SDaniel Lezcano 	if (ret)
3688433d49c3SDaniel Lezcano 		goto xfrm6_init;
36897e5449c2SDaniel Lezcano 
3690d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3691d189634eSThomas Graf 	if (ret)
3692d189634eSThomas Graf 		goto fib6_rules_init;
3693d189634eSThomas Graf 
3694433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3695c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3696c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3697c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3698d189634eSThomas Graf 		goto out_register_late_subsys;
3699433d49c3SDaniel Lezcano 
37008ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3701cdb18761SDaniel Lezcano 	if (ret)
3702d189634eSThomas Graf 		goto out_register_late_subsys;
37038ed67789SDaniel Lezcano 
37048d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
37058d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
37068d0b94afSMartin KaFai Lau 
37078d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
37088d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
37098d0b94afSMartin KaFai Lau 	}
37108d0b94afSMartin KaFai Lau 
3711433d49c3SDaniel Lezcano out:
3712433d49c3SDaniel Lezcano 	return ret;
3713433d49c3SDaniel Lezcano 
3714d189634eSThomas Graf out_register_late_subsys:
3715d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3716433d49c3SDaniel Lezcano fib6_rules_init:
3717433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3718433d49c3SDaniel Lezcano xfrm6_init:
3719433d49c3SDaniel Lezcano 	xfrm6_fini();
37202a0c451aSThomas Graf out_fib6_init:
37212a0c451aSThomas Graf 	fib6_gc_cleanup();
37228ed67789SDaniel Lezcano out_register_subsys:
37238ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
37247e52b33bSDavid S. Miller out_register_inetpeer:
37257e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3726fc66f95cSEric Dumazet out_dst_entries:
3727fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3728433d49c3SDaniel Lezcano out_kmem_cache:
3729f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3730433d49c3SDaniel Lezcano 	goto out;
37311da177e4SLinus Torvalds }
37321da177e4SLinus Torvalds 
37331da177e4SLinus Torvalds void ip6_route_cleanup(void)
37341da177e4SLinus Torvalds {
37358ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3736d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3737101367c2SThomas Graf 	fib6_rules_cleanup();
37381da177e4SLinus Torvalds 	xfrm6_fini();
37391da177e4SLinus Torvalds 	fib6_gc_cleanup();
3740c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
37418ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
374241bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3743f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
37441da177e4SLinus Torvalds }
3745