xref: /openbmc/linux/net/ipv6/route.c (revision 8d0b94afdca84598912347e61defa846a0988d04)
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>
571da177e4SLinus Torvalds #include <net/xfrm.h>
588d71740cSTom Tucker #include <net/netevent.h>
5921713ebcSThomas Graf #include <net/netlink.h>
6051ebd318SNicolas Dichtel #include <net/nexthop.h>
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds #include <asm/uaccess.h>
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
651da177e4SLinus Torvalds #include <linux/sysctl.h>
661da177e4SLinus Torvalds #endif
671da177e4SLinus Torvalds 
68afc154e9SHannes Frederic Sowa enum rt6_nud_state {
697e980569SJiri Benc 	RT6_NUD_FAIL_HARD = -3,
707e980569SJiri Benc 	RT6_NUD_FAIL_PROBE = -2,
717e980569SJiri Benc 	RT6_NUD_FAIL_DO_RR = -1,
72afc154e9SHannes Frederic Sowa 	RT6_NUD_SUCCEED = 1
73afc154e9SHannes Frederic Sowa };
74afc154e9SHannes Frederic Sowa 
751716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
7621efcfa0SEric Dumazet 				    const struct in6_addr *dest);
771da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
780dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
79ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
801da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
811da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
821da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
831da177e4SLinus Torvalds 				       struct net_device *dev, int how);
84569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
87aad88724SEric Dumazet static int		ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb);
887150aedeSKamala R static int		ip6_pkt_prohibit(struct sk_buff *skb);
89aad88724SEric Dumazet static int		ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb);
901da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
916700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
926700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
936700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
946700c270SDavid S. Miller 					struct sk_buff *skb);
954b32b5adSMartin KaFai Lau static void		rt6_dst_from_metrics_check(struct rt6_info *rt);
9652bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
971da177e4SLinus Torvalds 
9870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
99efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
100b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
101b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
10295c96174SEric Dumazet 					   unsigned int pref);
103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
104b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
105b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
10670ceb4f5SYOSHIFUJI Hideaki #endif
10770ceb4f5SYOSHIFUJI Hideaki 
108*8d0b94afSMartin KaFai Lau struct uncached_list {
109*8d0b94afSMartin KaFai Lau 	spinlock_t		lock;
110*8d0b94afSMartin KaFai Lau 	struct list_head	head;
111*8d0b94afSMartin KaFai Lau };
112*8d0b94afSMartin KaFai Lau 
113*8d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
114*8d0b94afSMartin KaFai Lau 
115*8d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt)
116*8d0b94afSMartin KaFai Lau {
117*8d0b94afSMartin KaFai Lau 	struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
118*8d0b94afSMartin KaFai Lau 
119*8d0b94afSMartin KaFai Lau 	rt->dst.flags |= DST_NOCACHE;
120*8d0b94afSMartin KaFai Lau 	rt->rt6i_uncached_list = ul;
121*8d0b94afSMartin KaFai Lau 
122*8d0b94afSMartin KaFai Lau 	spin_lock_bh(&ul->lock);
123*8d0b94afSMartin KaFai Lau 	list_add_tail(&rt->rt6i_uncached, &ul->head);
124*8d0b94afSMartin KaFai Lau 	spin_unlock_bh(&ul->lock);
125*8d0b94afSMartin KaFai Lau }
126*8d0b94afSMartin KaFai Lau 
127*8d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt)
128*8d0b94afSMartin KaFai Lau {
129*8d0b94afSMartin KaFai Lau 	if (!list_empty(&rt->rt6i_uncached)) {
130*8d0b94afSMartin KaFai Lau 		struct uncached_list *ul = rt->rt6i_uncached_list;
131*8d0b94afSMartin KaFai Lau 
132*8d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
133*8d0b94afSMartin KaFai Lau 		list_del(&rt->rt6i_uncached);
134*8d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
135*8d0b94afSMartin KaFai Lau 	}
136*8d0b94afSMartin KaFai Lau }
137*8d0b94afSMartin KaFai Lau 
138*8d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
139*8d0b94afSMartin KaFai Lau {
140*8d0b94afSMartin KaFai Lau 	struct net_device *loopback_dev = net->loopback_dev;
141*8d0b94afSMartin KaFai Lau 	int cpu;
142*8d0b94afSMartin KaFai Lau 
143*8d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
144*8d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
145*8d0b94afSMartin KaFai Lau 		struct rt6_info *rt;
146*8d0b94afSMartin KaFai Lau 
147*8d0b94afSMartin KaFai Lau 		spin_lock_bh(&ul->lock);
148*8d0b94afSMartin KaFai Lau 		list_for_each_entry(rt, &ul->head, rt6i_uncached) {
149*8d0b94afSMartin KaFai Lau 			struct inet6_dev *rt_idev = rt->rt6i_idev;
150*8d0b94afSMartin KaFai Lau 			struct net_device *rt_dev = rt->dst.dev;
151*8d0b94afSMartin KaFai Lau 
152*8d0b94afSMartin KaFai Lau 			if (rt_idev && (rt_idev->dev == dev || !dev) &&
153*8d0b94afSMartin KaFai Lau 			    rt_idev->dev != loopback_dev) {
154*8d0b94afSMartin KaFai Lau 				rt->rt6i_idev = in6_dev_get(loopback_dev);
155*8d0b94afSMartin KaFai Lau 				in6_dev_put(rt_idev);
156*8d0b94afSMartin KaFai Lau 			}
157*8d0b94afSMartin KaFai Lau 
158*8d0b94afSMartin KaFai Lau 			if (rt_dev && (rt_dev == dev || !dev) &&
159*8d0b94afSMartin KaFai Lau 			    rt_dev != loopback_dev) {
160*8d0b94afSMartin KaFai Lau 				rt->dst.dev = loopback_dev;
161*8d0b94afSMartin KaFai Lau 				dev_hold(rt->dst.dev);
162*8d0b94afSMartin KaFai Lau 				dev_put(rt_dev);
163*8d0b94afSMartin KaFai Lau 			}
164*8d0b94afSMartin KaFai Lau 		}
165*8d0b94afSMartin KaFai Lau 		spin_unlock_bh(&ul->lock);
166*8d0b94afSMartin KaFai Lau 	}
167*8d0b94afSMartin KaFai Lau }
168*8d0b94afSMartin KaFai Lau 
16906582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
17006582540SDavid S. Miller {
17106582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
17206582540SDavid S. Miller 
1734b32b5adSMartin KaFai Lau 	if (rt->rt6i_flags & RTF_CACHE)
1744b32b5adSMartin KaFai Lau 		return NULL;
1754b32b5adSMartin KaFai Lau 	else
1763b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
17706582540SDavid S. Miller }
17806582540SDavid S. Miller 
179f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
180f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
181f894cbf8SDavid S. Miller 					     const void *daddr)
18239232973SDavid S. Miller {
18339232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
18439232973SDavid S. Miller 
185a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
18639232973SDavid S. Miller 		return (const void *) p;
187f894cbf8SDavid S. Miller 	else if (skb)
188f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
18939232973SDavid S. Miller 	return daddr;
19039232973SDavid S. Miller }
19139232973SDavid S. Miller 
192f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
193f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
194f894cbf8SDavid S. Miller 					  const void *daddr)
195d3aaeb38SDavid S. Miller {
19639232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
19739232973SDavid S. Miller 	struct neighbour *n;
19839232973SDavid S. Miller 
199f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
2008e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
201f83c7790SDavid S. Miller 	if (n)
202f83c7790SDavid S. Miller 		return n;
203f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
204f83c7790SDavid S. Miller }
205f83c7790SDavid S. Miller 
2069a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
2071da177e4SLinus Torvalds 	.family			=	AF_INET6,
2081da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
2091da177e4SLinus Torvalds 	.gc_thresh		=	1024,
2101da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
2110dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
212ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
21306582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
2141da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
2151da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
2161da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
2171da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
2181da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
2196e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
2201ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
221d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
2221da177e4SLinus Torvalds };
2231da177e4SLinus Torvalds 
224ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
225ec831ea7SRoland Dreier {
226618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
227618f9bc7SSteffen Klassert 
228618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
229ec831ea7SRoland Dreier }
230ec831ea7SRoland Dreier 
2316700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2326700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
23314e50e57SDavid S. Miller {
23414e50e57SDavid S. Miller }
23514e50e57SDavid S. Miller 
2366700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2376700c270SDavid S. Miller 				      struct sk_buff *skb)
238b587ee3bSDavid S. Miller {
239b587ee3bSDavid S. Miller }
240b587ee3bSDavid S. Miller 
2410972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
2420972ddb2SHeld Bernhard 					 unsigned long old)
2430972ddb2SHeld Bernhard {
2440972ddb2SHeld Bernhard 	return NULL;
2450972ddb2SHeld Bernhard }
2460972ddb2SHeld Bernhard 
24714e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
24814e50e57SDavid S. Miller 	.family			=	AF_INET6,
24914e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
25014e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
251ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
252214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
25314e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
254b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2550972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
256d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
25714e50e57SDavid S. Miller };
25814e50e57SDavid S. Miller 
25962fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
26014edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
26162fa8a84SDavid S. Miller };
26262fa8a84SDavid S. Miller 
263fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2641da177e4SLinus Torvalds 	.dst = {
2651da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2661da177e4SLinus Torvalds 		.__use		= 1,
2672c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2681da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2691da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2701da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2711da177e4SLinus Torvalds 	},
2721da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2734f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2741da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2751da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2761da177e4SLinus Torvalds };
2771da177e4SLinus Torvalds 
278101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
279101367c2SThomas Graf 
280fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
281101367c2SThomas Graf 	.dst = {
282101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
283101367c2SThomas Graf 		.__use		= 1,
2842c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
285101367c2SThomas Graf 		.error		= -EACCES,
2869ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2879ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
288101367c2SThomas Graf 	},
289101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2904f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
291101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
292101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
293101367c2SThomas Graf };
294101367c2SThomas Graf 
295fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
296101367c2SThomas Graf 	.dst = {
297101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
298101367c2SThomas Graf 		.__use		= 1,
2992c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
300101367c2SThomas Graf 		.error		= -EINVAL,
301352e512cSHerbert Xu 		.input		= dst_discard,
302aad88724SEric Dumazet 		.output		= dst_discard_sk,
303101367c2SThomas Graf 	},
304101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
3054f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
306101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
307101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
308101367c2SThomas Graf };
309101367c2SThomas Graf 
310101367c2SThomas Graf #endif
311101367c2SThomas Graf 
3121da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
31397bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net,
314957c665fSDavid S. Miller 					     struct net_device *dev,
3158b96d22dSDavid S. Miller 					     int flags,
3168b96d22dSDavid S. Miller 					     struct fib6_table *table)
3171da177e4SLinus Torvalds {
31897bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
3196f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
320cf911662SDavid S. Miller 
32197bab73fSDavid S. Miller 	if (rt) {
3228104891bSSteffen Klassert 		struct dst_entry *dst = &rt->dst;
3238104891bSSteffen Klassert 
3248104891bSSteffen Klassert 		memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
32551ebd318SNicolas Dichtel 		INIT_LIST_HEAD(&rt->rt6i_siblings);
326*8d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&rt->rt6i_uncached);
32797bab73fSDavid S. Miller 	}
328cf911662SDavid S. Miller 	return rt;
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3321da177e4SLinus Torvalds {
3331da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
334ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
335*8d0b94afSMartin KaFai Lau 	struct inet6_dev *idev;
3361da177e4SLinus Torvalds 
3378e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
3388e2ec639SYan, Zheng 
339*8d0b94afSMartin KaFai Lau 	rt6_uncached_list_del(rt);
340*8d0b94afSMartin KaFai Lau 
341*8d0b94afSMartin KaFai Lau 	idev = rt->rt6i_idev;
34238308473SDavid S. Miller 	if (idev) {
3431da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3441da177e4SLinus Torvalds 		in6_dev_put(idev);
3451da177e4SLinus Torvalds 	}
3461716a961SGao feng 
347ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
348ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
349b3419363SDavid S. Miller }
350b3419363SDavid S. Miller 
3511da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3521da177e4SLinus Torvalds 			   int how)
3531da177e4SLinus Torvalds {
3541da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3551da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3565a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
357c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3581da177e4SLinus Torvalds 
35997cac082SDavid S. Miller 	if (dev != loopback_dev) {
36097cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3615a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3625a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
36338308473SDavid S. Miller 			if (loopback_idev) {
3641da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
3651da177e4SLinus Torvalds 				in6_dev_put(idev);
3661da177e4SLinus Torvalds 			}
3671da177e4SLinus Torvalds 		}
36897cac082SDavid S. Miller 	}
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds 
371a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
3721da177e4SLinus Torvalds {
3731716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
3741716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
375a50feda5SEric Dumazet 			return true;
3761716a961SGao feng 	} else if (rt->dst.from) {
3773fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
3781716a961SGao feng 	}
379a50feda5SEric Dumazet 	return false;
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds 
38251ebd318SNicolas Dichtel /* Multipath route selection:
38351ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
38451ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
38551ebd318SNicolas Dichtel  */
38651ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
38751ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
38851ebd318SNicolas Dichtel {
38951ebd318SNicolas Dichtel 	unsigned int val = fl6->flowi6_proto;
39051ebd318SNicolas Dichtel 
391c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->daddr);
392c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->saddr);
39351ebd318SNicolas Dichtel 
39451ebd318SNicolas Dichtel 	/* Work only if this not encapsulated */
39551ebd318SNicolas Dichtel 	switch (fl6->flowi6_proto) {
39651ebd318SNicolas Dichtel 	case IPPROTO_UDP:
39751ebd318SNicolas Dichtel 	case IPPROTO_TCP:
39851ebd318SNicolas Dichtel 	case IPPROTO_SCTP:
399b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_sport;
400b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_dport;
40151ebd318SNicolas Dichtel 		break;
40251ebd318SNicolas Dichtel 
40351ebd318SNicolas Dichtel 	case IPPROTO_ICMPV6:
404b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_type;
405b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_code;
40651ebd318SNicolas Dichtel 		break;
40751ebd318SNicolas Dichtel 	}
40851ebd318SNicolas Dichtel 	/* RFC6438 recommands to use flowlabel */
409b3ce5ae1SNicolas Dichtel 	val ^= (__force u32)fl6->flowlabel;
41051ebd318SNicolas Dichtel 
41151ebd318SNicolas Dichtel 	/* Perhaps, we need to tune, this function? */
41251ebd318SNicolas Dichtel 	val = val ^ (val >> 7) ^ (val >> 12);
41351ebd318SNicolas Dichtel 	return val % candidate_count;
41451ebd318SNicolas Dichtel }
41551ebd318SNicolas Dichtel 
41651ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
41752bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
41852bd4c0cSNicolas Dichtel 					     int strict)
41951ebd318SNicolas Dichtel {
42051ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
42151ebd318SNicolas Dichtel 	int route_choosen;
42251ebd318SNicolas Dichtel 
42351ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
42451ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
42551ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
42651ebd318SNicolas Dichtel 	 */
42751ebd318SNicolas Dichtel 	if (route_choosen)
42851ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
42951ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
43051ebd318SNicolas Dichtel 			route_choosen--;
43151ebd318SNicolas Dichtel 			if (route_choosen == 0) {
43252bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
43352bd4c0cSNicolas Dichtel 					break;
43451ebd318SNicolas Dichtel 				match = sibling;
43551ebd318SNicolas Dichtel 				break;
43651ebd318SNicolas Dichtel 			}
43751ebd318SNicolas Dichtel 		}
43851ebd318SNicolas Dichtel 	return match;
43951ebd318SNicolas Dichtel }
44051ebd318SNicolas Dichtel 
4411da177e4SLinus Torvalds /*
442c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4431da177e4SLinus Torvalds  */
4441da177e4SLinus Torvalds 
4458ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4468ed67789SDaniel Lezcano 						    struct rt6_info *rt,
447b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4481da177e4SLinus Torvalds 						    int oif,
449d420895eSYOSHIFUJI Hideaki 						    int flags)
4501da177e4SLinus Torvalds {
4511da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4521da177e4SLinus Torvalds 	struct rt6_info *sprt;
4531da177e4SLinus Torvalds 
454dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
455dd3abc4eSYOSHIFUJI Hideaki 		goto out;
456dd3abc4eSYOSHIFUJI Hideaki 
457d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
458d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
459dd3abc4eSYOSHIFUJI Hideaki 
460dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4611da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4621da177e4SLinus Torvalds 				return sprt;
4631da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
46438308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4651da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
466d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
4671da177e4SLinus Torvalds 						continue;
4681da177e4SLinus Torvalds 					if (local && (!oif ||
4691da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
4701da177e4SLinus Torvalds 						continue;
4711da177e4SLinus Torvalds 				}
4721da177e4SLinus Torvalds 				local = sprt;
4731da177e4SLinus Torvalds 			}
474dd3abc4eSYOSHIFUJI Hideaki 		} else {
475dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
476dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
477dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
478dd3abc4eSYOSHIFUJI Hideaki 		}
4791da177e4SLinus Torvalds 	}
4801da177e4SLinus Torvalds 
481dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
4821da177e4SLinus Torvalds 		if (local)
4831da177e4SLinus Torvalds 			return local;
4841da177e4SLinus Torvalds 
485d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
4868ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
4871da177e4SLinus Torvalds 	}
488dd3abc4eSYOSHIFUJI Hideaki out:
4891da177e4SLinus Torvalds 	return rt;
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds 
49227097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
493c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
494c2f17e82SHannes Frederic Sowa 	struct work_struct work;
495c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
496c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
497c2f17e82SHannes Frederic Sowa };
498c2f17e82SHannes Frederic Sowa 
499c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
500c2f17e82SHannes Frederic Sowa {
501c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
502c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
503c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
504c2f17e82SHannes Frederic Sowa 
505c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
506c2f17e82SHannes Frederic Sowa 	ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
507c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
508662f5533SMichael Büsch 	kfree(work);
509c2f17e82SHannes Frederic Sowa }
510c2f17e82SHannes Frederic Sowa 
51127097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
51227097255SYOSHIFUJI Hideaki {
513f2c31e32SEric Dumazet 	struct neighbour *neigh;
51427097255SYOSHIFUJI Hideaki 	/*
51527097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
51627097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
51727097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
51827097255SYOSHIFUJI Hideaki 	 *
51927097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
52027097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
52127097255SYOSHIFUJI Hideaki 	 */
5222152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
523fdd6681dSAmerigo Wang 		return;
5242152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
5252152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
5262152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
5272152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
5282152caeaSYOSHIFUJI Hideaki / 吉藤英明 		if (neigh->nud_state & NUD_VALID)
5292152caeaSYOSHIFUJI Hideaki / 吉藤英明 			goto out;
5307ff74a59SYOSHIFUJI Hideaki / 吉藤英明 	}
5312152caeaSYOSHIFUJI Hideaki / 吉藤英明 
5322152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!neigh ||
53352e16356SYOSHIFUJI Hideaki 	    time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
534c2f17e82SHannes Frederic Sowa 		struct __rt6_probe_work *work;
53527097255SYOSHIFUJI Hideaki 
536c2f17e82SHannes Frederic Sowa 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
537c2f17e82SHannes Frederic Sowa 
538c2f17e82SHannes Frederic Sowa 		if (neigh && work)
5397e980569SJiri Benc 			__neigh_set_probe_once(neigh);
5402152caeaSYOSHIFUJI Hideaki / 吉藤英明 
541c2f17e82SHannes Frederic Sowa 		if (neigh)
542c2f17e82SHannes Frederic Sowa 			write_unlock(&neigh->lock);
543c2f17e82SHannes Frederic Sowa 
544c2f17e82SHannes Frederic Sowa 		if (work) {
545c2f17e82SHannes Frederic Sowa 			INIT_WORK(&work->work, rt6_probe_deferred);
546c2f17e82SHannes Frederic Sowa 			work->target = rt->rt6i_gateway;
547c2f17e82SHannes Frederic Sowa 			dev_hold(rt->dst.dev);
548c2f17e82SHannes Frederic Sowa 			work->dev = rt->dst.dev;
549c2f17e82SHannes Frederic Sowa 			schedule_work(&work->work);
550c2f17e82SHannes Frederic Sowa 		}
551f2c31e32SEric Dumazet 	} else {
5522152caeaSYOSHIFUJI Hideaki / 吉藤英明 out:
5532152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_unlock(&neigh->lock);
55427097255SYOSHIFUJI Hideaki 	}
5552152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
556f2c31e32SEric Dumazet }
55727097255SYOSHIFUJI Hideaki #else
55827097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
55927097255SYOSHIFUJI Hideaki {
56027097255SYOSHIFUJI Hideaki }
56127097255SYOSHIFUJI Hideaki #endif
56227097255SYOSHIFUJI Hideaki 
5631da177e4SLinus Torvalds /*
564554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5651da177e4SLinus Torvalds  */
566b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5671da177e4SLinus Torvalds {
568d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
569161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
570554cfb7eSYOSHIFUJI Hideaki 		return 2;
571161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
572161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
573161980f4SDavid S. Miller 		return 1;
574554cfb7eSYOSHIFUJI Hideaki 	return 0;
5751da177e4SLinus Torvalds }
5761da177e4SLinus Torvalds 
577afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
5781da177e4SLinus Torvalds {
579f2c31e32SEric Dumazet 	struct neighbour *neigh;
580afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
581f2c31e32SEric Dumazet 
5824d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
5834d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
584afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
585145a3621SYOSHIFUJI Hideaki / 吉藤英明 
586145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
587145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
588145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
589145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
590554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
591afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
592398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
593a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
594afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
5957e980569SJiri Benc 		else
5967e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
597398bcbebSYOSHIFUJI Hideaki #endif
598145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
599afc154e9SHannes Frederic Sowa 	} else {
600afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
6017e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
602a5a81f0bSPaul Marks 	}
603145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
604145a3621SYOSHIFUJI Hideaki / 吉藤英明 
605a5a81f0bSPaul Marks 	return ret;
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds 
608554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
609554cfb7eSYOSHIFUJI Hideaki 			   int strict)
610554cfb7eSYOSHIFUJI Hideaki {
611a5a81f0bSPaul Marks 	int m;
6124d0c5911SYOSHIFUJI Hideaki 
6134d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
61477d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
615afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
616ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
617ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
618ebacaaa0SYOSHIFUJI Hideaki #endif
619afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
620afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
621afc154e9SHannes Frederic Sowa 		if (n < 0)
622afc154e9SHannes Frederic Sowa 			return n;
623afc154e9SHannes Frederic Sowa 	}
624554cfb7eSYOSHIFUJI Hideaki 	return m;
625554cfb7eSYOSHIFUJI Hideaki }
626554cfb7eSYOSHIFUJI Hideaki 
627f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
628afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
629afc154e9SHannes Frederic Sowa 				   bool *do_rr)
630554cfb7eSYOSHIFUJI Hideaki {
631554cfb7eSYOSHIFUJI Hideaki 	int m;
632afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
633554cfb7eSYOSHIFUJI Hideaki 
634554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
635f11e6659SDavid S. Miller 		goto out;
636554cfb7eSYOSHIFUJI Hideaki 
637554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6387e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
639afc154e9SHannes Frederic Sowa 		match_do_rr = true;
640afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6417e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
642f11e6659SDavid S. Miller 		goto out;
6431da177e4SLinus Torvalds 	}
644f11e6659SDavid S. Miller 
645afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
646afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
647afc154e9SHannes Frederic Sowa 
6487e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
649afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
650afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
651afc154e9SHannes Frederic Sowa 		*mpri = m;
652afc154e9SHannes Frederic Sowa 		match = rt;
653afc154e9SHannes Frederic Sowa 	}
654f11e6659SDavid S. Miller out:
655f11e6659SDavid S. Miller 	return match;
6561da177e4SLinus Torvalds }
6571da177e4SLinus Torvalds 
658f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
659f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
660afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
661afc154e9SHannes Frederic Sowa 				     bool *do_rr)
662f11e6659SDavid S. Miller {
6639fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
664f11e6659SDavid S. Miller 	int mpri = -1;
665f11e6659SDavid S. Miller 
666f11e6659SDavid S. Miller 	match = NULL;
6679fbdcfafSSteffen Klassert 	cont = NULL;
6689fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
6699fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6709fbdcfafSSteffen Klassert 			cont = rt;
6719fbdcfafSSteffen Klassert 			break;
6729fbdcfafSSteffen Klassert 		}
6739fbdcfafSSteffen Klassert 
674afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6759fbdcfafSSteffen Klassert 	}
6769fbdcfafSSteffen Klassert 
6779fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
6789fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6799fbdcfafSSteffen Klassert 			cont = rt;
6809fbdcfafSSteffen Klassert 			break;
6819fbdcfafSSteffen Klassert 		}
6829fbdcfafSSteffen Klassert 
6839fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6849fbdcfafSSteffen Klassert 	}
6859fbdcfafSSteffen Klassert 
6869fbdcfafSSteffen Klassert 	if (match || !cont)
6879fbdcfafSSteffen Klassert 		return match;
6889fbdcfafSSteffen Klassert 
6899fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
690afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
691f11e6659SDavid S. Miller 
692f11e6659SDavid S. Miller 	return match;
693f11e6659SDavid S. Miller }
694f11e6659SDavid S. Miller 
695f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
696f11e6659SDavid S. Miller {
697f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
6988ed67789SDaniel Lezcano 	struct net *net;
699afc154e9SHannes Frederic Sowa 	bool do_rr = false;
700f11e6659SDavid S. Miller 
701f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
702f11e6659SDavid S. Miller 	if (!rt0)
703f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
704f11e6659SDavid S. Miller 
705afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
706afc154e9SHannes Frederic Sowa 			     &do_rr);
707f11e6659SDavid S. Miller 
708afc154e9SHannes Frederic Sowa 	if (do_rr) {
709d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
710f11e6659SDavid S. Miller 
711554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
712f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
713f11e6659SDavid S. Miller 			next = fn->leaf;
714f11e6659SDavid S. Miller 
715f11e6659SDavid S. Miller 		if (next != rt0)
716f11e6659SDavid S. Miller 			fn->rr_ptr = next;
717554cfb7eSYOSHIFUJI Hideaki 	}
718554cfb7eSYOSHIFUJI Hideaki 
719d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
720a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
7211da177e4SLinus Torvalds }
7221da177e4SLinus Torvalds 
7238b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
7248b9df265SMartin KaFai Lau {
7258b9df265SMartin KaFai Lau 	return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY));
7268b9df265SMartin KaFai Lau }
7278b9df265SMartin KaFai Lau 
72870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
72970ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
730b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
73170ceb4f5SYOSHIFUJI Hideaki {
732c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
73370ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
73470ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
73570ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
7364bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
73770ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
73870ceb4f5SYOSHIFUJI Hideaki 
73970ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
74070ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
74170ceb4f5SYOSHIFUJI Hideaki 	}
74270ceb4f5SYOSHIFUJI Hideaki 
74370ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
74470ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
74570ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
74670ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
74770ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
74870ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
74970ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
75070ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
75170ceb4f5SYOSHIFUJI Hideaki 		}
75270ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
75370ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
75470ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
75570ceb4f5SYOSHIFUJI Hideaki 		}
75670ceb4f5SYOSHIFUJI Hideaki 	}
75770ceb4f5SYOSHIFUJI Hideaki 
75870ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
75970ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
7603933fc95SJens Rosenboom 		return -EINVAL;
76170ceb4f5SYOSHIFUJI Hideaki 
7624bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
76370ceb4f5SYOSHIFUJI Hideaki 
76470ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
76570ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
76670ceb4f5SYOSHIFUJI Hideaki 	else {
76770ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
76870ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
76970ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
77070ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
77170ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
77270ceb4f5SYOSHIFUJI Hideaki 	}
77370ceb4f5SYOSHIFUJI Hideaki 
774f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
775f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
776f104a567SDuan Jiong 	else
777f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
778f104a567SDuan Jiong 					gwaddr, dev->ifindex);
77970ceb4f5SYOSHIFUJI Hideaki 
78070ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
781e0a1ad73SThomas Graf 		ip6_del_rt(rt);
78270ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
78370ceb4f5SYOSHIFUJI Hideaki 	}
78470ceb4f5SYOSHIFUJI Hideaki 
78570ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
786efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
78770ceb4f5SYOSHIFUJI Hideaki 					pref);
78870ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
78970ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
79070ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
79170ceb4f5SYOSHIFUJI Hideaki 
79270ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
7931716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
7941716a961SGao feng 			rt6_clean_expires(rt);
7951716a961SGao feng 		else
7961716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
7971716a961SGao feng 
79894e187c0SAmerigo Wang 		ip6_rt_put(rt);
79970ceb4f5SYOSHIFUJI Hideaki 	}
80070ceb4f5SYOSHIFUJI Hideaki 	return 0;
80170ceb4f5SYOSHIFUJI Hideaki }
80270ceb4f5SYOSHIFUJI Hideaki #endif
80370ceb4f5SYOSHIFUJI Hideaki 
804a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
805a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
806a3c00e46SMartin KaFai Lau {
807a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
808a3c00e46SMartin KaFai Lau 	while (1) {
809a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
810a3c00e46SMartin KaFai Lau 			return NULL;
811a3c00e46SMartin KaFai Lau 		pn = fn->parent;
812a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
813a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
814a3c00e46SMartin KaFai Lau 		else
815a3c00e46SMartin KaFai Lau 			fn = pn;
816a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
817a3c00e46SMartin KaFai Lau 			return fn;
818a3c00e46SMartin KaFai Lau 	}
819a3c00e46SMartin KaFai Lau }
820c71099acSThomas Graf 
8218ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
8228ed67789SDaniel Lezcano 					     struct fib6_table *table,
8234c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
8241da177e4SLinus Torvalds {
8251da177e4SLinus Torvalds 	struct fib6_node *fn;
8261da177e4SLinus Torvalds 	struct rt6_info *rt;
8271da177e4SLinus Torvalds 
828c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8294c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
830c71099acSThomas Graf restart:
831c71099acSThomas Graf 	rt = fn->leaf;
8324c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
83351ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
83452bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
835a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
836a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
837a3c00e46SMartin KaFai Lau 		if (fn)
838a3c00e46SMartin KaFai Lau 			goto restart;
839a3c00e46SMartin KaFai Lau 	}
840d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
841c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8421da177e4SLinus Torvalds 	return rt;
843c71099acSThomas Graf 
844c71099acSThomas Graf }
845c71099acSThomas Graf 
846ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
847ea6e574eSFlorian Westphal 				    int flags)
848ea6e574eSFlorian Westphal {
849ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
850ea6e574eSFlorian Westphal }
851ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
852ea6e574eSFlorian Westphal 
8539acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8549acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
855c71099acSThomas Graf {
8564c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8574c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8584c9483b2SDavid S. Miller 		.daddr = *daddr,
859c71099acSThomas Graf 	};
860c71099acSThomas Graf 	struct dst_entry *dst;
86177d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
862c71099acSThomas Graf 
863adaa70bbSThomas Graf 	if (saddr) {
8644c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
865adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
866adaa70bbSThomas Graf 	}
867adaa70bbSThomas Graf 
8684c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
869c71099acSThomas Graf 	if (dst->error == 0)
870c71099acSThomas Graf 		return (struct rt6_info *) dst;
871c71099acSThomas Graf 
872c71099acSThomas Graf 	dst_release(dst);
873c71099acSThomas Graf 
8741da177e4SLinus Torvalds 	return NULL;
8751da177e4SLinus Torvalds }
8767159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
8777159039aSYOSHIFUJI Hideaki 
878c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
8791da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
8801da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
8811da177e4SLinus Torvalds    be destroyed.
8821da177e4SLinus Torvalds  */
8831da177e4SLinus Torvalds 
884e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
885e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
8861da177e4SLinus Torvalds {
8871da177e4SLinus Torvalds 	int err;
888c71099acSThomas Graf 	struct fib6_table *table;
8891da177e4SLinus Torvalds 
890c71099acSThomas Graf 	table = rt->rt6i_table;
891c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
892e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
893c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	return err;
8961da177e4SLinus Torvalds }
8971da177e4SLinus Torvalds 
89840e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
89940e22e8fSThomas Graf {
900e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
901e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
902e715b6d3SFlorian Westphal 
903e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
90440e22e8fSThomas Graf }
90540e22e8fSThomas Graf 
9068b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
90721efcfa0SEric Dumazet 					   const struct in6_addr *daddr,
908b71d1d42SEric Dumazet 					   const struct in6_addr *saddr)
9091da177e4SLinus Torvalds {
9101da177e4SLinus Torvalds 	struct rt6_info *rt;
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds 	/*
9131da177e4SLinus Torvalds 	 *	Clone the route.
9141da177e4SLinus Torvalds 	 */
9151da177e4SLinus Torvalds 
91621efcfa0SEric Dumazet 	rt = ip6_rt_copy(ort, daddr);
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds 	if (rt) {
9198b9df265SMartin KaFai Lau 		rt->rt6i_flags |= RTF_CACHE;
9208b9df265SMartin KaFai Lau 
9218b9df265SMartin KaFai Lau 		if (!rt6_is_gw_or_nonexthop(ort)) {
922bb3c3686SDavid S. Miller 			if (ort->rt6i_dst.plen != 128 &&
92321efcfa0SEric Dumazet 			    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
92458c4fb86SYOSHIFUJI Hideaki 				rt->rt6i_flags |= RTF_ANYCAST;
9251da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
9261da177e4SLinus Torvalds 			if (rt->rt6i_src.plen && saddr) {
9274e3fd7a0SAlexey Dobriyan 				rt->rt6i_src.addr = *saddr;
9281da177e4SLinus Torvalds 				rt->rt6i_src.plen = 128;
9291da177e4SLinus Torvalds 			}
9301da177e4SLinus Torvalds #endif
93195a9a5baSYOSHIFUJI Hideaki 		}
9321da177e4SLinus Torvalds 	}
93395a9a5baSYOSHIFUJI Hideaki 
934299d9939SYOSHIFUJI Hideaki 	return rt;
935299d9939SYOSHIFUJI Hideaki }
936299d9939SYOSHIFUJI Hideaki 
9378ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
9384c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
9391da177e4SLinus Torvalds {
940367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
94145e4fd26SMartin KaFai Lau 	struct rt6_info *rt;
942c71099acSThomas Graf 	int strict = 0;
9431da177e4SLinus Torvalds 
94477d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
945367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
946367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
9471da177e4SLinus Torvalds 
948c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
9491da177e4SLinus Torvalds 
9504c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
951367efcb9SMartin KaFai Lau 	saved_fn = fn;
9521da177e4SLinus Torvalds 
953a3c00e46SMartin KaFai Lau redo_rt6_select:
954367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
95552bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
956367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
957a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
958a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
959a3c00e46SMartin KaFai Lau 		if (fn)
960a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
961367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
962367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
963367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
964367efcb9SMartin KaFai Lau 			fn = saved_fn;
965367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
966367efcb9SMartin KaFai Lau 		}
967a3c00e46SMartin KaFai Lau 	}
968a3c00e46SMartin KaFai Lau 
9693da59bd9SMartin KaFai Lau 	dst_use(&rt->dst, jiffies);
970c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
9711da177e4SLinus Torvalds 
9723da59bd9SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) {
9733da59bd9SMartin KaFai Lau 		goto done;
9743da59bd9SMartin KaFai Lau 	} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
9753da59bd9SMartin KaFai Lau 			    !(rt->rt6i_flags & RTF_GATEWAY))) {
9763da59bd9SMartin KaFai Lau 		/* Create a RTF_CACHE clone which will not be
9773da59bd9SMartin KaFai Lau 		 * owned by the fib6 tree.  It is for the special case where
9783da59bd9SMartin KaFai Lau 		 * the daddr in the skb during the neighbor look-up is different
9793da59bd9SMartin KaFai Lau 		 * from the fl6->daddr used to look-up route here.
9803da59bd9SMartin KaFai Lau 		 */
981c71099acSThomas Graf 
9823da59bd9SMartin KaFai Lau 		struct rt6_info *uncached_rt;
9833da59bd9SMartin KaFai Lau 
9843da59bd9SMartin KaFai Lau 		uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL);
9853da59bd9SMartin KaFai Lau 		dst_release(&rt->dst);
9863da59bd9SMartin KaFai Lau 
9873da59bd9SMartin KaFai Lau 		if (uncached_rt)
988*8d0b94afSMartin KaFai Lau 			rt6_uncached_list_add(uncached_rt);
9893da59bd9SMartin KaFai Lau 		else
9903da59bd9SMartin KaFai Lau 			uncached_rt = net->ipv6.ip6_null_entry;
9913da59bd9SMartin KaFai Lau 		dst_hold(&uncached_rt->dst);
9923da59bd9SMartin KaFai Lau 		return uncached_rt;
9933da59bd9SMartin KaFai Lau 	}
9943da59bd9SMartin KaFai Lau 
9953da59bd9SMartin KaFai Lau done:
9963da59bd9SMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
997c71099acSThomas Graf 	return rt;
998c71099acSThomas Graf }
999c71099acSThomas Graf 
10008ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
10014c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
10024acad72dSPavel Emelyanov {
10034c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
10044acad72dSPavel Emelyanov }
10054acad72dSPavel Emelyanov 
100672331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
100772331bc0SShmulik Ladkani 						struct net_device *dev,
100872331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
100972331bc0SShmulik Ladkani {
101072331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
101172331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
101272331bc0SShmulik Ladkani 
101372331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
101472331bc0SShmulik Ladkani }
101572331bc0SShmulik Ladkani 
1016c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1017c71099acSThomas Graf {
1018b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1019c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1020adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
10214c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
10224c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
10234c9483b2SDavid S. Miller 		.daddr = iph->daddr,
10244c9483b2SDavid S. Miller 		.saddr = iph->saddr,
10256502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
10264c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
10274c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1028c71099acSThomas Graf 	};
1029adaa70bbSThomas Graf 
103072331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1031c71099acSThomas Graf }
1032c71099acSThomas Graf 
10338ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
10344c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1035c71099acSThomas Graf {
10364c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1037c71099acSThomas Graf }
1038c71099acSThomas Graf 
10399c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
10404c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1041c71099acSThomas Graf {
1042c71099acSThomas Graf 	int flags = 0;
1043c71099acSThomas Graf 
10441fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
10454dc27d1cSDavid McCullough 
10464c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
104777d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1048c71099acSThomas Graf 
10494c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
1050adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
10510c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
10520c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1053adaa70bbSThomas Graf 
10544c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
10551da177e4SLinus Torvalds }
10567159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
10571da177e4SLinus Torvalds 
10582774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
105914e50e57SDavid S. Miller {
10605c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
106114e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
106214e50e57SDavid S. Miller 
1063f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
106414e50e57SDavid S. Miller 	if (rt) {
1065d8d1f30bSChangli Gao 		new = &rt->dst;
106614e50e57SDavid S. Miller 
10678104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
10688104891bSSteffen Klassert 
106914e50e57SDavid S. Miller 		new->__use = 1;
1070352e512cSHerbert Xu 		new->input = dst_discard;
1071aad88724SEric Dumazet 		new->output = dst_discard_sk;
107214e50e57SDavid S. Miller 
107321efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
107421efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
107521efcfa0SEric Dumazet 		else
1076defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
107714e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
107814e50e57SDavid S. Miller 		if (rt->rt6i_idev)
107914e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
108014e50e57SDavid S. Miller 
10814e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
10821716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
108314e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
108414e50e57SDavid S. Miller 
108514e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
108614e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
108714e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
108814e50e57SDavid S. Miller #endif
108914e50e57SDavid S. Miller 
109014e50e57SDavid S. Miller 		dst_free(new);
109114e50e57SDavid S. Miller 	}
109214e50e57SDavid S. Miller 
109369ead7afSDavid S. Miller 	dst_release(dst_orig);
109469ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
109514e50e57SDavid S. Miller }
109614e50e57SDavid S. Miller 
10971da177e4SLinus Torvalds /*
10981da177e4SLinus Torvalds  *	Destination cache support functions
10991da177e4SLinus Torvalds  */
11001da177e4SLinus Torvalds 
11014b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
11024b32b5adSMartin KaFai Lau {
11034b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
11044b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
11054b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
11064b32b5adSMartin KaFai Lau }
11074b32b5adSMartin KaFai Lau 
11083da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
11093da59bd9SMartin KaFai Lau {
11103da59bd9SMartin KaFai Lau 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
11113da59bd9SMartin KaFai Lau 		return NULL;
11123da59bd9SMartin KaFai Lau 
11133da59bd9SMartin KaFai Lau 	if (rt6_check_expired(rt))
11143da59bd9SMartin KaFai Lau 		return NULL;
11153da59bd9SMartin KaFai Lau 
11163da59bd9SMartin KaFai Lau 	return &rt->dst;
11173da59bd9SMartin KaFai Lau }
11183da59bd9SMartin KaFai Lau 
11193da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
11203da59bd9SMartin KaFai Lau {
11213da59bd9SMartin KaFai Lau 	if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
11223da59bd9SMartin KaFai Lau 	    rt6_check((struct rt6_info *)(rt->dst.from), cookie))
11233da59bd9SMartin KaFai Lau 		return &rt->dst;
11243da59bd9SMartin KaFai Lau 	else
11253da59bd9SMartin KaFai Lau 		return NULL;
11263da59bd9SMartin KaFai Lau }
11273da59bd9SMartin KaFai Lau 
11281da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
11291da177e4SLinus Torvalds {
11301da177e4SLinus Torvalds 	struct rt6_info *rt;
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
11331da177e4SLinus Torvalds 
11346f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
11356f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
11366f3118b5SNicolas Dichtel 	 * into this function always.
11376f3118b5SNicolas Dichtel 	 */
1138e3bc10bdSHannes Frederic Sowa 
11394b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
11404b32b5adSMartin KaFai Lau 
11413da59bd9SMartin KaFai Lau 	if (unlikely(dst->flags & DST_NOCACHE))
11423da59bd9SMartin KaFai Lau 		return rt6_dst_from_check(rt, cookie);
11433da59bd9SMartin KaFai Lau 	else
11443da59bd9SMartin KaFai Lau 		return rt6_check(rt, cookie);
11451da177e4SLinus Torvalds }
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
11481da177e4SLinus Torvalds {
11491da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
11501da177e4SLinus Torvalds 
11511da177e4SLinus Torvalds 	if (rt) {
115254c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
115354c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1154e0a1ad73SThomas Graf 				ip6_del_rt(rt);
115554c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
11561da177e4SLinus Torvalds 			}
115754c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
115854c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
115954c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
116054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
116154c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
116254c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
11631da177e4SLinus Torvalds }
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
11661da177e4SLinus Torvalds {
11671da177e4SLinus Torvalds 	struct rt6_info *rt;
11681da177e4SLinus Torvalds 
11693ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
11701da177e4SLinus Torvalds 
1171adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
11721da177e4SLinus Torvalds 	if (rt) {
11731eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
11741eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
11751eb4f758SHannes Frederic Sowa 			if (ip6_del_rt(rt))
11761eb4f758SHannes Frederic Sowa 				dst_free(&rt->dst);
11771eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
11781da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
11791da177e4SLinus Torvalds 		}
11801da177e4SLinus Torvalds 	}
11811eb4f758SHannes Frederic Sowa }
11821da177e4SLinus Torvalds 
118345e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
118445e4fd26SMartin KaFai Lau {
118545e4fd26SMartin KaFai Lau 	struct net *net = dev_net(rt->dst.dev);
118645e4fd26SMartin KaFai Lau 
118745e4fd26SMartin KaFai Lau 	rt->rt6i_flags |= RTF_MODIFIED;
118845e4fd26SMartin KaFai Lau 	rt->rt6i_pmtu = mtu;
118945e4fd26SMartin KaFai Lau 	rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
119045e4fd26SMartin KaFai Lau }
119145e4fd26SMartin KaFai Lau 
119245e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
119345e4fd26SMartin KaFai Lau 				 const struct ipv6hdr *iph, u32 mtu)
11941da177e4SLinus Torvalds {
11951da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
11961da177e4SLinus Torvalds 
119745e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_LOCAL)
119845e4fd26SMartin KaFai Lau 		return;
119945e4fd26SMartin KaFai Lau 
120081aded24SDavid S. Miller 	dst_confirm(dst);
120145e4fd26SMartin KaFai Lau 	mtu = max_t(u32, mtu, IPV6_MIN_MTU);
120245e4fd26SMartin KaFai Lau 	if (mtu >= dst_mtu(dst))
120345e4fd26SMartin KaFai Lau 		return;
120481aded24SDavid S. Miller 
120545e4fd26SMartin KaFai Lau 	if (rt6->rt6i_flags & RTF_CACHE) {
120645e4fd26SMartin KaFai Lau 		rt6_do_update_pmtu(rt6, mtu);
120745e4fd26SMartin KaFai Lau 	} else {
120845e4fd26SMartin KaFai Lau 		const struct in6_addr *daddr, *saddr;
120945e4fd26SMartin KaFai Lau 		struct rt6_info *nrt6;
12109d289715SHagen Paul Pfeifer 
121145e4fd26SMartin KaFai Lau 		if (iph) {
121245e4fd26SMartin KaFai Lau 			daddr = &iph->daddr;
121345e4fd26SMartin KaFai Lau 			saddr = &iph->saddr;
121445e4fd26SMartin KaFai Lau 		} else if (sk) {
121545e4fd26SMartin KaFai Lau 			daddr = &sk->sk_v6_daddr;
121645e4fd26SMartin KaFai Lau 			saddr = &inet6_sk(sk)->saddr;
121745e4fd26SMartin KaFai Lau 		} else {
121845e4fd26SMartin KaFai Lau 			return;
12191da177e4SLinus Torvalds 		}
122045e4fd26SMartin KaFai Lau 		nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr);
122145e4fd26SMartin KaFai Lau 		if (nrt6) {
122245e4fd26SMartin KaFai Lau 			rt6_do_update_pmtu(nrt6, mtu);
122345e4fd26SMartin KaFai Lau 
122445e4fd26SMartin KaFai Lau 			/* ip6_ins_rt(nrt6) will bump the
122545e4fd26SMartin KaFai Lau 			 * rt6->rt6i_node->fn_sernum
122645e4fd26SMartin KaFai Lau 			 * which will fail the next rt6_check() and
122745e4fd26SMartin KaFai Lau 			 * invalidate the sk->sk_dst_cache.
122845e4fd26SMartin KaFai Lau 			 */
122945e4fd26SMartin KaFai Lau 			ip6_ins_rt(nrt6);
123045e4fd26SMartin KaFai Lau 		}
123145e4fd26SMartin KaFai Lau 	}
123245e4fd26SMartin KaFai Lau }
123345e4fd26SMartin KaFai Lau 
123445e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
123545e4fd26SMartin KaFai Lau 			       struct sk_buff *skb, u32 mtu)
123645e4fd26SMartin KaFai Lau {
123745e4fd26SMartin KaFai Lau 	__ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
12381da177e4SLinus Torvalds }
12391da177e4SLinus Torvalds 
124042ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
124142ae66c8SDavid S. Miller 		     int oif, u32 mark)
124281aded24SDavid S. Miller {
124381aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
124481aded24SDavid S. Miller 	struct dst_entry *dst;
124581aded24SDavid S. Miller 	struct flowi6 fl6;
124681aded24SDavid S. Miller 
124781aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
124881aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
12491b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
125081aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
125181aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
12526502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
125381aded24SDavid S. Miller 
125481aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
125581aded24SDavid S. Miller 	if (!dst->error)
125645e4fd26SMartin KaFai Lau 		__ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
125781aded24SDavid S. Miller 	dst_release(dst);
125881aded24SDavid S. Miller }
125981aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
126081aded24SDavid S. Miller 
126181aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
126281aded24SDavid S. Miller {
126381aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
126481aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
126581aded24SDavid S. Miller }
126681aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
126781aded24SDavid S. Miller 
1268b55b76b2SDuan Jiong /* Handle redirects */
1269b55b76b2SDuan Jiong struct ip6rd_flowi {
1270b55b76b2SDuan Jiong 	struct flowi6 fl6;
1271b55b76b2SDuan Jiong 	struct in6_addr gateway;
1272b55b76b2SDuan Jiong };
1273b55b76b2SDuan Jiong 
1274b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1275b55b76b2SDuan Jiong 					     struct fib6_table *table,
1276b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1277b55b76b2SDuan Jiong 					     int flags)
1278b55b76b2SDuan Jiong {
1279b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1280b55b76b2SDuan Jiong 	struct rt6_info *rt;
1281b55b76b2SDuan Jiong 	struct fib6_node *fn;
1282b55b76b2SDuan Jiong 
1283b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1284b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1285b55b76b2SDuan Jiong 	 *
1286b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1287b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1288b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1289b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1290b55b76b2SDuan Jiong 	 * routes.
1291b55b76b2SDuan Jiong 	 */
1292b55b76b2SDuan Jiong 
1293b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1294b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1295b55b76b2SDuan Jiong restart:
1296b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1297b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1298b55b76b2SDuan Jiong 			continue;
1299b55b76b2SDuan Jiong 		if (rt->dst.error)
1300b55b76b2SDuan Jiong 			break;
1301b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1302b55b76b2SDuan Jiong 			continue;
1303b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1304b55b76b2SDuan Jiong 			continue;
1305b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1306b55b76b2SDuan Jiong 			continue;
1307b55b76b2SDuan Jiong 		break;
1308b55b76b2SDuan Jiong 	}
1309b55b76b2SDuan Jiong 
1310b55b76b2SDuan Jiong 	if (!rt)
1311b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1312b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1313b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1314b0a1ba59SMartin KaFai Lau 		goto out;
1315b0a1ba59SMartin KaFai Lau 	}
1316b0a1ba59SMartin KaFai Lau 
1317b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1318a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1319a3c00e46SMartin KaFai Lau 		if (fn)
1320a3c00e46SMartin KaFai Lau 			goto restart;
1321b55b76b2SDuan Jiong 	}
1322a3c00e46SMartin KaFai Lau 
1323b0a1ba59SMartin KaFai Lau out:
1324b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1325b55b76b2SDuan Jiong 
1326b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1327b55b76b2SDuan Jiong 
1328b55b76b2SDuan Jiong 	return rt;
1329b55b76b2SDuan Jiong };
1330b55b76b2SDuan Jiong 
1331b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1332b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1333b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1334b55b76b2SDuan Jiong {
1335b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1336b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1337b55b76b2SDuan Jiong 
1338b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1339b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1340b55b76b2SDuan Jiong 
1341b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1342b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1343b55b76b2SDuan Jiong }
1344b55b76b2SDuan Jiong 
13453a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
13463a5ad2eeSDavid S. Miller {
13473a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
13483a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
13493a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
13503a5ad2eeSDavid S. Miller 
13513a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1352e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
13533a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
13543a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
13553a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
13563a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
13576502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
13583a5ad2eeSDavid S. Miller 
1359b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
13606700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
13613a5ad2eeSDavid S. Miller 	dst_release(dst);
13623a5ad2eeSDavid S. Miller }
13633a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
13643a5ad2eeSDavid S. Miller 
1365c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1366c92a59ecSDuan Jiong 			    u32 mark)
1367c92a59ecSDuan Jiong {
1368c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1369c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1370c92a59ecSDuan Jiong 	struct dst_entry *dst;
1371c92a59ecSDuan Jiong 	struct flowi6 fl6;
1372c92a59ecSDuan Jiong 
1373c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1374e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1375c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1376c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1377c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1378c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1379c92a59ecSDuan Jiong 
1380b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1381c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1382c92a59ecSDuan Jiong 	dst_release(dst);
1383c92a59ecSDuan Jiong }
1384c92a59ecSDuan Jiong 
13853a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
13863a5ad2eeSDavid S. Miller {
13873a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
13883a5ad2eeSDavid S. Miller }
13893a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
13903a5ad2eeSDavid S. Miller 
13910dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
13921da177e4SLinus Torvalds {
13930dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
13940dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
13950dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
13960dbaee3bSDavid S. Miller 
13971da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
13981da177e4SLinus Torvalds 
13995578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
14005578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
14011da177e4SLinus Torvalds 
14021da177e4SLinus Torvalds 	/*
14031da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
14041da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
14051da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
14061da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
14071da177e4SLinus Torvalds 	 */
14081da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
14091da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
14101da177e4SLinus Torvalds 	return mtu;
14111da177e4SLinus Torvalds }
14121da177e4SLinus Torvalds 
1413ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1414d33e4553SDavid S. Miller {
14154b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
14164b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1417d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1418618f9bc7SSteffen Klassert 
1419618f9bc7SSteffen Klassert 	if (mtu)
142030f78d8eSEric Dumazet 		goto out;
1421618f9bc7SSteffen Klassert 
14224b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
14234b32b5adSMartin KaFai Lau 	if (mtu)
14244b32b5adSMartin KaFai Lau 		goto out;
14254b32b5adSMartin KaFai Lau 
1426618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1427d33e4553SDavid S. Miller 
1428d33e4553SDavid S. Miller 	rcu_read_lock();
1429d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1430d33e4553SDavid S. Miller 	if (idev)
1431d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1432d33e4553SDavid S. Miller 	rcu_read_unlock();
1433d33e4553SDavid S. Miller 
143430f78d8eSEric Dumazet out:
143530f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1436d33e4553SDavid S. Miller }
1437d33e4553SDavid S. Miller 
14383b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
14393b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
14405d0bbeebSThomas Graf 
14413b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
144287a11578SDavid S. Miller 				  struct flowi6 *fl6)
14431da177e4SLinus Torvalds {
144487a11578SDavid S. Miller 	struct dst_entry *dst;
14451da177e4SLinus Torvalds 	struct rt6_info *rt;
14461da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1447c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
14481da177e4SLinus Torvalds 
144938308473SDavid S. Miller 	if (unlikely(!idev))
1450122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
14511da177e4SLinus Torvalds 
14528b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, dev, 0, NULL);
145338308473SDavid S. Miller 	if (unlikely(!rt)) {
14541da177e4SLinus Torvalds 		in6_dev_put(idev);
145587a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
14561da177e4SLinus Torvalds 		goto out;
14571da177e4SLinus Torvalds 	}
14581da177e4SLinus Torvalds 
14598e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
14608e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1461d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1462550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
146387a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
14648e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
14658e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
146614edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
14671da177e4SLinus Torvalds 
14683b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1469d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1470d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
14713b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
14721da177e4SLinus Torvalds 
14735578689aSDaniel Lezcano 	fib6_force_start_gc(net);
14741da177e4SLinus Torvalds 
147587a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
147687a11578SDavid S. Miller 
14771da177e4SLinus Torvalds out:
147887a11578SDavid S. Miller 	return dst;
14791da177e4SLinus Torvalds }
14801da177e4SLinus Torvalds 
14813d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
14821da177e4SLinus Torvalds {
1483e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
14843d0f24a7SStephen Hemminger 	int more = 0;
14851da177e4SLinus Torvalds 
14863b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
14873b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
14885d0bbeebSThomas Graf 
14891da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
14901da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
14911da177e4SLinus Torvalds 			*pprev = dst->next;
14921da177e4SLinus Torvalds 			dst_free(dst);
14931da177e4SLinus Torvalds 		} else {
14941da177e4SLinus Torvalds 			pprev = &dst->next;
14953d0f24a7SStephen Hemminger 			++more;
14961da177e4SLinus Torvalds 		}
14971da177e4SLinus Torvalds 	}
14981da177e4SLinus Torvalds 
14993b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
15005d0bbeebSThomas Graf 
15013d0f24a7SStephen Hemminger 	return more;
15021da177e4SLinus Torvalds }
15031da177e4SLinus Torvalds 
15041e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
15051e493d19SDavid S. Miller 			    void *arg)
15061e493d19SDavid S. Miller {
15071e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
15081e493d19SDavid S. Miller 
15091e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
15101e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
15111e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
15121e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
15131e493d19SDavid S. Miller 		if (func(rt, arg)) {
15141e493d19SDavid S. Miller 			*pprev = dst->next;
15151e493d19SDavid S. Miller 			dst_free(dst);
15161e493d19SDavid S. Miller 		} else {
15171e493d19SDavid S. Miller 			pprev = &dst->next;
15181e493d19SDavid S. Miller 		}
15191e493d19SDavid S. Miller 	}
15201e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
15211e493d19SDavid S. Miller }
15221e493d19SDavid S. Miller 
1523569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
15241da177e4SLinus Torvalds {
152586393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
15267019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
15277019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
15287019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
15297019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
15307019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1531fc66f95cSEric Dumazet 	int entries;
15321da177e4SLinus Torvalds 
1533fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
153449a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1535fc66f95cSEric Dumazet 	    entries <= rt_max_size)
15361da177e4SLinus Torvalds 		goto out;
15371da177e4SLinus Torvalds 
15386891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
153914956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1540fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1541fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
15427019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
15431da177e4SLinus Torvalds out:
15447019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1545fc66f95cSEric Dumazet 	return entries > rt_max_size;
15461da177e4SLinus Torvalds }
15471da177e4SLinus Torvalds 
1548e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1549e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1550e715b6d3SFlorian Westphal {
1551e715b6d3SFlorian Westphal 	struct nlattr *nla;
1552e715b6d3SFlorian Westphal 	int remaining;
1553e715b6d3SFlorian Westphal 	u32 *mp;
1554e715b6d3SFlorian Westphal 
155563159f29SIan Morris 	if (!cfg->fc_mx)
1556e715b6d3SFlorian Westphal 		return 0;
1557e715b6d3SFlorian Westphal 
1558e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1559e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1560e715b6d3SFlorian Westphal 		return -ENOMEM;
1561e715b6d3SFlorian Westphal 
1562e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1563e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1564e715b6d3SFlorian Westphal 
1565e715b6d3SFlorian Westphal 		if (type) {
1566ea697639SDaniel Borkmann 			u32 val;
1567ea697639SDaniel Borkmann 
1568e715b6d3SFlorian Westphal 			if (unlikely(type > RTAX_MAX))
1569e715b6d3SFlorian Westphal 				goto err;
1570ea697639SDaniel Borkmann 			if (type == RTAX_CC_ALGO) {
1571ea697639SDaniel Borkmann 				char tmp[TCP_CA_NAME_MAX];
1572e715b6d3SFlorian Westphal 
1573ea697639SDaniel Borkmann 				nla_strlcpy(tmp, nla, sizeof(tmp));
1574ea697639SDaniel Borkmann 				val = tcp_ca_get_key_by_name(tmp);
1575ea697639SDaniel Borkmann 				if (val == TCP_CA_UNSPEC)
1576ea697639SDaniel Borkmann 					goto err;
1577ea697639SDaniel Borkmann 			} else {
1578ea697639SDaniel Borkmann 				val = nla_get_u32(nla);
1579ea697639SDaniel Borkmann 			}
1580ea697639SDaniel Borkmann 
1581ea697639SDaniel Borkmann 			mp[type - 1] = val;
1582e715b6d3SFlorian Westphal 			__set_bit(type - 1, mxc->mx_valid);
1583e715b6d3SFlorian Westphal 		}
1584e715b6d3SFlorian Westphal 	}
1585e715b6d3SFlorian Westphal 
1586e715b6d3SFlorian Westphal 	mxc->mx = mp;
1587e715b6d3SFlorian Westphal 
1588e715b6d3SFlorian Westphal 	return 0;
1589e715b6d3SFlorian Westphal  err:
1590e715b6d3SFlorian Westphal 	kfree(mp);
1591e715b6d3SFlorian Westphal 	return -EINVAL;
1592e715b6d3SFlorian Westphal }
15931da177e4SLinus Torvalds 
159486872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
15951da177e4SLinus Torvalds {
15961da177e4SLinus Torvalds 	int err;
15975578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
15981da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
15991da177e4SLinus Torvalds 	struct net_device *dev = NULL;
16001da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1601c71099acSThomas Graf 	struct fib6_table *table;
1602e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
16031da177e4SLinus Torvalds 	int addr_type;
16041da177e4SLinus Torvalds 
160586872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
16061da177e4SLinus Torvalds 		return -EINVAL;
16071da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
160886872cb5SThomas Graf 	if (cfg->fc_src_len)
16091da177e4SLinus Torvalds 		return -EINVAL;
16101da177e4SLinus Torvalds #endif
161186872cb5SThomas Graf 	if (cfg->fc_ifindex) {
16121da177e4SLinus Torvalds 		err = -ENODEV;
16135578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
16141da177e4SLinus Torvalds 		if (!dev)
16151da177e4SLinus Torvalds 			goto out;
16161da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
16171da177e4SLinus Torvalds 		if (!idev)
16181da177e4SLinus Torvalds 			goto out;
16191da177e4SLinus Torvalds 	}
16201da177e4SLinus Torvalds 
162186872cb5SThomas Graf 	if (cfg->fc_metric == 0)
162286872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
16231da177e4SLinus Torvalds 
1624c71099acSThomas Graf 	err = -ENOBUFS;
162538308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1626d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1627d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
162838308473SDavid S. Miller 		if (!table) {
1629f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1630d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1631d71314b4SMatti Vaittinen 		}
1632d71314b4SMatti Vaittinen 	} else {
1633d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1634d71314b4SMatti Vaittinen 	}
163538308473SDavid S. Miller 
163638308473SDavid S. Miller 	if (!table)
1637c71099acSThomas Graf 		goto out;
1638c71099acSThomas Graf 
1639c88507fbSSabrina Dubroca 	rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table);
16401da177e4SLinus Torvalds 
164138308473SDavid S. Miller 	if (!rt) {
16421da177e4SLinus Torvalds 		err = -ENOMEM;
16431da177e4SLinus Torvalds 		goto out;
16441da177e4SLinus Torvalds 	}
16451da177e4SLinus Torvalds 
16461716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
16471716a961SGao feng 		rt6_set_expires(rt, jiffies +
16481716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
16491716a961SGao feng 	else
16501716a961SGao feng 		rt6_clean_expires(rt);
16511da177e4SLinus Torvalds 
165286872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
165386872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
165486872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
165586872cb5SThomas Graf 
165686872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
16571da177e4SLinus Torvalds 
16581da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1659d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1660ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1661ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
16621da177e4SLinus Torvalds 	else
1663d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
16641da177e4SLinus Torvalds 
1665d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
16661da177e4SLinus Torvalds 
166786872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
166886872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1669afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
167011d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
16711da177e4SLinus Torvalds 
16721da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
167386872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
167486872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
16751da177e4SLinus Torvalds #endif
16761da177e4SLinus Torvalds 
167786872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
16781da177e4SLinus Torvalds 
16791da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
16801da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
16811da177e4SLinus Torvalds 	 */
168286872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
168338308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
168438308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
168538308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
16861da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
16875578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
16881da177e4SLinus Torvalds 			if (dev) {
16891da177e4SLinus Torvalds 				dev_put(dev);
16901da177e4SLinus Torvalds 				in6_dev_put(idev);
16911da177e4SLinus Torvalds 			}
16925578689aSDaniel Lezcano 			dev = net->loopback_dev;
16931da177e4SLinus Torvalds 			dev_hold(dev);
16941da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
16951da177e4SLinus Torvalds 			if (!idev) {
16961da177e4SLinus Torvalds 				err = -ENODEV;
16971da177e4SLinus Torvalds 				goto out;
16981da177e4SLinus Torvalds 			}
16991da177e4SLinus Torvalds 		}
17001da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1701ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1702ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1703ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1704aad88724SEric Dumazet 			rt->dst.output = dst_discard_sk;
17057150aedeSKamala R 			rt->dst.input = dst_discard;
1706ef2c7d7bSNicolas Dichtel 			break;
1707ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1708ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
17097150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
17107150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1711ef2c7d7bSNicolas Dichtel 			break;
1712b4949ab2SNicolas Dichtel 		case RTN_THROW:
1713ef2c7d7bSNicolas Dichtel 		default:
17147150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
17157150aedeSKamala R 					: -ENETUNREACH;
17167150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
17177150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1718ef2c7d7bSNicolas Dichtel 			break;
1719ef2c7d7bSNicolas Dichtel 		}
17201da177e4SLinus Torvalds 		goto install_route;
17211da177e4SLinus Torvalds 	}
17221da177e4SLinus Torvalds 
172386872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1724b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
17251da177e4SLinus Torvalds 		int gwa_type;
17261da177e4SLinus Torvalds 
172786872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
172848ed7b26SFlorian Westphal 
172948ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
173048ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
173148ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
173248ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
173348ed7b26SFlorian Westphal 		 */
173448ed7b26SFlorian Westphal 		err = -EINVAL;
173548ed7b26SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr, NULL, 0, 0))
173648ed7b26SFlorian Westphal 			goto out;
173748ed7b26SFlorian Westphal 
17384e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
17391da177e4SLinus Torvalds 		gwa_type = ipv6_addr_type(gw_addr);
17401da177e4SLinus Torvalds 
17411da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
17421da177e4SLinus Torvalds 			struct rt6_info *grt;
17431da177e4SLinus Torvalds 
17441da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
17451da177e4SLinus Torvalds 			   addresses as nexthop address.
17461da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
17471da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
17481da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
17491da177e4SLinus Torvalds 			   some exceptions. --ANK
17501da177e4SLinus Torvalds 			 */
17511da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
17521da177e4SLinus Torvalds 				goto out;
17531da177e4SLinus Torvalds 
17545578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
17551da177e4SLinus Torvalds 
17561da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
175738308473SDavid S. Miller 			if (!grt)
17581da177e4SLinus Torvalds 				goto out;
17591da177e4SLinus Torvalds 			if (dev) {
1760d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
176194e187c0SAmerigo Wang 					ip6_rt_put(grt);
17621da177e4SLinus Torvalds 					goto out;
17631da177e4SLinus Torvalds 				}
17641da177e4SLinus Torvalds 			} else {
1765d1918542SDavid S. Miller 				dev = grt->dst.dev;
17661da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
17671da177e4SLinus Torvalds 				dev_hold(dev);
17681da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
17691da177e4SLinus Torvalds 			}
17701da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
17711da177e4SLinus Torvalds 				err = 0;
177294e187c0SAmerigo Wang 			ip6_rt_put(grt);
17731da177e4SLinus Torvalds 
17741da177e4SLinus Torvalds 			if (err)
17751da177e4SLinus Torvalds 				goto out;
17761da177e4SLinus Torvalds 		}
17771da177e4SLinus Torvalds 		err = -EINVAL;
177838308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
17791da177e4SLinus Torvalds 			goto out;
17801da177e4SLinus Torvalds 	}
17811da177e4SLinus Torvalds 
17821da177e4SLinus Torvalds 	err = -ENODEV;
178338308473SDavid S. Miller 	if (!dev)
17841da177e4SLinus Torvalds 		goto out;
17851da177e4SLinus Torvalds 
1786c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1787c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1788c3968a85SDaniel Walter 			err = -EINVAL;
1789c3968a85SDaniel Walter 			goto out;
1790c3968a85SDaniel Walter 		}
17914e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1792c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1793c3968a85SDaniel Walter 	} else
1794c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1795c3968a85SDaniel Walter 
179686872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
17971da177e4SLinus Torvalds 
17981da177e4SLinus Torvalds install_route:
1799d8d1f30bSChangli Gao 	rt->dst.dev = dev;
18001da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1801c71099acSThomas Graf 	rt->rt6i_table = table;
180263152fc0SDaniel Lezcano 
1803c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
180463152fc0SDaniel Lezcano 
1805e715b6d3SFlorian Westphal 	err = ip6_convert_metrics(&mxc, cfg);
1806e715b6d3SFlorian Westphal 	if (err)
1807e715b6d3SFlorian Westphal 		goto out;
18081da177e4SLinus Torvalds 
1809e715b6d3SFlorian Westphal 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
1810e715b6d3SFlorian Westphal 
1811e715b6d3SFlorian Westphal 	kfree(mxc.mx);
1812e715b6d3SFlorian Westphal 	return err;
18131da177e4SLinus Torvalds out:
18141da177e4SLinus Torvalds 	if (dev)
18151da177e4SLinus Torvalds 		dev_put(dev);
18161da177e4SLinus Torvalds 	if (idev)
18171da177e4SLinus Torvalds 		in6_dev_put(idev);
18181da177e4SLinus Torvalds 	if (rt)
1819d8d1f30bSChangli Gao 		dst_free(&rt->dst);
18201da177e4SLinus Torvalds 	return err;
18211da177e4SLinus Torvalds }
18221da177e4SLinus Torvalds 
182386872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
18241da177e4SLinus Torvalds {
18251da177e4SLinus Torvalds 	int err;
1826c71099acSThomas Graf 	struct fib6_table *table;
1827d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
18281da177e4SLinus Torvalds 
18296825a26cSGao feng 	if (rt == net->ipv6.ip6_null_entry) {
18306825a26cSGao feng 		err = -ENOENT;
18316825a26cSGao feng 		goto out;
18326825a26cSGao feng 	}
18336c813a72SPatrick McHardy 
1834c71099acSThomas Graf 	table = rt->rt6i_table;
1835c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
183686872cb5SThomas Graf 	err = fib6_del(rt, info);
1837c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
18381da177e4SLinus Torvalds 
18396825a26cSGao feng out:
184094e187c0SAmerigo Wang 	ip6_rt_put(rt);
18411da177e4SLinus Torvalds 	return err;
18421da177e4SLinus Torvalds }
18431da177e4SLinus Torvalds 
1844e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1845e0a1ad73SThomas Graf {
18464d1169c1SDenis V. Lunev 	struct nl_info info = {
1847d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
18484d1169c1SDenis V. Lunev 	};
1849528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1850e0a1ad73SThomas Graf }
1851e0a1ad73SThomas Graf 
185286872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
18531da177e4SLinus Torvalds {
1854c71099acSThomas Graf 	struct fib6_table *table;
18551da177e4SLinus Torvalds 	struct fib6_node *fn;
18561da177e4SLinus Torvalds 	struct rt6_info *rt;
18571da177e4SLinus Torvalds 	int err = -ESRCH;
18581da177e4SLinus Torvalds 
18595578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
186038308473SDavid S. Miller 	if (!table)
1861c71099acSThomas Graf 		return err;
18621da177e4SLinus Torvalds 
1863c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1864c71099acSThomas Graf 
1865c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
186686872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
186786872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
18681da177e4SLinus Torvalds 
18691da177e4SLinus Torvalds 	if (fn) {
1870d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
18711f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
18721f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
18731f56a01fSMartin KaFai Lau 				continue;
187486872cb5SThomas Graf 			if (cfg->fc_ifindex &&
1875d1918542SDavid S. Miller 			    (!rt->dst.dev ||
1876d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
18771da177e4SLinus Torvalds 				continue;
187886872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
187986872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
18801da177e4SLinus Torvalds 				continue;
188186872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
18821da177e4SLinus Torvalds 				continue;
1883d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1884c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
18851da177e4SLinus Torvalds 
188686872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
18871da177e4SLinus Torvalds 		}
18881da177e4SLinus Torvalds 	}
1889c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
18901da177e4SLinus Torvalds 
18911da177e4SLinus Torvalds 	return err;
18921da177e4SLinus Torvalds }
18931da177e4SLinus Torvalds 
18946700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
1895a6279458SYOSHIFUJI Hideaki {
1896e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
1897a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
1898e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
1899e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
1900e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
1901e8599ff4SDavid S. Miller 	struct neighbour *neigh;
190271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
19036e157b6aSDavid S. Miller 	int optlen, on_link;
19046e157b6aSDavid S. Miller 	u8 *lladdr;
1905e8599ff4SDavid S. Miller 
190629a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
190771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
1908e8599ff4SDavid S. Miller 
1909e8599ff4SDavid S. Miller 	if (optlen < 0) {
19106e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
1911e8599ff4SDavid S. Miller 		return;
1912e8599ff4SDavid S. Miller 	}
1913e8599ff4SDavid S. Miller 
191471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
1915e8599ff4SDavid S. Miller 
191671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
19176e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
1918e8599ff4SDavid S. Miller 		return;
1919e8599ff4SDavid S. Miller 	}
1920e8599ff4SDavid S. Miller 
19216e157b6aSDavid S. Miller 	on_link = 0;
192271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
1923e8599ff4SDavid S. Miller 		on_link = 1;
192471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
1925e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
19266e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
1927e8599ff4SDavid S. Miller 		return;
1928e8599ff4SDavid S. Miller 	}
1929e8599ff4SDavid S. Miller 
1930e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
1931e8599ff4SDavid S. Miller 	if (!in6_dev)
1932e8599ff4SDavid S. Miller 		return;
1933e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1934e8599ff4SDavid S. Miller 		return;
1935e8599ff4SDavid S. Miller 
1936e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
1937e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
1938e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
1939e8599ff4SDavid S. Miller 	 */
1940e8599ff4SDavid S. Miller 
194171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
1942e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1943e8599ff4SDavid S. Miller 		return;
1944e8599ff4SDavid S. Miller 	}
19456e157b6aSDavid S. Miller 
19466e157b6aSDavid S. Miller 	lladdr = NULL;
1947e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
1948e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1949e8599ff4SDavid S. Miller 					     skb->dev);
1950e8599ff4SDavid S. Miller 		if (!lladdr) {
1951e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1952e8599ff4SDavid S. Miller 			return;
1953e8599ff4SDavid S. Miller 		}
1954e8599ff4SDavid S. Miller 	}
1955e8599ff4SDavid S. Miller 
19566e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
19576e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
19586e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
19596e157b6aSDavid S. Miller 		return;
19606e157b6aSDavid S. Miller 	}
19616e157b6aSDavid S. Miller 
19626e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
19636e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
19646e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
19656e157b6aSDavid S. Miller 	 */
19666e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
19676e157b6aSDavid S. Miller 
196871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
1969e8599ff4SDavid S. Miller 	if (!neigh)
1970e8599ff4SDavid S. Miller 		return;
1971e8599ff4SDavid S. Miller 
19721da177e4SLinus Torvalds 	/*
19731da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
19741da177e4SLinus Torvalds 	 */
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
19771da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
19781da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
19791da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
19801da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
19811da177e4SLinus Torvalds 		     );
19821da177e4SLinus Torvalds 
198371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	nrt = ip6_rt_copy(rt, &msg->dest);
198438308473SDavid S. Miller 	if (!nrt)
19851da177e4SLinus Torvalds 		goto out;
19861da177e4SLinus Torvalds 
19871da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
19881da177e4SLinus Torvalds 	if (on_link)
19891da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
19901da177e4SLinus Torvalds 
19914e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
19921da177e4SLinus Torvalds 
199340e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
19941da177e4SLinus Torvalds 		goto out;
19951da177e4SLinus Torvalds 
1996d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
1997d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
199871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
199960592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
20008d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
20018d71740cSTom Tucker 
20021da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
20036e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
2004e0a1ad73SThomas Graf 		ip6_del_rt(rt);
20051da177e4SLinus Torvalds 	}
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds out:
2008e8599ff4SDavid S. Miller 	neigh_release(neigh);
20096e157b6aSDavid S. Miller }
20106e157b6aSDavid S. Miller 
20111da177e4SLinus Torvalds /*
20121da177e4SLinus Torvalds  *	Misc support functions
20131da177e4SLinus Torvalds  */
20141da177e4SLinus Torvalds 
20154b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
20164b32b5adSMartin KaFai Lau {
20174b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
20184b32b5adSMartin KaFai Lau 
20194b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
20204b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
20214b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
20224b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
20234b32b5adSMartin KaFai Lau }
20244b32b5adSMartin KaFai Lau 
20251716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
202621efcfa0SEric Dumazet 				    const struct in6_addr *dest)
20271da177e4SLinus Torvalds {
2028d1918542SDavid S. Miller 	struct net *net = dev_net(ort->dst.dev);
20294b32b5adSMartin KaFai Lau 	struct rt6_info *rt;
20304b32b5adSMartin KaFai Lau 
20314b32b5adSMartin KaFai Lau 	if (ort->rt6i_flags & RTF_CACHE)
20324b32b5adSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
20334b32b5adSMartin KaFai Lau 
20344b32b5adSMartin KaFai Lau 	rt = ip6_dst_alloc(net, ort->dst.dev, 0,
20358b96d22dSDavid S. Miller 			   ort->rt6i_table);
20361da177e4SLinus Torvalds 
20371da177e4SLinus Torvalds 	if (rt) {
2038d8d1f30bSChangli Gao 		rt->dst.input = ort->dst.input;
2039d8d1f30bSChangli Gao 		rt->dst.output = ort->dst.output;
20408e2ec639SYan, Zheng 		rt->dst.flags |= DST_HOST;
20411da177e4SLinus Torvalds 
20424e3fd7a0SAlexey Dobriyan 		rt->rt6i_dst.addr = *dest;
20438e2ec639SYan, Zheng 		rt->rt6i_dst.plen = 128;
2044d8d1f30bSChangli Gao 		rt->dst.error = ort->dst.error;
20451da177e4SLinus Torvalds 		rt->rt6i_idev = ort->rt6i_idev;
20461da177e4SLinus Torvalds 		if (rt->rt6i_idev)
20471da177e4SLinus Torvalds 			in6_dev_hold(rt->rt6i_idev);
2048d8d1f30bSChangli Gao 		rt->dst.lastuse = jiffies;
20494e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
20501716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
20511716a961SGao feng 		rt6_set_from(rt, ort);
20521da177e4SLinus Torvalds 		rt->rt6i_metric = 0;
20531da177e4SLinus Torvalds 
20541da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
20551da177e4SLinus Torvalds 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
20561da177e4SLinus Torvalds #endif
20570f6c6392SFlorian Westphal 		memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
2058c71099acSThomas Graf 		rt->rt6i_table = ort->rt6i_table;
20591da177e4SLinus Torvalds 	}
20601da177e4SLinus Torvalds 	return rt;
20611da177e4SLinus Torvalds }
20621da177e4SLinus Torvalds 
206370ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
2064efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
2065b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2066b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
206770ceb4f5SYOSHIFUJI Hideaki {
206870ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
206970ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
2070c71099acSThomas Graf 	struct fib6_table *table;
207170ceb4f5SYOSHIFUJI Hideaki 
2072efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
207338308473SDavid S. Miller 	if (!table)
2074c71099acSThomas Graf 		return NULL;
2075c71099acSThomas Graf 
20765744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2077c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
207870ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
207970ceb4f5SYOSHIFUJI Hideaki 		goto out;
208070ceb4f5SYOSHIFUJI Hideaki 
2081d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2082d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
208370ceb4f5SYOSHIFUJI Hideaki 			continue;
208470ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
208570ceb4f5SYOSHIFUJI Hideaki 			continue;
208670ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
208770ceb4f5SYOSHIFUJI Hideaki 			continue;
2088d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
208970ceb4f5SYOSHIFUJI Hideaki 		break;
209070ceb4f5SYOSHIFUJI Hideaki 	}
209170ceb4f5SYOSHIFUJI Hideaki out:
20925744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
209370ceb4f5SYOSHIFUJI Hideaki 	return rt;
209470ceb4f5SYOSHIFUJI Hideaki }
209570ceb4f5SYOSHIFUJI Hideaki 
2096efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2097b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2098b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
209995c96174SEric Dumazet 					   unsigned int pref)
210070ceb4f5SYOSHIFUJI Hideaki {
210186872cb5SThomas Graf 	struct fib6_config cfg = {
210286872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
2103238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
210486872cb5SThomas Graf 		.fc_ifindex	= ifindex,
210586872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
210686872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
210786872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
210815e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2109efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2110efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
211186872cb5SThomas Graf 	};
211270ceb4f5SYOSHIFUJI Hideaki 
21134e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
21144e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
211586872cb5SThomas Graf 
2116e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2117e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
211886872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
211970ceb4f5SYOSHIFUJI Hideaki 
212086872cb5SThomas Graf 	ip6_route_add(&cfg);
212170ceb4f5SYOSHIFUJI Hideaki 
2122efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
212370ceb4f5SYOSHIFUJI Hideaki }
212470ceb4f5SYOSHIFUJI Hideaki #endif
212570ceb4f5SYOSHIFUJI Hideaki 
2126b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
21271da177e4SLinus Torvalds {
21281da177e4SLinus Torvalds 	struct rt6_info *rt;
2129c71099acSThomas Graf 	struct fib6_table *table;
21301da177e4SLinus Torvalds 
2131c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
213238308473SDavid S. Miller 	if (!table)
2133c71099acSThomas Graf 		return NULL;
21341da177e4SLinus Torvalds 
21355744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2136d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2137d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2138045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
21391da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
21401da177e4SLinus Torvalds 			break;
21411da177e4SLinus Torvalds 	}
21421da177e4SLinus Torvalds 	if (rt)
2143d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
21445744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
21451da177e4SLinus Torvalds 	return rt;
21461da177e4SLinus Torvalds }
21471da177e4SLinus Torvalds 
2148b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2149ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2150ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
21511da177e4SLinus Torvalds {
215286872cb5SThomas Graf 	struct fib6_config cfg = {
215386872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
2154238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
215586872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
215686872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
215786872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
215815e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
21595578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2160c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
216186872cb5SThomas Graf 	};
21621da177e4SLinus Torvalds 
21634e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
21641da177e4SLinus Torvalds 
216586872cb5SThomas Graf 	ip6_route_add(&cfg);
21661da177e4SLinus Torvalds 
21671da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
21681da177e4SLinus Torvalds }
21691da177e4SLinus Torvalds 
21707b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
21711da177e4SLinus Torvalds {
21721da177e4SLinus Torvalds 	struct rt6_info *rt;
2173c71099acSThomas Graf 	struct fib6_table *table;
2174c71099acSThomas Graf 
2175c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
21767b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
217738308473SDavid S. Miller 	if (!table)
2178c71099acSThomas Graf 		return;
21791da177e4SLinus Torvalds 
21801da177e4SLinus Torvalds restart:
2181c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2182d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
21833e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
21843e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2185d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2186c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2187e0a1ad73SThomas Graf 			ip6_del_rt(rt);
21881da177e4SLinus Torvalds 			goto restart;
21891da177e4SLinus Torvalds 		}
21901da177e4SLinus Torvalds 	}
2191c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
21921da177e4SLinus Torvalds }
21931da177e4SLinus Torvalds 
21945578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
21955578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
219686872cb5SThomas Graf 				 struct fib6_config *cfg)
219786872cb5SThomas Graf {
219886872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
219986872cb5SThomas Graf 
220086872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
220186872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
220286872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
220386872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
220486872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
220586872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
220686872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
220786872cb5SThomas Graf 
22085578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2209f1243c2dSBenjamin Thery 
22104e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
22114e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
22124e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
221386872cb5SThomas Graf }
221486872cb5SThomas Graf 
22155578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
22161da177e4SLinus Torvalds {
221786872cb5SThomas Graf 	struct fib6_config cfg;
22181da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
22191da177e4SLinus Torvalds 	int err;
22201da177e4SLinus Torvalds 
22211da177e4SLinus Torvalds 	switch (cmd) {
22221da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
22231da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2224af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
22251da177e4SLinus Torvalds 			return -EPERM;
22261da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
22271da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
22281da177e4SLinus Torvalds 		if (err)
22291da177e4SLinus Torvalds 			return -EFAULT;
22301da177e4SLinus Torvalds 
22315578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
223286872cb5SThomas Graf 
22331da177e4SLinus Torvalds 		rtnl_lock();
22341da177e4SLinus Torvalds 		switch (cmd) {
22351da177e4SLinus Torvalds 		case SIOCADDRT:
223686872cb5SThomas Graf 			err = ip6_route_add(&cfg);
22371da177e4SLinus Torvalds 			break;
22381da177e4SLinus Torvalds 		case SIOCDELRT:
223986872cb5SThomas Graf 			err = ip6_route_del(&cfg);
22401da177e4SLinus Torvalds 			break;
22411da177e4SLinus Torvalds 		default:
22421da177e4SLinus Torvalds 			err = -EINVAL;
22431da177e4SLinus Torvalds 		}
22441da177e4SLinus Torvalds 		rtnl_unlock();
22451da177e4SLinus Torvalds 
22461da177e4SLinus Torvalds 		return err;
22473ff50b79SStephen Hemminger 	}
22481da177e4SLinus Torvalds 
22491da177e4SLinus Torvalds 	return -EINVAL;
22501da177e4SLinus Torvalds }
22511da177e4SLinus Torvalds 
22521da177e4SLinus Torvalds /*
22531da177e4SLinus Torvalds  *	Drop the packet on the floor
22541da177e4SLinus Torvalds  */
22551da177e4SLinus Torvalds 
2256d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
22571da177e4SLinus Torvalds {
2258612f09e8SYOSHIFUJI Hideaki 	int type;
2259adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2260612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2261612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
22620660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
226345bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
22643bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
22653bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2266612f09e8SYOSHIFUJI Hideaki 			break;
2267612f09e8SYOSHIFUJI Hideaki 		}
2268612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2269612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
22703bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
22713bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2272612f09e8SYOSHIFUJI Hideaki 		break;
2273612f09e8SYOSHIFUJI Hideaki 	}
22743ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
22751da177e4SLinus Torvalds 	kfree_skb(skb);
22761da177e4SLinus Torvalds 	return 0;
22771da177e4SLinus Torvalds }
22781da177e4SLinus Torvalds 
22799ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
22809ce8ade0SThomas Graf {
2281612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
22829ce8ade0SThomas Graf }
22839ce8ade0SThomas Graf 
2284aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb)
22851da177e4SLinus Torvalds {
2286adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2287612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
22881da177e4SLinus Torvalds }
22891da177e4SLinus Torvalds 
22909ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
22919ce8ade0SThomas Graf {
2292612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
22939ce8ade0SThomas Graf }
22949ce8ade0SThomas Graf 
2295aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb)
22969ce8ade0SThomas Graf {
2297adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2298612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
22999ce8ade0SThomas Graf }
23009ce8ade0SThomas Graf 
23011da177e4SLinus Torvalds /*
23021da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
23031da177e4SLinus Torvalds  */
23041da177e4SLinus Torvalds 
23051da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
23061da177e4SLinus Torvalds 				    const struct in6_addr *addr,
23078f031519SDavid S. Miller 				    bool anycast)
23081da177e4SLinus Torvalds {
2309c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2310a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2311a3300ef4SHannes Frederic Sowa 					    DST_NOCOUNT, NULL);
2312a3300ef4SHannes Frederic Sowa 	if (!rt)
23131da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
23141da177e4SLinus Torvalds 
23151da177e4SLinus Torvalds 	in6_dev_hold(idev);
23161da177e4SLinus Torvalds 
231711d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2318d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2319d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
23201da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
23211da177e4SLinus Torvalds 
23221da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
232358c4fb86SYOSHIFUJI Hideaki 	if (anycast)
232458c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
232558c4fb86SYOSHIFUJI Hideaki 	else
23261da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
23271da177e4SLinus Torvalds 
2328550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
23294e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
23301da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
23315578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
23321da177e4SLinus Torvalds 
2333d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
23341da177e4SLinus Torvalds 
23351da177e4SLinus Torvalds 	return rt;
23361da177e4SLinus Torvalds }
23371da177e4SLinus Torvalds 
2338c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2339c3968a85SDaniel Walter 			struct rt6_info *rt,
2340b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2341c3968a85SDaniel Walter 			unsigned int prefs,
2342c3968a85SDaniel Walter 			struct in6_addr *saddr)
2343c3968a85SDaniel Walter {
2344e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2345e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2346c3968a85SDaniel Walter 	int err = 0;
2347e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
23484e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2349c3968a85SDaniel Walter 	else
2350c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2351c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2352c3968a85SDaniel Walter 	return err;
2353c3968a85SDaniel Walter }
2354c3968a85SDaniel Walter 
2355c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2356c3968a85SDaniel Walter struct arg_dev_net_ip {
2357c3968a85SDaniel Walter 	struct net_device *dev;
2358c3968a85SDaniel Walter 	struct net *net;
2359c3968a85SDaniel Walter 	struct in6_addr *addr;
2360c3968a85SDaniel Walter };
2361c3968a85SDaniel Walter 
2362c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2363c3968a85SDaniel Walter {
2364c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2365c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2366c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2367c3968a85SDaniel Walter 
2368d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2369c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2370c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2371c3968a85SDaniel Walter 		/* remove prefsrc entry */
2372c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2373c3968a85SDaniel Walter 	}
2374c3968a85SDaniel Walter 	return 0;
2375c3968a85SDaniel Walter }
2376c3968a85SDaniel Walter 
2377c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2378c3968a85SDaniel Walter {
2379c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2380c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2381c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2382c3968a85SDaniel Walter 		.net = net,
2383c3968a85SDaniel Walter 		.addr = &ifp->addr,
2384c3968a85SDaniel Walter 	};
23850c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2386c3968a85SDaniel Walter }
2387c3968a85SDaniel Walter 
2388be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2389be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2390be7a010dSDuan Jiong 
2391be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2392be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2393be7a010dSDuan Jiong {
2394be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2395be7a010dSDuan Jiong 
2396be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2397be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2398be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2399be7a010dSDuan Jiong 		return -1;
2400be7a010dSDuan Jiong 	}
2401be7a010dSDuan Jiong 	return 0;
2402be7a010dSDuan Jiong }
2403be7a010dSDuan Jiong 
2404be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2405be7a010dSDuan Jiong {
2406be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2407be7a010dSDuan Jiong }
2408be7a010dSDuan Jiong 
24098ed67789SDaniel Lezcano struct arg_dev_net {
24108ed67789SDaniel Lezcano 	struct net_device *dev;
24118ed67789SDaniel Lezcano 	struct net *net;
24128ed67789SDaniel Lezcano };
24138ed67789SDaniel Lezcano 
24141da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
24151da177e4SLinus Torvalds {
2416bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2417bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
24188ed67789SDaniel Lezcano 
2419d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2420c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
24211da177e4SLinus Torvalds 		return -1;
2422c159d30cSDavid S. Miller 
24231da177e4SLinus Torvalds 	return 0;
24241da177e4SLinus Torvalds }
24251da177e4SLinus Torvalds 
2426f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
24271da177e4SLinus Torvalds {
24288ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
24298ed67789SDaniel Lezcano 		.dev = dev,
24308ed67789SDaniel Lezcano 		.net = net,
24318ed67789SDaniel Lezcano 	};
24328ed67789SDaniel Lezcano 
24330c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
24341e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
2435*8d0b94afSMartin KaFai Lau 	rt6_uncached_list_flush_dev(net, dev);
24361da177e4SLinus Torvalds }
24371da177e4SLinus Torvalds 
243895c96174SEric Dumazet struct rt6_mtu_change_arg {
24391da177e4SLinus Torvalds 	struct net_device *dev;
244095c96174SEric Dumazet 	unsigned int mtu;
24411da177e4SLinus Torvalds };
24421da177e4SLinus Torvalds 
24431da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
24441da177e4SLinus Torvalds {
24451da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
24461da177e4SLinus Torvalds 	struct inet6_dev *idev;
24471da177e4SLinus Torvalds 
24481da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
24491da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
24501da177e4SLinus Torvalds 	   We still use this lock to block changes
24511da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
24521da177e4SLinus Torvalds 	*/
24531da177e4SLinus Torvalds 
24541da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
245538308473SDavid S. Miller 	if (!idev)
24561da177e4SLinus Torvalds 		return 0;
24571da177e4SLinus Torvalds 
24581da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
24591da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
24601da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
24611da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
24621da177e4SLinus Torvalds 	 */
24631da177e4SLinus Torvalds 	/*
24641da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
24651da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
24661da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
24671da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
24681da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
24691da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
24701da177e4SLinus Torvalds 	   PMTU discouvery.
24711da177e4SLinus Torvalds 	 */
2472d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
24734b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
24744b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
24754b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
24764b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
24774b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
24784b32b5adSMartin KaFai Lau 			 * been updated.
24794b32b5adSMartin KaFai Lau 			 */
24804b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
24814b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
24824b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2483d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
24844b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2485defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2486566cfd8fSSimon Arlott 		}
24874b32b5adSMartin KaFai Lau 	}
24881da177e4SLinus Torvalds 	return 0;
24891da177e4SLinus Torvalds }
24901da177e4SLinus Torvalds 
249195c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
24921da177e4SLinus Torvalds {
2493c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2494c71099acSThomas Graf 		.dev = dev,
2495c71099acSThomas Graf 		.mtu = mtu,
2496c71099acSThomas Graf 	};
24971da177e4SLinus Torvalds 
24980c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
24991da177e4SLinus Torvalds }
25001da177e4SLinus Torvalds 
2501ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
25025176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
250386872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2504ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
250586872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
250686872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
250751ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2508c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
250986872cb5SThomas Graf };
251086872cb5SThomas Graf 
251186872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
251286872cb5SThomas Graf 			      struct fib6_config *cfg)
25131da177e4SLinus Torvalds {
251486872cb5SThomas Graf 	struct rtmsg *rtm;
251586872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2516c78ba6d6SLubomir Rintel 	unsigned int pref;
251786872cb5SThomas Graf 	int err;
25181da177e4SLinus Torvalds 
251986872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
252086872cb5SThomas Graf 	if (err < 0)
252186872cb5SThomas Graf 		goto errout;
25221da177e4SLinus Torvalds 
252386872cb5SThomas Graf 	err = -EINVAL;
252486872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
252586872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
252686872cb5SThomas Graf 
252786872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
252886872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
252986872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
253086872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
253186872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2532ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
253386872cb5SThomas Graf 
2534ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2535ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2536b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2537b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
253886872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
253986872cb5SThomas Graf 
2540ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2541ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2542ab79ad14SMaciej Żenczykowski 
25431f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
25441f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
25451f56a01fSMartin KaFai Lau 
254615e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
254786872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
25483b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
254986872cb5SThomas Graf 
255086872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
255167b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
255286872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
25531da177e4SLinus Torvalds 	}
255486872cb5SThomas Graf 
255586872cb5SThomas Graf 	if (tb[RTA_DST]) {
255686872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
255786872cb5SThomas Graf 
255886872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
255986872cb5SThomas Graf 			goto errout;
256086872cb5SThomas Graf 
256186872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
25621da177e4SLinus Torvalds 	}
256386872cb5SThomas Graf 
256486872cb5SThomas Graf 	if (tb[RTA_SRC]) {
256586872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
256686872cb5SThomas Graf 
256786872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
256886872cb5SThomas Graf 			goto errout;
256986872cb5SThomas Graf 
257086872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
25711da177e4SLinus Torvalds 	}
257286872cb5SThomas Graf 
2573c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
257467b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2575c3968a85SDaniel Walter 
257686872cb5SThomas Graf 	if (tb[RTA_OIF])
257786872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
257886872cb5SThomas Graf 
257986872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
258086872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
258186872cb5SThomas Graf 
258286872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
258386872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
258486872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
25851da177e4SLinus Torvalds 	}
258686872cb5SThomas Graf 
258786872cb5SThomas Graf 	if (tb[RTA_TABLE])
258886872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
258986872cb5SThomas Graf 
259051ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
259151ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
259251ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
259351ebd318SNicolas Dichtel 	}
259451ebd318SNicolas Dichtel 
2595c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2596c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2597c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2598c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2599c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2600c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2601c78ba6d6SLubomir Rintel 	}
2602c78ba6d6SLubomir Rintel 
260386872cb5SThomas Graf 	err = 0;
260486872cb5SThomas Graf errout:
260586872cb5SThomas Graf 	return err;
26061da177e4SLinus Torvalds }
26071da177e4SLinus Torvalds 
260851ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add)
260951ebd318SNicolas Dichtel {
261051ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
261151ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
261251ebd318SNicolas Dichtel 	int remaining;
261351ebd318SNicolas Dichtel 	int attrlen;
261451ebd318SNicolas Dichtel 	int err = 0, last_err = 0;
261551ebd318SNicolas Dichtel 
261635f1b4e9SMichal Kubeček 	remaining = cfg->fc_mp_len;
261751ebd318SNicolas Dichtel beginning:
261851ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
261951ebd318SNicolas Dichtel 
262051ebd318SNicolas Dichtel 	/* Parse a Multipath Entry */
262151ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
262251ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
262351ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
262451ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
262551ebd318SNicolas Dichtel 
262651ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
262751ebd318SNicolas Dichtel 		if (attrlen > 0) {
262851ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
262951ebd318SNicolas Dichtel 
263051ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
263151ebd318SNicolas Dichtel 			if (nla) {
263267b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
263351ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
263451ebd318SNicolas Dichtel 			}
263551ebd318SNicolas Dichtel 		}
263651ebd318SNicolas Dichtel 		err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
263751ebd318SNicolas Dichtel 		if (err) {
263851ebd318SNicolas Dichtel 			last_err = err;
263951ebd318SNicolas Dichtel 			/* If we are trying to remove a route, do not stop the
264051ebd318SNicolas Dichtel 			 * loop when ip6_route_del() fails (because next hop is
264151ebd318SNicolas Dichtel 			 * already gone), we should try to remove all next hops.
264251ebd318SNicolas Dichtel 			 */
264351ebd318SNicolas Dichtel 			if (add) {
264451ebd318SNicolas Dichtel 				/* If add fails, we should try to delete all
264551ebd318SNicolas Dichtel 				 * next hops that have been already added.
264651ebd318SNicolas Dichtel 				 */
264751ebd318SNicolas Dichtel 				add = 0;
264835f1b4e9SMichal Kubeček 				remaining = cfg->fc_mp_len - remaining;
264951ebd318SNicolas Dichtel 				goto beginning;
265051ebd318SNicolas Dichtel 			}
265151ebd318SNicolas Dichtel 		}
26521a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
265327596472SMichal Kubeček 		 * these flags after the first nexthop: if there is a collision,
265427596472SMichal Kubeček 		 * we have already failed to add the first nexthop:
265527596472SMichal Kubeček 		 * fib6_add_rt2node() has rejected it; when replacing, old
265627596472SMichal Kubeček 		 * nexthops have been replaced by first new, the rest should
265727596472SMichal Kubeček 		 * be added to it.
26581a72418bSNicolas Dichtel 		 */
265927596472SMichal Kubeček 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
266027596472SMichal Kubeček 						     NLM_F_REPLACE);
266151ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
266251ebd318SNicolas Dichtel 	}
266351ebd318SNicolas Dichtel 
266451ebd318SNicolas Dichtel 	return last_err;
266551ebd318SNicolas Dichtel }
266651ebd318SNicolas Dichtel 
2667661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
26681da177e4SLinus Torvalds {
266986872cb5SThomas Graf 	struct fib6_config cfg;
267086872cb5SThomas Graf 	int err;
26711da177e4SLinus Torvalds 
267286872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
267386872cb5SThomas Graf 	if (err < 0)
267486872cb5SThomas Graf 		return err;
267586872cb5SThomas Graf 
267651ebd318SNicolas Dichtel 	if (cfg.fc_mp)
267751ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 0);
267851ebd318SNicolas Dichtel 	else
267986872cb5SThomas Graf 		return ip6_route_del(&cfg);
26801da177e4SLinus Torvalds }
26811da177e4SLinus Torvalds 
2682661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
26831da177e4SLinus Torvalds {
268486872cb5SThomas Graf 	struct fib6_config cfg;
268586872cb5SThomas Graf 	int err;
26861da177e4SLinus Torvalds 
268786872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
268886872cb5SThomas Graf 	if (err < 0)
268986872cb5SThomas Graf 		return err;
269086872cb5SThomas Graf 
269151ebd318SNicolas Dichtel 	if (cfg.fc_mp)
269251ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 1);
269351ebd318SNicolas Dichtel 	else
269486872cb5SThomas Graf 		return ip6_route_add(&cfg);
26951da177e4SLinus Torvalds }
26961da177e4SLinus Torvalds 
2697339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void)
2698339bf98fSThomas Graf {
2699339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2700339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2701339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2702339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2703339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2704339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2705339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2706339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2707339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
27086a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2709ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
2710c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
2711c78ba6d6SLubomir Rintel 	       + nla_total_size(1); /* RTA_PREF */
2712339bf98fSThomas Graf }
2713339bf98fSThomas Graf 
2714191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2715191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
27160d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
271715e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
27187bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
27191da177e4SLinus Torvalds {
27204b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
27211da177e4SLinus Torvalds 	struct rtmsg *rtm;
27221da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2723e3703b3dSThomas Graf 	long expires;
27249e762a4aSPatrick McHardy 	u32 table;
27251da177e4SLinus Torvalds 
27261da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
27271da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
27281da177e4SLinus Torvalds 			/* success since this is not a prefix route */
27291da177e4SLinus Torvalds 			return 1;
27301da177e4SLinus Torvalds 		}
27311da177e4SLinus Torvalds 	}
27321da177e4SLinus Torvalds 
273315e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
273438308473SDavid S. Miller 	if (!nlh)
273526932566SPatrick McHardy 		return -EMSGSIZE;
27362d7202bfSThomas Graf 
27372d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
27381da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
27391da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
27401da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
27411da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2742c71099acSThomas Graf 	if (rt->rt6i_table)
27439e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2744c71099acSThomas Graf 	else
27459e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
27469e762a4aSPatrick McHardy 	rtm->rtm_table = table;
2747c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
2748c78679e8SDavid S. Miller 		goto nla_put_failure;
2749ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
2750ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
2751ef2c7d7bSNicolas Dichtel 		case -EINVAL:
2752ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
2753ef2c7d7bSNicolas Dichtel 			break;
2754ef2c7d7bSNicolas Dichtel 		case -EACCES:
2755ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
2756ef2c7d7bSNicolas Dichtel 			break;
2757b4949ab2SNicolas Dichtel 		case -EAGAIN:
2758b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
2759b4949ab2SNicolas Dichtel 			break;
2760ef2c7d7bSNicolas Dichtel 		default:
27611da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
2762ef2c7d7bSNicolas Dichtel 			break;
2763ef2c7d7bSNicolas Dichtel 		}
2764ef2c7d7bSNicolas Dichtel 	}
2765ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2766ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2767d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
27681da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
27691da177e4SLinus Torvalds 	else
27701da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
27711da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
27721da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
27731da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
27741da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
27751da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
2776f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
2777f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
27781da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
2779f0396f60SDenis Ovsienko 		else
2780f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
2781f0396f60SDenis Ovsienko 	}
27821da177e4SLinus Torvalds 
27831da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
27841da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
27851da177e4SLinus Torvalds 
27861da177e4SLinus Torvalds 	if (dst) {
2787930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
2788c78679e8SDavid S. Miller 			goto nla_put_failure;
27891da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
27901da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
2791930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
2792c78679e8SDavid S. Miller 			goto nla_put_failure;
27931da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
27941da177e4SLinus Torvalds 	if (src) {
2795930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
2796c78679e8SDavid S. Miller 			goto nla_put_failure;
27971da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
2798c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
2799930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
2800c78679e8SDavid S. Miller 		goto nla_put_failure;
28011da177e4SLinus Torvalds #endif
28027bc570c8SYOSHIFUJI Hideaki 	if (iif) {
28037bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
28047bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
28058229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
28067bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
28077bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
28087bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
28097bc570c8SYOSHIFUJI Hideaki 						return 0;
28107bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
28117bc570c8SYOSHIFUJI Hideaki 				} else {
28127bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
28137bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
28147bc570c8SYOSHIFUJI Hideaki 				}
28157bc570c8SYOSHIFUJI Hideaki 			}
28167bc570c8SYOSHIFUJI Hideaki 		} else
28177bc570c8SYOSHIFUJI Hideaki #endif
2818c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
2819c78679e8SDavid S. Miller 				goto nla_put_failure;
28207bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
28211da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2822c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2823930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2824c78679e8SDavid S. Miller 			goto nla_put_failure;
2825c3968a85SDaniel Walter 	}
2826c3968a85SDaniel Walter 
2827c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2828c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
28294e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
2830930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2831c78679e8SDavid S. Miller 			goto nla_put_failure;
28321da177e4SLinus Torvalds 	}
28332d7202bfSThomas Graf 
28344b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
28354b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
28364b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
28374b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
28382d7202bfSThomas Graf 		goto nla_put_failure;
28392d7202bfSThomas Graf 
2840dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
2841930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
284294f826b8SEric Dumazet 			goto nla_put_failure;
284394f826b8SEric Dumazet 	}
28442d7202bfSThomas Graf 
2845c78679e8SDavid S. Miller 	if (rt->dst.dev &&
2846c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2847c78679e8SDavid S. Miller 		goto nla_put_failure;
2848c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2849c78679e8SDavid S. Miller 		goto nla_put_failure;
28508253947eSLi Wei 
28518253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
285269cdf8f9SYOSHIFUJI Hideaki 
285387a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
2854e3703b3dSThomas Graf 		goto nla_put_failure;
28551da177e4SLinus Torvalds 
2856c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
2857c78ba6d6SLubomir Rintel 		goto nla_put_failure;
2858c78ba6d6SLubomir Rintel 
2859053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2860053c095aSJohannes Berg 	return 0;
28612d7202bfSThomas Graf 
28622d7202bfSThomas Graf nla_put_failure:
286326932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
286426932566SPatrick McHardy 	return -EMSGSIZE;
28651da177e4SLinus Torvalds }
28661da177e4SLinus Torvalds 
28671b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
28681da177e4SLinus Torvalds {
28691da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
28701da177e4SLinus Torvalds 	int prefix;
28711da177e4SLinus Torvalds 
28722d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
28732d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
28741da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
28751da177e4SLinus Torvalds 	} else
28761da177e4SLinus Torvalds 		prefix = 0;
28771da177e4SLinus Torvalds 
2878191cd582SBrian Haley 	return rt6_fill_node(arg->net,
2879191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
288015e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
28817bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
28821da177e4SLinus Torvalds }
28831da177e4SLinus Torvalds 
2884661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
28851da177e4SLinus Torvalds {
28863b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
2887ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
28881da177e4SLinus Torvalds 	struct rt6_info *rt;
2889ab364a6fSThomas Graf 	struct sk_buff *skb;
2890ab364a6fSThomas Graf 	struct rtmsg *rtm;
28914c9483b2SDavid S. Miller 	struct flowi6 fl6;
289272331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
2893ab364a6fSThomas Graf 
2894ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2895ab364a6fSThomas Graf 	if (err < 0)
2896ab364a6fSThomas Graf 		goto errout;
2897ab364a6fSThomas Graf 
2898ab364a6fSThomas Graf 	err = -EINVAL;
28994c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2900ab364a6fSThomas Graf 
2901ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
2902ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2903ab364a6fSThomas Graf 			goto errout;
2904ab364a6fSThomas Graf 
29054e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
2906ab364a6fSThomas Graf 	}
2907ab364a6fSThomas Graf 
2908ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
2909ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2910ab364a6fSThomas Graf 			goto errout;
2911ab364a6fSThomas Graf 
29124e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
2913ab364a6fSThomas Graf 	}
2914ab364a6fSThomas Graf 
2915ab364a6fSThomas Graf 	if (tb[RTA_IIF])
2916ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
2917ab364a6fSThomas Graf 
2918ab364a6fSThomas Graf 	if (tb[RTA_OIF])
291972331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
2920ab364a6fSThomas Graf 
29212e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
29222e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
29232e47b291SLorenzo Colitti 
2924ab364a6fSThomas Graf 	if (iif) {
2925ab364a6fSThomas Graf 		struct net_device *dev;
292672331bc0SShmulik Ladkani 		int flags = 0;
292772331bc0SShmulik Ladkani 
29285578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
2929ab364a6fSThomas Graf 		if (!dev) {
2930ab364a6fSThomas Graf 			err = -ENODEV;
2931ab364a6fSThomas Graf 			goto errout;
2932ab364a6fSThomas Graf 		}
293372331bc0SShmulik Ladkani 
293472331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
293572331bc0SShmulik Ladkani 
293672331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
293772331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
293872331bc0SShmulik Ladkani 
293972331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
294072331bc0SShmulik Ladkani 							       flags);
294172331bc0SShmulik Ladkani 	} else {
294272331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
294372331bc0SShmulik Ladkani 
294472331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
2945ab364a6fSThomas Graf 	}
29461da177e4SLinus Torvalds 
29471da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
294838308473SDavid S. Miller 	if (!skb) {
294994e187c0SAmerigo Wang 		ip6_rt_put(rt);
2950ab364a6fSThomas Graf 		err = -ENOBUFS;
2951ab364a6fSThomas Graf 		goto errout;
2952ab364a6fSThomas Graf 	}
29531da177e4SLinus Torvalds 
29541da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
29551da177e4SLinus Torvalds 	   through good chunk of routing engine.
29561da177e4SLinus Torvalds 	 */
2957459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
29581da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
29591da177e4SLinus Torvalds 
2960d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
29611da177e4SLinus Torvalds 
29624c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
296315e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
29647bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
29651da177e4SLinus Torvalds 	if (err < 0) {
2966ab364a6fSThomas Graf 		kfree_skb(skb);
2967ab364a6fSThomas Graf 		goto errout;
29681da177e4SLinus Torvalds 	}
29691da177e4SLinus Torvalds 
297015e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
2971ab364a6fSThomas Graf errout:
29721da177e4SLinus Torvalds 	return err;
29731da177e4SLinus Torvalds }
29741da177e4SLinus Torvalds 
297586872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
29761da177e4SLinus Torvalds {
29771da177e4SLinus Torvalds 	struct sk_buff *skb;
29785578689aSDaniel Lezcano 	struct net *net = info->nl_net;
2979528c4cebSDenis V. Lunev 	u32 seq;
2980528c4cebSDenis V. Lunev 	int err;
29810d51aa80SJamal Hadi Salim 
2982528c4cebSDenis V. Lunev 	err = -ENOBUFS;
298338308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
298486872cb5SThomas Graf 
2985339bf98fSThomas Graf 	skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
298638308473SDavid S. Miller 	if (!skb)
298721713ebcSThomas Graf 		goto errout;
29881da177e4SLinus Torvalds 
2989191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
299015e47304SEric W. Biederman 				event, info->portid, seq, 0, 0, 0);
299126932566SPatrick McHardy 	if (err < 0) {
299226932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
299326932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
299426932566SPatrick McHardy 		kfree_skb(skb);
299526932566SPatrick McHardy 		goto errout;
299626932566SPatrick McHardy 	}
299715e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
29985578689aSDaniel Lezcano 		    info->nlh, gfp_any());
29991ce85fe4SPablo Neira Ayuso 	return;
300021713ebcSThomas Graf errout:
300121713ebcSThomas Graf 	if (err < 0)
30025578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
30031da177e4SLinus Torvalds }
30041da177e4SLinus Torvalds 
30058ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
3006351638e7SJiri Pirko 				unsigned long event, void *ptr)
30078ed67789SDaniel Lezcano {
3008351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3009c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
30108ed67789SDaniel Lezcano 
30118ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
3012d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
30138ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
30148ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3015d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
30168ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
3017d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
30188ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
30198ed67789SDaniel Lezcano #endif
30208ed67789SDaniel Lezcano 	}
30218ed67789SDaniel Lezcano 
30228ed67789SDaniel Lezcano 	return NOTIFY_OK;
30238ed67789SDaniel Lezcano }
30248ed67789SDaniel Lezcano 
30251da177e4SLinus Torvalds /*
30261da177e4SLinus Torvalds  *	/proc
30271da177e4SLinus Torvalds  */
30281da177e4SLinus Torvalds 
30291da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
30301da177e4SLinus Torvalds 
303133120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
303233120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
303333120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
303433120b30SAlexey Dobriyan 	.read		= seq_read,
303533120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
30368d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
303733120b30SAlexey Dobriyan };
303833120b30SAlexey Dobriyan 
30391da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
30401da177e4SLinus Torvalds {
304169ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
30421da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
304369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
304469ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
304569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
304669ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
304769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
3048fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
304969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
30501da177e4SLinus Torvalds 
30511da177e4SLinus Torvalds 	return 0;
30521da177e4SLinus Torvalds }
30531da177e4SLinus Torvalds 
30541da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
30551da177e4SLinus Torvalds {
3056de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
305769ddb805SDaniel Lezcano }
305869ddb805SDaniel Lezcano 
30599a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
30601da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
30611da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
30621da177e4SLinus Torvalds 	.read	 = seq_read,
30631da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
3064b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
30651da177e4SLinus Torvalds };
30661da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
30671da177e4SLinus Torvalds 
30681da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
30691da177e4SLinus Torvalds 
30701da177e4SLinus Torvalds static
3071fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
30721da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
30731da177e4SLinus Torvalds {
3074c486da34SLucian Adrian Grijincu 	struct net *net;
3075c486da34SLucian Adrian Grijincu 	int delay;
3076c486da34SLucian Adrian Grijincu 	if (!write)
3077c486da34SLucian Adrian Grijincu 		return -EINVAL;
3078c486da34SLucian Adrian Grijincu 
3079c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3080c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
30818d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
30822ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
30831da177e4SLinus Torvalds 	return 0;
30841da177e4SLinus Torvalds }
30851da177e4SLinus Torvalds 
3086fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
30871da177e4SLinus Torvalds 	{
30881da177e4SLinus Torvalds 		.procname	=	"flush",
30894990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
30901da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
309189c8b3a1SDave Jones 		.mode		=	0200,
30926d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
30931da177e4SLinus Torvalds 	},
30941da177e4SLinus Torvalds 	{
30951da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
30969a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
30971da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30981da177e4SLinus Torvalds 		.mode		=	0644,
30996d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
31001da177e4SLinus Torvalds 	},
31011da177e4SLinus Torvalds 	{
31021da177e4SLinus Torvalds 		.procname	=	"max_size",
31034990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
31041da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31051da177e4SLinus Torvalds 		.mode		=	0644,
31066d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
31071da177e4SLinus Torvalds 	},
31081da177e4SLinus Torvalds 	{
31091da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
31104990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
31111da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31121da177e4SLinus Torvalds 		.mode		=	0644,
31136d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
31141da177e4SLinus Torvalds 	},
31151da177e4SLinus Torvalds 	{
31161da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
31174990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
31181da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31191da177e4SLinus Torvalds 		.mode		=	0644,
31206d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
31211da177e4SLinus Torvalds 	},
31221da177e4SLinus Torvalds 	{
31231da177e4SLinus Torvalds 		.procname	=	"gc_interval",
31244990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
31251da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31261da177e4SLinus Torvalds 		.mode		=	0644,
31276d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
31281da177e4SLinus Torvalds 	},
31291da177e4SLinus Torvalds 	{
31301da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
31314990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
31321da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31331da177e4SLinus Torvalds 		.mode		=	0644,
3134f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
31351da177e4SLinus Torvalds 	},
31361da177e4SLinus Torvalds 	{
31371da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
31384990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
31391da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31401da177e4SLinus Torvalds 		.mode		=	0644,
31416d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
31421da177e4SLinus Torvalds 	},
31431da177e4SLinus Torvalds 	{
31441da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
31454990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
31461da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31471da177e4SLinus Torvalds 		.mode		=	0644,
3148f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
31491da177e4SLinus Torvalds 	},
31501da177e4SLinus Torvalds 	{
31511da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
31524990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
31531da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
31541da177e4SLinus Torvalds 		.mode		=	0644,
31556d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
31561da177e4SLinus Torvalds 	},
3157f8572d8fSEric W. Biederman 	{ }
31581da177e4SLinus Torvalds };
31591da177e4SLinus Torvalds 
31602c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3161760f2d01SDaniel Lezcano {
3162760f2d01SDaniel Lezcano 	struct ctl_table *table;
3163760f2d01SDaniel Lezcano 
3164760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3165760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3166760f2d01SDaniel Lezcano 			GFP_KERNEL);
31675ee09105SYOSHIFUJI Hideaki 
31685ee09105SYOSHIFUJI Hideaki 	if (table) {
31695ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3170c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
317186393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
31725ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
31735ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
31745ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
31755ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
31765ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
31775ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
31785ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
31799c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3180464dc801SEric W. Biederman 
3181464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3182464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3183464dc801SEric W. Biederman 			table[0].procname = NULL;
31845ee09105SYOSHIFUJI Hideaki 	}
31855ee09105SYOSHIFUJI Hideaki 
3186760f2d01SDaniel Lezcano 	return table;
3187760f2d01SDaniel Lezcano }
31881da177e4SLinus Torvalds #endif
31891da177e4SLinus Torvalds 
31902c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3191cdb18761SDaniel Lezcano {
3192633d424bSPavel Emelyanov 	int ret = -ENOMEM;
31938ed67789SDaniel Lezcano 
319486393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
319586393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3196f2fc6a54SBenjamin Thery 
3197fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3198fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3199fc66f95cSEric Dumazet 
32008ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
32018ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
32028ed67789SDaniel Lezcano 					   GFP_KERNEL);
32038ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3204fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3205d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
32068ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3207d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
320862fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
320962fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
32108ed67789SDaniel Lezcano 
32118ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
32128ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
32138ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
32148ed67789SDaniel Lezcano 					       GFP_KERNEL);
321568fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
321668fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3217d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
32188ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3219d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
322062fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
322162fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
32228ed67789SDaniel Lezcano 
32238ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
32248ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
32258ed67789SDaniel Lezcano 					       GFP_KERNEL);
322668fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
322768fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3228d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
32298ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3230d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
323162fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
323262fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
32338ed67789SDaniel Lezcano #endif
32348ed67789SDaniel Lezcano 
3235b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3236b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3237b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3238b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3239b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3240b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3241b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3242b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3243b339a47cSPeter Zijlstra 
32446891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
32456891a346SBenjamin Thery 
32468ed67789SDaniel Lezcano 	ret = 0;
32478ed67789SDaniel Lezcano out:
32488ed67789SDaniel Lezcano 	return ret;
3249f2fc6a54SBenjamin Thery 
325068fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
325168fffc67SPeter Zijlstra out_ip6_prohibit_entry:
325268fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
325368fffc67SPeter Zijlstra out_ip6_null_entry:
325468fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
325568fffc67SPeter Zijlstra #endif
3256fc66f95cSEric Dumazet out_ip6_dst_entries:
3257fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3258f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3259f2fc6a54SBenjamin Thery 	goto out;
3260cdb18761SDaniel Lezcano }
3261cdb18761SDaniel Lezcano 
32622c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3263cdb18761SDaniel Lezcano {
32648ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
32658ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
32668ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
32678ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
32688ed67789SDaniel Lezcano #endif
326941bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3270cdb18761SDaniel Lezcano }
3271cdb18761SDaniel Lezcano 
3272d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3273d189634eSThomas Graf {
3274d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3275d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3276d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3277d189634eSThomas Graf #endif
3278d189634eSThomas Graf 	return 0;
3279d189634eSThomas Graf }
3280d189634eSThomas Graf 
3281d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3282d189634eSThomas Graf {
3283d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3284ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3285ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3286d189634eSThomas Graf #endif
3287d189634eSThomas Graf }
3288d189634eSThomas Graf 
3289cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3290cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3291cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3292cdb18761SDaniel Lezcano };
3293cdb18761SDaniel Lezcano 
3294c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3295c3426b47SDavid S. Miller {
3296c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3297c3426b47SDavid S. Miller 
3298c3426b47SDavid S. Miller 	if (!bp)
3299c3426b47SDavid S. Miller 		return -ENOMEM;
3300c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3301c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3302c3426b47SDavid S. Miller 	return 0;
3303c3426b47SDavid S. Miller }
3304c3426b47SDavid S. Miller 
3305c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3306c3426b47SDavid S. Miller {
3307c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3308c3426b47SDavid S. Miller 
3309c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
331056a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3311c3426b47SDavid S. Miller 	kfree(bp);
3312c3426b47SDavid S. Miller }
3313c3426b47SDavid S. Miller 
33142b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3315c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3316c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3317c3426b47SDavid S. Miller };
3318c3426b47SDavid S. Miller 
3319d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3320d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3321d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3322d189634eSThomas Graf };
3323d189634eSThomas Graf 
33248ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
33258ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
33268ed67789SDaniel Lezcano 	.priority = 0,
33278ed67789SDaniel Lezcano };
33288ed67789SDaniel Lezcano 
3329433d49c3SDaniel Lezcano int __init ip6_route_init(void)
33301da177e4SLinus Torvalds {
3331433d49c3SDaniel Lezcano 	int ret;
3332*8d0b94afSMartin KaFai Lau 	int cpu;
3333433d49c3SDaniel Lezcano 
33349a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
33359a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
33369a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
33379a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
33389a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3339c19a28e1SFernando Carrijo 		goto out;
334014e50e57SDavid S. Miller 
3341fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
33428ed67789SDaniel Lezcano 	if (ret)
3343bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3344bdb3289fSDaniel Lezcano 
3345c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3346c3426b47SDavid S. Miller 	if (ret)
3347e8803b6cSDavid S. Miller 		goto out_dst_entries;
33482a0c451aSThomas Graf 
33497e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
33507e52b33bSDavid S. Miller 	if (ret)
33517e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3352c3426b47SDavid S. Miller 
33535dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
33545dc121e9SArnaud Ebalard 
33558ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
33568ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
33578ed67789SDaniel Lezcano 	 * manually for init_net */
3358d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
33598ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3360bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3361d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
33628ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3363d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
33648ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3365bdb3289fSDaniel Lezcano   #endif
3366e8803b6cSDavid S. Miller 	ret = fib6_init();
3367433d49c3SDaniel Lezcano 	if (ret)
33688ed67789SDaniel Lezcano 		goto out_register_subsys;
3369433d49c3SDaniel Lezcano 
3370433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3371433d49c3SDaniel Lezcano 	if (ret)
3372e8803b6cSDavid S. Miller 		goto out_fib6_init;
3373c35b7e72SDaniel Lezcano 
3374433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3375433d49c3SDaniel Lezcano 	if (ret)
3376433d49c3SDaniel Lezcano 		goto xfrm6_init;
33777e5449c2SDaniel Lezcano 
3378d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3379d189634eSThomas Graf 	if (ret)
3380d189634eSThomas Graf 		goto fib6_rules_init;
3381d189634eSThomas Graf 
3382433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3383c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3384c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3385c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3386d189634eSThomas Graf 		goto out_register_late_subsys;
3387433d49c3SDaniel Lezcano 
33888ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3389cdb18761SDaniel Lezcano 	if (ret)
3390d189634eSThomas Graf 		goto out_register_late_subsys;
33918ed67789SDaniel Lezcano 
3392*8d0b94afSMartin KaFai Lau 	for_each_possible_cpu(cpu) {
3393*8d0b94afSMartin KaFai Lau 		struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
3394*8d0b94afSMartin KaFai Lau 
3395*8d0b94afSMartin KaFai Lau 		INIT_LIST_HEAD(&ul->head);
3396*8d0b94afSMartin KaFai Lau 		spin_lock_init(&ul->lock);
3397*8d0b94afSMartin KaFai Lau 	}
3398*8d0b94afSMartin KaFai Lau 
3399433d49c3SDaniel Lezcano out:
3400433d49c3SDaniel Lezcano 	return ret;
3401433d49c3SDaniel Lezcano 
3402d189634eSThomas Graf out_register_late_subsys:
3403d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3404433d49c3SDaniel Lezcano fib6_rules_init:
3405433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3406433d49c3SDaniel Lezcano xfrm6_init:
3407433d49c3SDaniel Lezcano 	xfrm6_fini();
34082a0c451aSThomas Graf out_fib6_init:
34092a0c451aSThomas Graf 	fib6_gc_cleanup();
34108ed67789SDaniel Lezcano out_register_subsys:
34118ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
34127e52b33bSDavid S. Miller out_register_inetpeer:
34137e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3414fc66f95cSEric Dumazet out_dst_entries:
3415fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3416433d49c3SDaniel Lezcano out_kmem_cache:
3417f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3418433d49c3SDaniel Lezcano 	goto out;
34191da177e4SLinus Torvalds }
34201da177e4SLinus Torvalds 
34211da177e4SLinus Torvalds void ip6_route_cleanup(void)
34221da177e4SLinus Torvalds {
34238ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3424d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3425101367c2SThomas Graf 	fib6_rules_cleanup();
34261da177e4SLinus Torvalds 	xfrm6_fini();
34271da177e4SLinus Torvalds 	fib6_gc_cleanup();
3428c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
34298ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
343041bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3431f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
34321da177e4SLinus Torvalds }
3433