xref: /openbmc/linux/net/ipv6/route.c (revision 48ed7b26faa758e6612cd1fb11c07f25cd54f771)
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 
10806582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
10906582540SDavid S. Miller {
11006582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
11106582540SDavid S. Miller 
1124b32b5adSMartin KaFai Lau 	if (rt->rt6i_flags & RTF_CACHE)
1134b32b5adSMartin KaFai Lau 		return NULL;
1144b32b5adSMartin KaFai Lau 	else
1153b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
11606582540SDavid S. Miller }
11706582540SDavid S. Miller 
118f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
119f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
120f894cbf8SDavid S. Miller 					     const void *daddr)
12139232973SDavid S. Miller {
12239232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
12339232973SDavid S. Miller 
124a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
12539232973SDavid S. Miller 		return (const void *) p;
126f894cbf8SDavid S. Miller 	else if (skb)
127f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
12839232973SDavid S. Miller 	return daddr;
12939232973SDavid S. Miller }
13039232973SDavid S. Miller 
131f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
132f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
133f894cbf8SDavid S. Miller 					  const void *daddr)
134d3aaeb38SDavid S. Miller {
13539232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
13639232973SDavid S. Miller 	struct neighbour *n;
13739232973SDavid S. Miller 
138f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
1398e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
140f83c7790SDavid S. Miller 	if (n)
141f83c7790SDavid S. Miller 		return n;
142f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
143f83c7790SDavid S. Miller }
144f83c7790SDavid S. Miller 
1459a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
1461da177e4SLinus Torvalds 	.family			=	AF_INET6,
1471da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
1481da177e4SLinus Torvalds 	.gc_thresh		=	1024,
1491da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
1500dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
151ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
15206582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
1531da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
1541da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
1551da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
1561da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
1571da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
1586e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
1591ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
160d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
1611da177e4SLinus Torvalds };
1621da177e4SLinus Torvalds 
163ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
164ec831ea7SRoland Dreier {
165618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
166618f9bc7SSteffen Klassert 
167618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
168ec831ea7SRoland Dreier }
169ec831ea7SRoland Dreier 
1706700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
1716700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
17214e50e57SDavid S. Miller {
17314e50e57SDavid S. Miller }
17414e50e57SDavid S. Miller 
1756700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
1766700c270SDavid S. Miller 				      struct sk_buff *skb)
177b587ee3bSDavid S. Miller {
178b587ee3bSDavid S. Miller }
179b587ee3bSDavid S. Miller 
1800972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
1810972ddb2SHeld Bernhard 					 unsigned long old)
1820972ddb2SHeld Bernhard {
1830972ddb2SHeld Bernhard 	return NULL;
1840972ddb2SHeld Bernhard }
1850972ddb2SHeld Bernhard 
18614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
18714e50e57SDavid S. Miller 	.family			=	AF_INET6,
18814e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
18914e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
190ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
191214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
19214e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
193b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
1940972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
195d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
19614e50e57SDavid S. Miller };
19714e50e57SDavid S. Miller 
19862fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
19914edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
20062fa8a84SDavid S. Miller };
20162fa8a84SDavid S. Miller 
202fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2031da177e4SLinus Torvalds 	.dst = {
2041da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2051da177e4SLinus Torvalds 		.__use		= 1,
2062c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2071da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2081da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2091da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2101da177e4SLinus Torvalds 	},
2111da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2124f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2131da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2141da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2151da177e4SLinus Torvalds };
2161da177e4SLinus Torvalds 
217101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
218101367c2SThomas Graf 
219fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
220101367c2SThomas Graf 	.dst = {
221101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
222101367c2SThomas Graf 		.__use		= 1,
2232c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
224101367c2SThomas Graf 		.error		= -EACCES,
2259ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2269ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
227101367c2SThomas Graf 	},
228101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2294f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
230101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
231101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
232101367c2SThomas Graf };
233101367c2SThomas Graf 
234fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
235101367c2SThomas Graf 	.dst = {
236101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
237101367c2SThomas Graf 		.__use		= 1,
2382c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
239101367c2SThomas Graf 		.error		= -EINVAL,
240352e512cSHerbert Xu 		.input		= dst_discard,
241aad88724SEric Dumazet 		.output		= dst_discard_sk,
242101367c2SThomas Graf 	},
243101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2444f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
245101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
246101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
247101367c2SThomas Graf };
248101367c2SThomas Graf 
249101367c2SThomas Graf #endif
250101367c2SThomas Graf 
2511da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
25297bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net,
253957c665fSDavid S. Miller 					     struct net_device *dev,
2548b96d22dSDavid S. Miller 					     int flags,
2558b96d22dSDavid S. Miller 					     struct fib6_table *table)
2561da177e4SLinus Torvalds {
25797bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
2586f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
259cf911662SDavid S. Miller 
26097bab73fSDavid S. Miller 	if (rt) {
2618104891bSSteffen Klassert 		struct dst_entry *dst = &rt->dst;
2628104891bSSteffen Klassert 
2638104891bSSteffen Klassert 		memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
26451ebd318SNicolas Dichtel 		INIT_LIST_HEAD(&rt->rt6i_siblings);
26597bab73fSDavid S. Miller 	}
266cf911662SDavid S. Miller 	return rt;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
2701da177e4SLinus Torvalds {
2711da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
2721da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
273ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
2741da177e4SLinus Torvalds 
2758e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
2768e2ec639SYan, Zheng 
27738308473SDavid S. Miller 	if (idev) {
2781da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
2791da177e4SLinus Torvalds 		in6_dev_put(idev);
2801da177e4SLinus Torvalds 	}
2811716a961SGao feng 
282ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
283ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
284b3419363SDavid S. Miller }
285b3419363SDavid S. Miller 
2861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
2871da177e4SLinus Torvalds 			   int how)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
2901da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
2915a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
292c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
2931da177e4SLinus Torvalds 
29497cac082SDavid S. Miller 	if (dev != loopback_dev) {
29597cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
2965a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
2975a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
29838308473SDavid S. Miller 			if (loopback_idev) {
2991da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
3001da177e4SLinus Torvalds 				in6_dev_put(idev);
3011da177e4SLinus Torvalds 			}
3021da177e4SLinus Torvalds 		}
30397cac082SDavid S. Miller 	}
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
306a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
3071da177e4SLinus Torvalds {
3081716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
3091716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
310a50feda5SEric Dumazet 			return true;
3111716a961SGao feng 	} else if (rt->dst.from) {
3123fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
3131716a961SGao feng 	}
314a50feda5SEric Dumazet 	return false;
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds 
31751ebd318SNicolas Dichtel /* Multipath route selection:
31851ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
31951ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
32051ebd318SNicolas Dichtel  */
32151ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
32251ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
32351ebd318SNicolas Dichtel {
32451ebd318SNicolas Dichtel 	unsigned int val = fl6->flowi6_proto;
32551ebd318SNicolas Dichtel 
326c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->daddr);
327c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->saddr);
32851ebd318SNicolas Dichtel 
32951ebd318SNicolas Dichtel 	/* Work only if this not encapsulated */
33051ebd318SNicolas Dichtel 	switch (fl6->flowi6_proto) {
33151ebd318SNicolas Dichtel 	case IPPROTO_UDP:
33251ebd318SNicolas Dichtel 	case IPPROTO_TCP:
33351ebd318SNicolas Dichtel 	case IPPROTO_SCTP:
334b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_sport;
335b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_dport;
33651ebd318SNicolas Dichtel 		break;
33751ebd318SNicolas Dichtel 
33851ebd318SNicolas Dichtel 	case IPPROTO_ICMPV6:
339b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_type;
340b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_code;
34151ebd318SNicolas Dichtel 		break;
34251ebd318SNicolas Dichtel 	}
34351ebd318SNicolas Dichtel 	/* RFC6438 recommands to use flowlabel */
344b3ce5ae1SNicolas Dichtel 	val ^= (__force u32)fl6->flowlabel;
34551ebd318SNicolas Dichtel 
34651ebd318SNicolas Dichtel 	/* Perhaps, we need to tune, this function? */
34751ebd318SNicolas Dichtel 	val = val ^ (val >> 7) ^ (val >> 12);
34851ebd318SNicolas Dichtel 	return val % candidate_count;
34951ebd318SNicolas Dichtel }
35051ebd318SNicolas Dichtel 
35151ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
35252bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
35352bd4c0cSNicolas Dichtel 					     int strict)
35451ebd318SNicolas Dichtel {
35551ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
35651ebd318SNicolas Dichtel 	int route_choosen;
35751ebd318SNicolas Dichtel 
35851ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
35951ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
36051ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
36151ebd318SNicolas Dichtel 	 */
36251ebd318SNicolas Dichtel 	if (route_choosen)
36351ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
36451ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
36551ebd318SNicolas Dichtel 			route_choosen--;
36651ebd318SNicolas Dichtel 			if (route_choosen == 0) {
36752bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
36852bd4c0cSNicolas Dichtel 					break;
36951ebd318SNicolas Dichtel 				match = sibling;
37051ebd318SNicolas Dichtel 				break;
37151ebd318SNicolas Dichtel 			}
37251ebd318SNicolas Dichtel 		}
37351ebd318SNicolas Dichtel 	return match;
37451ebd318SNicolas Dichtel }
37551ebd318SNicolas Dichtel 
3761da177e4SLinus Torvalds /*
377c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
3781da177e4SLinus Torvalds  */
3791da177e4SLinus Torvalds 
3808ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
3818ed67789SDaniel Lezcano 						    struct rt6_info *rt,
382b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
3831da177e4SLinus Torvalds 						    int oif,
384d420895eSYOSHIFUJI Hideaki 						    int flags)
3851da177e4SLinus Torvalds {
3861da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
3871da177e4SLinus Torvalds 	struct rt6_info *sprt;
3881da177e4SLinus Torvalds 
389dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
390dd3abc4eSYOSHIFUJI Hideaki 		goto out;
391dd3abc4eSYOSHIFUJI Hideaki 
392d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
393d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
394dd3abc4eSYOSHIFUJI Hideaki 
395dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
3961da177e4SLinus Torvalds 			if (dev->ifindex == oif)
3971da177e4SLinus Torvalds 				return sprt;
3981da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
39938308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4001da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
401d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
4021da177e4SLinus Torvalds 						continue;
4031da177e4SLinus Torvalds 					if (local && (!oif ||
4041da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
4051da177e4SLinus Torvalds 						continue;
4061da177e4SLinus Torvalds 				}
4071da177e4SLinus Torvalds 				local = sprt;
4081da177e4SLinus Torvalds 			}
409dd3abc4eSYOSHIFUJI Hideaki 		} else {
410dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
411dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
412dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
413dd3abc4eSYOSHIFUJI Hideaki 		}
4141da177e4SLinus Torvalds 	}
4151da177e4SLinus Torvalds 
416dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
4171da177e4SLinus Torvalds 		if (local)
4181da177e4SLinus Torvalds 			return local;
4191da177e4SLinus Torvalds 
420d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
4218ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
4221da177e4SLinus Torvalds 	}
423dd3abc4eSYOSHIFUJI Hideaki out:
4241da177e4SLinus Torvalds 	return rt;
4251da177e4SLinus Torvalds }
4261da177e4SLinus Torvalds 
42727097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
428c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
429c2f17e82SHannes Frederic Sowa 	struct work_struct work;
430c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
431c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
432c2f17e82SHannes Frederic Sowa };
433c2f17e82SHannes Frederic Sowa 
434c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
435c2f17e82SHannes Frederic Sowa {
436c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
437c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
438c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
439c2f17e82SHannes Frederic Sowa 
440c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
441c2f17e82SHannes Frederic Sowa 	ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
442c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
443662f5533SMichael Büsch 	kfree(work);
444c2f17e82SHannes Frederic Sowa }
445c2f17e82SHannes Frederic Sowa 
44627097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
44727097255SYOSHIFUJI Hideaki {
448f2c31e32SEric Dumazet 	struct neighbour *neigh;
44927097255SYOSHIFUJI Hideaki 	/*
45027097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
45127097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
45227097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
45327097255SYOSHIFUJI Hideaki 	 *
45427097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
45527097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
45627097255SYOSHIFUJI Hideaki 	 */
4572152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
458fdd6681dSAmerigo Wang 		return;
4592152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
4602152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
4612152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
4622152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
4632152caeaSYOSHIFUJI Hideaki / 吉藤英明 		if (neigh->nud_state & NUD_VALID)
4642152caeaSYOSHIFUJI Hideaki / 吉藤英明 			goto out;
4657ff74a59SYOSHIFUJI Hideaki / 吉藤英明 	}
4662152caeaSYOSHIFUJI Hideaki / 吉藤英明 
4672152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!neigh ||
46852e16356SYOSHIFUJI Hideaki 	    time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
469c2f17e82SHannes Frederic Sowa 		struct __rt6_probe_work *work;
47027097255SYOSHIFUJI Hideaki 
471c2f17e82SHannes Frederic Sowa 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
472c2f17e82SHannes Frederic Sowa 
473c2f17e82SHannes Frederic Sowa 		if (neigh && work)
4747e980569SJiri Benc 			__neigh_set_probe_once(neigh);
4752152caeaSYOSHIFUJI Hideaki / 吉藤英明 
476c2f17e82SHannes Frederic Sowa 		if (neigh)
477c2f17e82SHannes Frederic Sowa 			write_unlock(&neigh->lock);
478c2f17e82SHannes Frederic Sowa 
479c2f17e82SHannes Frederic Sowa 		if (work) {
480c2f17e82SHannes Frederic Sowa 			INIT_WORK(&work->work, rt6_probe_deferred);
481c2f17e82SHannes Frederic Sowa 			work->target = rt->rt6i_gateway;
482c2f17e82SHannes Frederic Sowa 			dev_hold(rt->dst.dev);
483c2f17e82SHannes Frederic Sowa 			work->dev = rt->dst.dev;
484c2f17e82SHannes Frederic Sowa 			schedule_work(&work->work);
485c2f17e82SHannes Frederic Sowa 		}
486f2c31e32SEric Dumazet 	} else {
4872152caeaSYOSHIFUJI Hideaki / 吉藤英明 out:
4882152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_unlock(&neigh->lock);
48927097255SYOSHIFUJI Hideaki 	}
4902152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
491f2c31e32SEric Dumazet }
49227097255SYOSHIFUJI Hideaki #else
49327097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
49427097255SYOSHIFUJI Hideaki {
49527097255SYOSHIFUJI Hideaki }
49627097255SYOSHIFUJI Hideaki #endif
49727097255SYOSHIFUJI Hideaki 
4981da177e4SLinus Torvalds /*
499554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5001da177e4SLinus Torvalds  */
501b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5021da177e4SLinus Torvalds {
503d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
504161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
505554cfb7eSYOSHIFUJI Hideaki 		return 2;
506161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
507161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
508161980f4SDavid S. Miller 		return 1;
509554cfb7eSYOSHIFUJI Hideaki 	return 0;
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds 
512afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
5131da177e4SLinus Torvalds {
514f2c31e32SEric Dumazet 	struct neighbour *neigh;
515afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
516f2c31e32SEric Dumazet 
5174d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
5184d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
519afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
520145a3621SYOSHIFUJI Hideaki / 吉藤英明 
521145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
522145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
523145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
524145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
525554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
526afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
527398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
528a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
529afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
5307e980569SJiri Benc 		else
5317e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
532398bcbebSYOSHIFUJI Hideaki #endif
533145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
534afc154e9SHannes Frederic Sowa 	} else {
535afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
5367e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
537a5a81f0bSPaul Marks 	}
538145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
539145a3621SYOSHIFUJI Hideaki / 吉藤英明 
540a5a81f0bSPaul Marks 	return ret;
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
543554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
544554cfb7eSYOSHIFUJI Hideaki 			   int strict)
545554cfb7eSYOSHIFUJI Hideaki {
546a5a81f0bSPaul Marks 	int m;
5474d0c5911SYOSHIFUJI Hideaki 
5484d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
54977d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
550afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
551ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
552ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
553ebacaaa0SYOSHIFUJI Hideaki #endif
554afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
555afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
556afc154e9SHannes Frederic Sowa 		if (n < 0)
557afc154e9SHannes Frederic Sowa 			return n;
558afc154e9SHannes Frederic Sowa 	}
559554cfb7eSYOSHIFUJI Hideaki 	return m;
560554cfb7eSYOSHIFUJI Hideaki }
561554cfb7eSYOSHIFUJI Hideaki 
562f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
563afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
564afc154e9SHannes Frederic Sowa 				   bool *do_rr)
565554cfb7eSYOSHIFUJI Hideaki {
566554cfb7eSYOSHIFUJI Hideaki 	int m;
567afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
568554cfb7eSYOSHIFUJI Hideaki 
569554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
570f11e6659SDavid S. Miller 		goto out;
571554cfb7eSYOSHIFUJI Hideaki 
572554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
5737e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
574afc154e9SHannes Frederic Sowa 		match_do_rr = true;
575afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
5767e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
577f11e6659SDavid S. Miller 		goto out;
5781da177e4SLinus Torvalds 	}
579f11e6659SDavid S. Miller 
580afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
581afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
582afc154e9SHannes Frederic Sowa 
5837e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
584afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
585afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
586afc154e9SHannes Frederic Sowa 		*mpri = m;
587afc154e9SHannes Frederic Sowa 		match = rt;
588afc154e9SHannes Frederic Sowa 	}
589f11e6659SDavid S. Miller out:
590f11e6659SDavid S. Miller 	return match;
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds 
593f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
594f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
595afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
596afc154e9SHannes Frederic Sowa 				     bool *do_rr)
597f11e6659SDavid S. Miller {
5989fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
599f11e6659SDavid S. Miller 	int mpri = -1;
600f11e6659SDavid S. Miller 
601f11e6659SDavid S. Miller 	match = NULL;
6029fbdcfafSSteffen Klassert 	cont = NULL;
6039fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
6049fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6059fbdcfafSSteffen Klassert 			cont = rt;
6069fbdcfafSSteffen Klassert 			break;
6079fbdcfafSSteffen Klassert 		}
6089fbdcfafSSteffen Klassert 
609afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6109fbdcfafSSteffen Klassert 	}
6119fbdcfafSSteffen Klassert 
6129fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
6139fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6149fbdcfafSSteffen Klassert 			cont = rt;
6159fbdcfafSSteffen Klassert 			break;
6169fbdcfafSSteffen Klassert 		}
6179fbdcfafSSteffen Klassert 
6189fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6199fbdcfafSSteffen Klassert 	}
6209fbdcfafSSteffen Klassert 
6219fbdcfafSSteffen Klassert 	if (match || !cont)
6229fbdcfafSSteffen Klassert 		return match;
6239fbdcfafSSteffen Klassert 
6249fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
625afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
626f11e6659SDavid S. Miller 
627f11e6659SDavid S. Miller 	return match;
628f11e6659SDavid S. Miller }
629f11e6659SDavid S. Miller 
630f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
631f11e6659SDavid S. Miller {
632f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
6338ed67789SDaniel Lezcano 	struct net *net;
634afc154e9SHannes Frederic Sowa 	bool do_rr = false;
635f11e6659SDavid S. Miller 
636f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
637f11e6659SDavid S. Miller 	if (!rt0)
638f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
639f11e6659SDavid S. Miller 
640afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
641afc154e9SHannes Frederic Sowa 			     &do_rr);
642f11e6659SDavid S. Miller 
643afc154e9SHannes Frederic Sowa 	if (do_rr) {
644d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
645f11e6659SDavid S. Miller 
646554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
647f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
648f11e6659SDavid S. Miller 			next = fn->leaf;
649f11e6659SDavid S. Miller 
650f11e6659SDavid S. Miller 		if (next != rt0)
651f11e6659SDavid S. Miller 			fn->rr_ptr = next;
652554cfb7eSYOSHIFUJI Hideaki 	}
653554cfb7eSYOSHIFUJI Hideaki 
654d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
655a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
6561da177e4SLinus Torvalds }
6571da177e4SLinus Torvalds 
65870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
65970ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
660b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
66170ceb4f5SYOSHIFUJI Hideaki {
662c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
66370ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
66470ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
66570ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
6664bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
66770ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
66870ceb4f5SYOSHIFUJI Hideaki 
66970ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
67070ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
67170ceb4f5SYOSHIFUJI Hideaki 	}
67270ceb4f5SYOSHIFUJI Hideaki 
67370ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
67470ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
67570ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
67670ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
67770ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
67870ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
67970ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
68070ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
68170ceb4f5SYOSHIFUJI Hideaki 		}
68270ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
68370ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
68470ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
68570ceb4f5SYOSHIFUJI Hideaki 		}
68670ceb4f5SYOSHIFUJI Hideaki 	}
68770ceb4f5SYOSHIFUJI Hideaki 
68870ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
68970ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
6903933fc95SJens Rosenboom 		return -EINVAL;
69170ceb4f5SYOSHIFUJI Hideaki 
6924bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
69370ceb4f5SYOSHIFUJI Hideaki 
69470ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
69570ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
69670ceb4f5SYOSHIFUJI Hideaki 	else {
69770ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
69870ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
69970ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
70070ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
70170ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
70270ceb4f5SYOSHIFUJI Hideaki 	}
70370ceb4f5SYOSHIFUJI Hideaki 
704f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
705f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
706f104a567SDuan Jiong 	else
707f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
708f104a567SDuan Jiong 					gwaddr, dev->ifindex);
70970ceb4f5SYOSHIFUJI Hideaki 
71070ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
711e0a1ad73SThomas Graf 		ip6_del_rt(rt);
71270ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
71370ceb4f5SYOSHIFUJI Hideaki 	}
71470ceb4f5SYOSHIFUJI Hideaki 
71570ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
716efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
71770ceb4f5SYOSHIFUJI Hideaki 					pref);
71870ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
71970ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
72070ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
72170ceb4f5SYOSHIFUJI Hideaki 
72270ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
7231716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
7241716a961SGao feng 			rt6_clean_expires(rt);
7251716a961SGao feng 		else
7261716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
7271716a961SGao feng 
72894e187c0SAmerigo Wang 		ip6_rt_put(rt);
72970ceb4f5SYOSHIFUJI Hideaki 	}
73070ceb4f5SYOSHIFUJI Hideaki 	return 0;
73170ceb4f5SYOSHIFUJI Hideaki }
73270ceb4f5SYOSHIFUJI Hideaki #endif
73370ceb4f5SYOSHIFUJI Hideaki 
734a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
735a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
736a3c00e46SMartin KaFai Lau {
737a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
738a3c00e46SMartin KaFai Lau 	while (1) {
739a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
740a3c00e46SMartin KaFai Lau 			return NULL;
741a3c00e46SMartin KaFai Lau 		pn = fn->parent;
742a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
743a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
744a3c00e46SMartin KaFai Lau 		else
745a3c00e46SMartin KaFai Lau 			fn = pn;
746a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
747a3c00e46SMartin KaFai Lau 			return fn;
748a3c00e46SMartin KaFai Lau 	}
749a3c00e46SMartin KaFai Lau }
750c71099acSThomas Graf 
7518ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
7528ed67789SDaniel Lezcano 					     struct fib6_table *table,
7534c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
7541da177e4SLinus Torvalds {
7551da177e4SLinus Torvalds 	struct fib6_node *fn;
7561da177e4SLinus Torvalds 	struct rt6_info *rt;
7571da177e4SLinus Torvalds 
758c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
7594c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
760c71099acSThomas Graf restart:
761c71099acSThomas Graf 	rt = fn->leaf;
7624c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
76351ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
76452bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
765a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
766a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
767a3c00e46SMartin KaFai Lau 		if (fn)
768a3c00e46SMartin KaFai Lau 			goto restart;
769a3c00e46SMartin KaFai Lau 	}
770d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
771c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
7721da177e4SLinus Torvalds 	return rt;
773c71099acSThomas Graf 
774c71099acSThomas Graf }
775c71099acSThomas Graf 
776ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
777ea6e574eSFlorian Westphal 				    int flags)
778ea6e574eSFlorian Westphal {
779ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
780ea6e574eSFlorian Westphal }
781ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
782ea6e574eSFlorian Westphal 
7839acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
7849acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
785c71099acSThomas Graf {
7864c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
7874c9483b2SDavid S. Miller 		.flowi6_oif = oif,
7884c9483b2SDavid S. Miller 		.daddr = *daddr,
789c71099acSThomas Graf 	};
790c71099acSThomas Graf 	struct dst_entry *dst;
79177d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
792c71099acSThomas Graf 
793adaa70bbSThomas Graf 	if (saddr) {
7944c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
795adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
796adaa70bbSThomas Graf 	}
797adaa70bbSThomas Graf 
7984c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
799c71099acSThomas Graf 	if (dst->error == 0)
800c71099acSThomas Graf 		return (struct rt6_info *) dst;
801c71099acSThomas Graf 
802c71099acSThomas Graf 	dst_release(dst);
803c71099acSThomas Graf 
8041da177e4SLinus Torvalds 	return NULL;
8051da177e4SLinus Torvalds }
8067159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
8077159039aSYOSHIFUJI Hideaki 
808c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
8091da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
8101da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
8111da177e4SLinus Torvalds    be destroyed.
8121da177e4SLinus Torvalds  */
8131da177e4SLinus Torvalds 
814e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
815e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
8161da177e4SLinus Torvalds {
8171da177e4SLinus Torvalds 	int err;
818c71099acSThomas Graf 	struct fib6_table *table;
8191da177e4SLinus Torvalds 
820c71099acSThomas Graf 	table = rt->rt6i_table;
821c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
822e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
823c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds 	return err;
8261da177e4SLinus Torvalds }
8271da177e4SLinus Torvalds 
82840e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
82940e22e8fSThomas Graf {
830e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
831e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
832e715b6d3SFlorian Westphal 
833e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
83440e22e8fSThomas Graf }
83540e22e8fSThomas Graf 
8361716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
83721efcfa0SEric Dumazet 				      const struct in6_addr *daddr,
838b71d1d42SEric Dumazet 				      const struct in6_addr *saddr)
8391da177e4SLinus Torvalds {
8401da177e4SLinus Torvalds 	struct rt6_info *rt;
8411da177e4SLinus Torvalds 
8421da177e4SLinus Torvalds 	/*
8431da177e4SLinus Torvalds 	 *	Clone the route.
8441da177e4SLinus Torvalds 	 */
8451da177e4SLinus Torvalds 
84621efcfa0SEric Dumazet 	rt = ip6_rt_copy(ort, daddr);
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 	if (rt) {
849bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
85021efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
85158c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_CACHE;
8541da177e4SLinus Torvalds 
8551da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
8561da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
8574e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
8581da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
8591da177e4SLinus Torvalds 		}
8601da177e4SLinus Torvalds #endif
86195a9a5baSYOSHIFUJI Hideaki 	}
8621da177e4SLinus Torvalds 
8631da177e4SLinus Torvalds 	return rt;
8641da177e4SLinus Torvalds }
86595a9a5baSYOSHIFUJI Hideaki 
86621efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
86721efcfa0SEric Dumazet 					const struct in6_addr *daddr)
868299d9939SYOSHIFUJI Hideaki {
86921efcfa0SEric Dumazet 	struct rt6_info *rt = ip6_rt_copy(ort, daddr);
87021efcfa0SEric Dumazet 
871887c95ccSYOSHIFUJI Hideaki / 吉藤英明 	if (rt)
872299d9939SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_CACHE;
873299d9939SYOSHIFUJI Hideaki 	return rt;
874299d9939SYOSHIFUJI Hideaki }
875299d9939SYOSHIFUJI Hideaki 
8768ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
8774c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
8781da177e4SLinus Torvalds {
879367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
880519fbd87SYOSHIFUJI Hideaki 	struct rt6_info *rt, *nrt;
881c71099acSThomas Graf 	int strict = 0;
8821da177e4SLinus Torvalds 	int attempts = 3;
883519fbd87SYOSHIFUJI Hideaki 	int err;
8841da177e4SLinus Torvalds 
88577d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
886367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
887367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
8881da177e4SLinus Torvalds 
889a3c00e46SMartin KaFai Lau redo_fib6_lookup_lock:
890c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8911da177e4SLinus Torvalds 
8924c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
893367efcb9SMartin KaFai Lau 	saved_fn = fn;
8941da177e4SLinus Torvalds 
895a3c00e46SMartin KaFai Lau redo_rt6_select:
896367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
89752bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
898367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
899a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
900a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
901a3c00e46SMartin KaFai Lau 		if (fn)
902a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
903367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
904367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
905367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
906367efcb9SMartin KaFai Lau 			fn = saved_fn;
907367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
908367efcb9SMartin KaFai Lau 		} else {
909367efcb9SMartin KaFai Lau 			dst_hold(&rt->dst);
910367efcb9SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
911367efcb9SMartin KaFai Lau 			goto out2;
912367efcb9SMartin KaFai Lau 		}
913a3c00e46SMartin KaFai Lau 	}
914a3c00e46SMartin KaFai Lau 
915d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
916c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
9171da177e4SLinus Torvalds 
91894c77bb4SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_CACHE)
91994c77bb4SMartin KaFai Lau 		goto out2;
92094c77bb4SMartin KaFai Lau 
921c440f160SYOSHIFUJI Hideaki / 吉藤英明 	if (!(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)))
9224c9483b2SDavid S. Miller 		nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
9237035870dSMartin KaFai Lau 	else if (!(rt->dst.flags & DST_HOST) || !(rt->rt6i_flags & RTF_LOCAL))
9244c9483b2SDavid S. Miller 		nrt = rt6_alloc_clone(rt, &fl6->daddr);
9257343ff31SDavid S. Miller 	else
9267343ff31SDavid S. Miller 		goto out2;
9271da177e4SLinus Torvalds 
92894e187c0SAmerigo Wang 	ip6_rt_put(rt);
9298ed67789SDaniel Lezcano 	rt = nrt ? : net->ipv6.ip6_null_entry;
9301da177e4SLinus Torvalds 
931d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
932e40cf353SYOSHIFUJI Hideaki 	if (nrt) {
93340e22e8fSThomas Graf 		err = ip6_ins_rt(nrt);
934e40cf353SYOSHIFUJI Hideaki 		if (!err)
935e40cf353SYOSHIFUJI Hideaki 			goto out2;
936e40cf353SYOSHIFUJI Hideaki 	}
937e40cf353SYOSHIFUJI Hideaki 
938e40cf353SYOSHIFUJI Hideaki 	if (--attempts <= 0)
9391da177e4SLinus Torvalds 		goto out2;
9401da177e4SLinus Torvalds 
941519fbd87SYOSHIFUJI Hideaki 	/*
942c71099acSThomas Graf 	 * Race condition! In the gap, when table->tb6_lock was
943519fbd87SYOSHIFUJI Hideaki 	 * released someone could insert this route.  Relookup.
9441da177e4SLinus Torvalds 	 */
94594e187c0SAmerigo Wang 	ip6_rt_put(rt);
946a3c00e46SMartin KaFai Lau 	goto redo_fib6_lookup_lock;
947e40cf353SYOSHIFUJI Hideaki 
9481da177e4SLinus Torvalds out2:
9494b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
950d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
951d8d1f30bSChangli Gao 	rt->dst.__use++;
952c71099acSThomas Graf 
953c71099acSThomas Graf 	return rt;
954c71099acSThomas Graf }
955c71099acSThomas Graf 
9568ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
9574c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
9584acad72dSPavel Emelyanov {
9594c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
9604acad72dSPavel Emelyanov }
9614acad72dSPavel Emelyanov 
96272331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
96372331bc0SShmulik Ladkani 						struct net_device *dev,
96472331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
96572331bc0SShmulik Ladkani {
96672331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
96772331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
96872331bc0SShmulik Ladkani 
96972331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
97072331bc0SShmulik Ladkani }
97172331bc0SShmulik Ladkani 
972c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
973c71099acSThomas Graf {
974b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
975c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
976adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
9774c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
9784c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
9794c9483b2SDavid S. Miller 		.daddr = iph->daddr,
9804c9483b2SDavid S. Miller 		.saddr = iph->saddr,
9816502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
9824c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
9834c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
984c71099acSThomas Graf 	};
985adaa70bbSThomas Graf 
98672331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
987c71099acSThomas Graf }
988c71099acSThomas Graf 
9898ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
9904c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
991c71099acSThomas Graf {
9924c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
993c71099acSThomas Graf }
994c71099acSThomas Graf 
9959c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
9964c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
997c71099acSThomas Graf {
998c71099acSThomas Graf 	int flags = 0;
999c71099acSThomas Graf 
10001fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
10014dc27d1cSDavid McCullough 
10024c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
100377d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1004c71099acSThomas Graf 
10054c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
1006adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
10070c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
10080c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1009adaa70bbSThomas Graf 
10104c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
10111da177e4SLinus Torvalds }
10127159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
10131da177e4SLinus Torvalds 
10142774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
101514e50e57SDavid S. Miller {
10165c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
101714e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
101814e50e57SDavid S. Miller 
1019f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
102014e50e57SDavid S. Miller 	if (rt) {
1021d8d1f30bSChangli Gao 		new = &rt->dst;
102214e50e57SDavid S. Miller 
10238104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
10248104891bSSteffen Klassert 
102514e50e57SDavid S. Miller 		new->__use = 1;
1026352e512cSHerbert Xu 		new->input = dst_discard;
1027aad88724SEric Dumazet 		new->output = dst_discard_sk;
102814e50e57SDavid S. Miller 
102921efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
103021efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
103121efcfa0SEric Dumazet 		else
1032defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
103314e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
103414e50e57SDavid S. Miller 		if (rt->rt6i_idev)
103514e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
103614e50e57SDavid S. Miller 
10374e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
10381716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
103914e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
104014e50e57SDavid S. Miller 
104114e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
104214e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
104314e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
104414e50e57SDavid S. Miller #endif
104514e50e57SDavid S. Miller 
104614e50e57SDavid S. Miller 		dst_free(new);
104714e50e57SDavid S. Miller 	}
104814e50e57SDavid S. Miller 
104969ead7afSDavid S. Miller 	dst_release(dst_orig);
105069ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
105114e50e57SDavid S. Miller }
105214e50e57SDavid S. Miller 
10531da177e4SLinus Torvalds /*
10541da177e4SLinus Torvalds  *	Destination cache support functions
10551da177e4SLinus Torvalds  */
10561da177e4SLinus Torvalds 
10574b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
10584b32b5adSMartin KaFai Lau {
10594b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
10604b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
10614b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
10624b32b5adSMartin KaFai Lau }
10634b32b5adSMartin KaFai Lau 
10641da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
10651da177e4SLinus Torvalds {
10661da177e4SLinus Torvalds 	struct rt6_info *rt;
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
10691da177e4SLinus Torvalds 
10706f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
10716f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
10726f3118b5SNicolas Dichtel 	 * into this function always.
10736f3118b5SNicolas Dichtel 	 */
1074e3bc10bdSHannes Frederic Sowa 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
10751da177e4SLinus Torvalds 		return NULL;
1076e3bc10bdSHannes Frederic Sowa 
1077e3bc10bdSHannes Frederic Sowa 	if (rt6_check_expired(rt))
1078e3bc10bdSHannes Frederic Sowa 		return NULL;
1079e3bc10bdSHannes Frederic Sowa 
10804b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
10814b32b5adSMartin KaFai Lau 
1082e3bc10bdSHannes Frederic Sowa 	return dst;
10831da177e4SLinus Torvalds }
10841da177e4SLinus Torvalds 
10851da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
10861da177e4SLinus Torvalds {
10871da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	if (rt) {
109054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
109154c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1092e0a1ad73SThomas Graf 				ip6_del_rt(rt);
109354c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
10941da177e4SLinus Torvalds 			}
109554c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
109654c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
109754c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
109854c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
109954c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
110054c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
11011da177e4SLinus Torvalds }
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
11041da177e4SLinus Torvalds {
11051da177e4SLinus Torvalds 	struct rt6_info *rt;
11061da177e4SLinus Torvalds 
11073ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
11081da177e4SLinus Torvalds 
1109adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
11101da177e4SLinus Torvalds 	if (rt) {
11111eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
11121eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
11131eb4f758SHannes Frederic Sowa 			if (ip6_del_rt(rt))
11141eb4f758SHannes Frederic Sowa 				dst_free(&rt->dst);
11151eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
11161da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
11171da177e4SLinus Torvalds 		}
11181da177e4SLinus Torvalds 	}
11191eb4f758SHannes Frederic Sowa }
11201da177e4SLinus Torvalds 
11216700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
11226700c270SDavid S. Miller 			       struct sk_buff *skb, u32 mtu)
11231da177e4SLinus Torvalds {
11241da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
11251da177e4SLinus Torvalds 
112681aded24SDavid S. Miller 	dst_confirm(dst);
1127653437d0SMartin KaFai Lau 	if (mtu < dst_mtu(dst) && (rt6->rt6i_flags & RTF_CACHE)) {
112881aded24SDavid S. Miller 		struct net *net = dev_net(dst->dev);
112981aded24SDavid S. Miller 
11301da177e4SLinus Torvalds 		rt6->rt6i_flags |= RTF_MODIFIED;
11319d289715SHagen Paul Pfeifer 		if (mtu < IPV6_MIN_MTU)
11321da177e4SLinus Torvalds 			mtu = IPV6_MIN_MTU;
11339d289715SHagen Paul Pfeifer 
11344b32b5adSMartin KaFai Lau 		rt6->rt6i_pmtu = mtu;
113581aded24SDavid S. Miller 		rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
11361da177e4SLinus Torvalds 	}
11371da177e4SLinus Torvalds }
11381da177e4SLinus Torvalds 
113942ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
114042ae66c8SDavid S. Miller 		     int oif, u32 mark)
114181aded24SDavid S. Miller {
114281aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
114381aded24SDavid S. Miller 	struct dst_entry *dst;
114481aded24SDavid S. Miller 	struct flowi6 fl6;
114581aded24SDavid S. Miller 
114681aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
114781aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
11481b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
114981aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
115081aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
11516502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
115281aded24SDavid S. Miller 
115381aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
115481aded24SDavid S. Miller 	if (!dst->error)
11556700c270SDavid S. Miller 		ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
115681aded24SDavid S. Miller 	dst_release(dst);
115781aded24SDavid S. Miller }
115881aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
115981aded24SDavid S. Miller 
116081aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
116181aded24SDavid S. Miller {
116281aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
116381aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
116481aded24SDavid S. Miller }
116581aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
116681aded24SDavid S. Miller 
1167b55b76b2SDuan Jiong /* Handle redirects */
1168b55b76b2SDuan Jiong struct ip6rd_flowi {
1169b55b76b2SDuan Jiong 	struct flowi6 fl6;
1170b55b76b2SDuan Jiong 	struct in6_addr gateway;
1171b55b76b2SDuan Jiong };
1172b55b76b2SDuan Jiong 
1173b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1174b55b76b2SDuan Jiong 					     struct fib6_table *table,
1175b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1176b55b76b2SDuan Jiong 					     int flags)
1177b55b76b2SDuan Jiong {
1178b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1179b55b76b2SDuan Jiong 	struct rt6_info *rt;
1180b55b76b2SDuan Jiong 	struct fib6_node *fn;
1181b55b76b2SDuan Jiong 
1182b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1183b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1184b55b76b2SDuan Jiong 	 *
1185b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1186b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1187b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1188b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1189b55b76b2SDuan Jiong 	 * routes.
1190b55b76b2SDuan Jiong 	 */
1191b55b76b2SDuan Jiong 
1192b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1193b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1194b55b76b2SDuan Jiong restart:
1195b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1196b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1197b55b76b2SDuan Jiong 			continue;
1198b55b76b2SDuan Jiong 		if (rt->dst.error)
1199b55b76b2SDuan Jiong 			break;
1200b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1201b55b76b2SDuan Jiong 			continue;
1202b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1203b55b76b2SDuan Jiong 			continue;
1204b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1205b55b76b2SDuan Jiong 			continue;
1206b55b76b2SDuan Jiong 		break;
1207b55b76b2SDuan Jiong 	}
1208b55b76b2SDuan Jiong 
1209b55b76b2SDuan Jiong 	if (!rt)
1210b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1211b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1212b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1213b0a1ba59SMartin KaFai Lau 		goto out;
1214b0a1ba59SMartin KaFai Lau 	}
1215b0a1ba59SMartin KaFai Lau 
1216b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1217a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1218a3c00e46SMartin KaFai Lau 		if (fn)
1219a3c00e46SMartin KaFai Lau 			goto restart;
1220b55b76b2SDuan Jiong 	}
1221a3c00e46SMartin KaFai Lau 
1222b0a1ba59SMartin KaFai Lau out:
1223b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1224b55b76b2SDuan Jiong 
1225b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1226b55b76b2SDuan Jiong 
1227b55b76b2SDuan Jiong 	return rt;
1228b55b76b2SDuan Jiong };
1229b55b76b2SDuan Jiong 
1230b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1231b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1232b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1233b55b76b2SDuan Jiong {
1234b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1235b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1236b55b76b2SDuan Jiong 
1237b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1238b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1239b55b76b2SDuan Jiong 
1240b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1241b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1242b55b76b2SDuan Jiong }
1243b55b76b2SDuan Jiong 
12443a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
12453a5ad2eeSDavid S. Miller {
12463a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
12473a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
12483a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
12493a5ad2eeSDavid S. Miller 
12503a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1251e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
12523a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
12533a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
12543a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
12553a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
12566502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
12573a5ad2eeSDavid S. Miller 
1258b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
12596700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
12603a5ad2eeSDavid S. Miller 	dst_release(dst);
12613a5ad2eeSDavid S. Miller }
12623a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
12633a5ad2eeSDavid S. Miller 
1264c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1265c92a59ecSDuan Jiong 			    u32 mark)
1266c92a59ecSDuan Jiong {
1267c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1268c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1269c92a59ecSDuan Jiong 	struct dst_entry *dst;
1270c92a59ecSDuan Jiong 	struct flowi6 fl6;
1271c92a59ecSDuan Jiong 
1272c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1273e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1274c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1275c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1276c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1277c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1278c92a59ecSDuan Jiong 
1279b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1280c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1281c92a59ecSDuan Jiong 	dst_release(dst);
1282c92a59ecSDuan Jiong }
1283c92a59ecSDuan Jiong 
12843a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
12853a5ad2eeSDavid S. Miller {
12863a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
12873a5ad2eeSDavid S. Miller }
12883a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
12893a5ad2eeSDavid S. Miller 
12900dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
12911da177e4SLinus Torvalds {
12920dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
12930dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
12940dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
12950dbaee3bSDavid S. Miller 
12961da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
12971da177e4SLinus Torvalds 
12985578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
12995578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
13001da177e4SLinus Torvalds 
13011da177e4SLinus Torvalds 	/*
13021da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
13031da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
13041da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
13051da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
13061da177e4SLinus Torvalds 	 */
13071da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
13081da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
13091da177e4SLinus Torvalds 	return mtu;
13101da177e4SLinus Torvalds }
13111da177e4SLinus Torvalds 
1312ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1313d33e4553SDavid S. Miller {
13144b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
13154b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1316d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1317618f9bc7SSteffen Klassert 
1318618f9bc7SSteffen Klassert 	if (mtu)
131930f78d8eSEric Dumazet 		goto out;
1320618f9bc7SSteffen Klassert 
13214b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
13224b32b5adSMartin KaFai Lau 	if (mtu)
13234b32b5adSMartin KaFai Lau 		goto out;
13244b32b5adSMartin KaFai Lau 
1325618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1326d33e4553SDavid S. Miller 
1327d33e4553SDavid S. Miller 	rcu_read_lock();
1328d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1329d33e4553SDavid S. Miller 	if (idev)
1330d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1331d33e4553SDavid S. Miller 	rcu_read_unlock();
1332d33e4553SDavid S. Miller 
133330f78d8eSEric Dumazet out:
133430f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1335d33e4553SDavid S. Miller }
1336d33e4553SDavid S. Miller 
13373b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
13383b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
13395d0bbeebSThomas Graf 
13403b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
134187a11578SDavid S. Miller 				  struct flowi6 *fl6)
13421da177e4SLinus Torvalds {
134387a11578SDavid S. Miller 	struct dst_entry *dst;
13441da177e4SLinus Torvalds 	struct rt6_info *rt;
13451da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1346c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13471da177e4SLinus Torvalds 
134838308473SDavid S. Miller 	if (unlikely(!idev))
1349122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
13501da177e4SLinus Torvalds 
13518b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, dev, 0, NULL);
135238308473SDavid S. Miller 	if (unlikely(!rt)) {
13531da177e4SLinus Torvalds 		in6_dev_put(idev);
135487a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
13551da177e4SLinus Torvalds 		goto out;
13561da177e4SLinus Torvalds 	}
13571da177e4SLinus Torvalds 
13588e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
13598e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1360d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1361550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
136287a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
13638e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
13648e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
136514edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
13661da177e4SLinus Torvalds 
13673b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1368d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1369d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
13703b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
13711da177e4SLinus Torvalds 
13725578689aSDaniel Lezcano 	fib6_force_start_gc(net);
13731da177e4SLinus Torvalds 
137487a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
137587a11578SDavid S. Miller 
13761da177e4SLinus Torvalds out:
137787a11578SDavid S. Miller 	return dst;
13781da177e4SLinus Torvalds }
13791da177e4SLinus Torvalds 
13803d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
13811da177e4SLinus Torvalds {
1382e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
13833d0f24a7SStephen Hemminger 	int more = 0;
13841da177e4SLinus Torvalds 
13853b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
13863b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
13875d0bbeebSThomas Graf 
13881da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
13891da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
13901da177e4SLinus Torvalds 			*pprev = dst->next;
13911da177e4SLinus Torvalds 			dst_free(dst);
13921da177e4SLinus Torvalds 		} else {
13931da177e4SLinus Torvalds 			pprev = &dst->next;
13943d0f24a7SStephen Hemminger 			++more;
13951da177e4SLinus Torvalds 		}
13961da177e4SLinus Torvalds 	}
13971da177e4SLinus Torvalds 
13983b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
13995d0bbeebSThomas Graf 
14003d0f24a7SStephen Hemminger 	return more;
14011da177e4SLinus Torvalds }
14021da177e4SLinus Torvalds 
14031e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
14041e493d19SDavid S. Miller 			    void *arg)
14051e493d19SDavid S. Miller {
14061e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
14071e493d19SDavid S. Miller 
14081e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
14091e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
14101e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
14111e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
14121e493d19SDavid S. Miller 		if (func(rt, arg)) {
14131e493d19SDavid S. Miller 			*pprev = dst->next;
14141e493d19SDavid S. Miller 			dst_free(dst);
14151e493d19SDavid S. Miller 		} else {
14161e493d19SDavid S. Miller 			pprev = &dst->next;
14171e493d19SDavid S. Miller 		}
14181e493d19SDavid S. Miller 	}
14191e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
14201e493d19SDavid S. Miller }
14211e493d19SDavid S. Miller 
1422569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
14231da177e4SLinus Torvalds {
142486393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
14257019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
14267019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
14277019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
14287019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
14297019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1430fc66f95cSEric Dumazet 	int entries;
14311da177e4SLinus Torvalds 
1432fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
143349a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1434fc66f95cSEric Dumazet 	    entries <= rt_max_size)
14351da177e4SLinus Torvalds 		goto out;
14361da177e4SLinus Torvalds 
14376891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
143814956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1439fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1440fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
14417019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
14421da177e4SLinus Torvalds out:
14437019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1444fc66f95cSEric Dumazet 	return entries > rt_max_size;
14451da177e4SLinus Torvalds }
14461da177e4SLinus Torvalds 
1447e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1448e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1449e715b6d3SFlorian Westphal {
1450e715b6d3SFlorian Westphal 	struct nlattr *nla;
1451e715b6d3SFlorian Westphal 	int remaining;
1452e715b6d3SFlorian Westphal 	u32 *mp;
1453e715b6d3SFlorian Westphal 
145463159f29SIan Morris 	if (!cfg->fc_mx)
1455e715b6d3SFlorian Westphal 		return 0;
1456e715b6d3SFlorian Westphal 
1457e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1458e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1459e715b6d3SFlorian Westphal 		return -ENOMEM;
1460e715b6d3SFlorian Westphal 
1461e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1462e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1463e715b6d3SFlorian Westphal 
1464e715b6d3SFlorian Westphal 		if (type) {
1465ea697639SDaniel Borkmann 			u32 val;
1466ea697639SDaniel Borkmann 
1467e715b6d3SFlorian Westphal 			if (unlikely(type > RTAX_MAX))
1468e715b6d3SFlorian Westphal 				goto err;
1469ea697639SDaniel Borkmann 			if (type == RTAX_CC_ALGO) {
1470ea697639SDaniel Borkmann 				char tmp[TCP_CA_NAME_MAX];
1471e715b6d3SFlorian Westphal 
1472ea697639SDaniel Borkmann 				nla_strlcpy(tmp, nla, sizeof(tmp));
1473ea697639SDaniel Borkmann 				val = tcp_ca_get_key_by_name(tmp);
1474ea697639SDaniel Borkmann 				if (val == TCP_CA_UNSPEC)
1475ea697639SDaniel Borkmann 					goto err;
1476ea697639SDaniel Borkmann 			} else {
1477ea697639SDaniel Borkmann 				val = nla_get_u32(nla);
1478ea697639SDaniel Borkmann 			}
1479ea697639SDaniel Borkmann 
1480ea697639SDaniel Borkmann 			mp[type - 1] = val;
1481e715b6d3SFlorian Westphal 			__set_bit(type - 1, mxc->mx_valid);
1482e715b6d3SFlorian Westphal 		}
1483e715b6d3SFlorian Westphal 	}
1484e715b6d3SFlorian Westphal 
1485e715b6d3SFlorian Westphal 	mxc->mx = mp;
1486e715b6d3SFlorian Westphal 
1487e715b6d3SFlorian Westphal 	return 0;
1488e715b6d3SFlorian Westphal  err:
1489e715b6d3SFlorian Westphal 	kfree(mp);
1490e715b6d3SFlorian Westphal 	return -EINVAL;
1491e715b6d3SFlorian Westphal }
14921da177e4SLinus Torvalds 
149386872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
14941da177e4SLinus Torvalds {
14951da177e4SLinus Torvalds 	int err;
14965578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
14971da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
14981da177e4SLinus Torvalds 	struct net_device *dev = NULL;
14991da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1500c71099acSThomas Graf 	struct fib6_table *table;
1501e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
15021da177e4SLinus Torvalds 	int addr_type;
15031da177e4SLinus Torvalds 
150486872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
15051da177e4SLinus Torvalds 		return -EINVAL;
15061da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
150786872cb5SThomas Graf 	if (cfg->fc_src_len)
15081da177e4SLinus Torvalds 		return -EINVAL;
15091da177e4SLinus Torvalds #endif
151086872cb5SThomas Graf 	if (cfg->fc_ifindex) {
15111da177e4SLinus Torvalds 		err = -ENODEV;
15125578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
15131da177e4SLinus Torvalds 		if (!dev)
15141da177e4SLinus Torvalds 			goto out;
15151da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
15161da177e4SLinus Torvalds 		if (!idev)
15171da177e4SLinus Torvalds 			goto out;
15181da177e4SLinus Torvalds 	}
15191da177e4SLinus Torvalds 
152086872cb5SThomas Graf 	if (cfg->fc_metric == 0)
152186872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
15221da177e4SLinus Torvalds 
1523c71099acSThomas Graf 	err = -ENOBUFS;
152438308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1525d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1526d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
152738308473SDavid S. Miller 		if (!table) {
1528f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1529d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1530d71314b4SMatti Vaittinen 		}
1531d71314b4SMatti Vaittinen 	} else {
1532d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1533d71314b4SMatti Vaittinen 	}
153438308473SDavid S. Miller 
153538308473SDavid S. Miller 	if (!table)
1536c71099acSThomas Graf 		goto out;
1537c71099acSThomas Graf 
1538c88507fbSSabrina Dubroca 	rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table);
15391da177e4SLinus Torvalds 
154038308473SDavid S. Miller 	if (!rt) {
15411da177e4SLinus Torvalds 		err = -ENOMEM;
15421da177e4SLinus Torvalds 		goto out;
15431da177e4SLinus Torvalds 	}
15441da177e4SLinus Torvalds 
15451716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
15461716a961SGao feng 		rt6_set_expires(rt, jiffies +
15471716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
15481716a961SGao feng 	else
15491716a961SGao feng 		rt6_clean_expires(rt);
15501da177e4SLinus Torvalds 
155186872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
155286872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
155386872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
155486872cb5SThomas Graf 
155586872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
15561da177e4SLinus Torvalds 
15571da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1558d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1559ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1560ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
15611da177e4SLinus Torvalds 	else
1562d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
15631da177e4SLinus Torvalds 
1564d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
15651da177e4SLinus Torvalds 
156686872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
156786872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1568afc4eef8SMartin KaFai Lau 	if (rt->rt6i_dst.plen == 128)
156911d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
15701da177e4SLinus Torvalds 
15711da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
157286872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
157386872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
15741da177e4SLinus Torvalds #endif
15751da177e4SLinus Torvalds 
157686872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
15771da177e4SLinus Torvalds 
15781da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
15791da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
15801da177e4SLinus Torvalds 	 */
158186872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
158238308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
158338308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
158438308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
15851da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
15865578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
15871da177e4SLinus Torvalds 			if (dev) {
15881da177e4SLinus Torvalds 				dev_put(dev);
15891da177e4SLinus Torvalds 				in6_dev_put(idev);
15901da177e4SLinus Torvalds 			}
15915578689aSDaniel Lezcano 			dev = net->loopback_dev;
15921da177e4SLinus Torvalds 			dev_hold(dev);
15931da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
15941da177e4SLinus Torvalds 			if (!idev) {
15951da177e4SLinus Torvalds 				err = -ENODEV;
15961da177e4SLinus Torvalds 				goto out;
15971da177e4SLinus Torvalds 			}
15981da177e4SLinus Torvalds 		}
15991da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1600ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1601ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1602ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1603aad88724SEric Dumazet 			rt->dst.output = dst_discard_sk;
16047150aedeSKamala R 			rt->dst.input = dst_discard;
1605ef2c7d7bSNicolas Dichtel 			break;
1606ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1607ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
16087150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
16097150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1610ef2c7d7bSNicolas Dichtel 			break;
1611b4949ab2SNicolas Dichtel 		case RTN_THROW:
1612ef2c7d7bSNicolas Dichtel 		default:
16137150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
16147150aedeSKamala R 					: -ENETUNREACH;
16157150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
16167150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1617ef2c7d7bSNicolas Dichtel 			break;
1618ef2c7d7bSNicolas Dichtel 		}
16191da177e4SLinus Torvalds 		goto install_route;
16201da177e4SLinus Torvalds 	}
16211da177e4SLinus Torvalds 
162286872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1623b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
16241da177e4SLinus Torvalds 		int gwa_type;
16251da177e4SLinus Torvalds 
162686872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
1627*48ed7b26SFlorian Westphal 
1628*48ed7b26SFlorian Westphal 		/* if gw_addr is local we will fail to detect this in case
1629*48ed7b26SFlorian Westphal 		 * address is still TENTATIVE (DAD in progress). rt6_lookup()
1630*48ed7b26SFlorian Westphal 		 * will return already-added prefix route via interface that
1631*48ed7b26SFlorian Westphal 		 * prefix route was assigned to, which might be non-loopback.
1632*48ed7b26SFlorian Westphal 		 */
1633*48ed7b26SFlorian Westphal 		err = -EINVAL;
1634*48ed7b26SFlorian Westphal 		if (ipv6_chk_addr_and_flags(net, gw_addr, NULL, 0, 0))
1635*48ed7b26SFlorian Westphal 			goto out;
1636*48ed7b26SFlorian Westphal 
16374e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
16381da177e4SLinus Torvalds 		gwa_type = ipv6_addr_type(gw_addr);
16391da177e4SLinus Torvalds 
16401da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
16411da177e4SLinus Torvalds 			struct rt6_info *grt;
16421da177e4SLinus Torvalds 
16431da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
16441da177e4SLinus Torvalds 			   addresses as nexthop address.
16451da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
16461da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
16471da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
16481da177e4SLinus Torvalds 			   some exceptions. --ANK
16491da177e4SLinus Torvalds 			 */
16501da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
16511da177e4SLinus Torvalds 				goto out;
16521da177e4SLinus Torvalds 
16535578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
16541da177e4SLinus Torvalds 
16551da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
165638308473SDavid S. Miller 			if (!grt)
16571da177e4SLinus Torvalds 				goto out;
16581da177e4SLinus Torvalds 			if (dev) {
1659d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
166094e187c0SAmerigo Wang 					ip6_rt_put(grt);
16611da177e4SLinus Torvalds 					goto out;
16621da177e4SLinus Torvalds 				}
16631da177e4SLinus Torvalds 			} else {
1664d1918542SDavid S. Miller 				dev = grt->dst.dev;
16651da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
16661da177e4SLinus Torvalds 				dev_hold(dev);
16671da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
16681da177e4SLinus Torvalds 			}
16691da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
16701da177e4SLinus Torvalds 				err = 0;
167194e187c0SAmerigo Wang 			ip6_rt_put(grt);
16721da177e4SLinus Torvalds 
16731da177e4SLinus Torvalds 			if (err)
16741da177e4SLinus Torvalds 				goto out;
16751da177e4SLinus Torvalds 		}
16761da177e4SLinus Torvalds 		err = -EINVAL;
167738308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
16781da177e4SLinus Torvalds 			goto out;
16791da177e4SLinus Torvalds 	}
16801da177e4SLinus Torvalds 
16811da177e4SLinus Torvalds 	err = -ENODEV;
168238308473SDavid S. Miller 	if (!dev)
16831da177e4SLinus Torvalds 		goto out;
16841da177e4SLinus Torvalds 
1685c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1686c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1687c3968a85SDaniel Walter 			err = -EINVAL;
1688c3968a85SDaniel Walter 			goto out;
1689c3968a85SDaniel Walter 		}
16904e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1691c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1692c3968a85SDaniel Walter 	} else
1693c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1694c3968a85SDaniel Walter 
169586872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
16961da177e4SLinus Torvalds 
16971da177e4SLinus Torvalds install_route:
1698d8d1f30bSChangli Gao 	rt->dst.dev = dev;
16991da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1700c71099acSThomas Graf 	rt->rt6i_table = table;
170163152fc0SDaniel Lezcano 
1702c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
170363152fc0SDaniel Lezcano 
1704e715b6d3SFlorian Westphal 	err = ip6_convert_metrics(&mxc, cfg);
1705e715b6d3SFlorian Westphal 	if (err)
1706e715b6d3SFlorian Westphal 		goto out;
17071da177e4SLinus Torvalds 
1708e715b6d3SFlorian Westphal 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
1709e715b6d3SFlorian Westphal 
1710e715b6d3SFlorian Westphal 	kfree(mxc.mx);
1711e715b6d3SFlorian Westphal 	return err;
17121da177e4SLinus Torvalds out:
17131da177e4SLinus Torvalds 	if (dev)
17141da177e4SLinus Torvalds 		dev_put(dev);
17151da177e4SLinus Torvalds 	if (idev)
17161da177e4SLinus Torvalds 		in6_dev_put(idev);
17171da177e4SLinus Torvalds 	if (rt)
1718d8d1f30bSChangli Gao 		dst_free(&rt->dst);
17191da177e4SLinus Torvalds 	return err;
17201da177e4SLinus Torvalds }
17211da177e4SLinus Torvalds 
172286872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
17231da177e4SLinus Torvalds {
17241da177e4SLinus Torvalds 	int err;
1725c71099acSThomas Graf 	struct fib6_table *table;
1726d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
17271da177e4SLinus Torvalds 
17286825a26cSGao feng 	if (rt == net->ipv6.ip6_null_entry) {
17296825a26cSGao feng 		err = -ENOENT;
17306825a26cSGao feng 		goto out;
17316825a26cSGao feng 	}
17326c813a72SPatrick McHardy 
1733c71099acSThomas Graf 	table = rt->rt6i_table;
1734c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
173586872cb5SThomas Graf 	err = fib6_del(rt, info);
1736c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
17371da177e4SLinus Torvalds 
17386825a26cSGao feng out:
173994e187c0SAmerigo Wang 	ip6_rt_put(rt);
17401da177e4SLinus Torvalds 	return err;
17411da177e4SLinus Torvalds }
17421da177e4SLinus Torvalds 
1743e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1744e0a1ad73SThomas Graf {
17454d1169c1SDenis V. Lunev 	struct nl_info info = {
1746d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
17474d1169c1SDenis V. Lunev 	};
1748528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1749e0a1ad73SThomas Graf }
1750e0a1ad73SThomas Graf 
175186872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
17521da177e4SLinus Torvalds {
1753c71099acSThomas Graf 	struct fib6_table *table;
17541da177e4SLinus Torvalds 	struct fib6_node *fn;
17551da177e4SLinus Torvalds 	struct rt6_info *rt;
17561da177e4SLinus Torvalds 	int err = -ESRCH;
17571da177e4SLinus Torvalds 
17585578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
175938308473SDavid S. Miller 	if (!table)
1760c71099acSThomas Graf 		return err;
17611da177e4SLinus Torvalds 
1762c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1763c71099acSThomas Graf 
1764c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
176586872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
176686872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
17671da177e4SLinus Torvalds 
17681da177e4SLinus Torvalds 	if (fn) {
1769d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
17701f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
17711f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
17721f56a01fSMartin KaFai Lau 				continue;
177386872cb5SThomas Graf 			if (cfg->fc_ifindex &&
1774d1918542SDavid S. Miller 			    (!rt->dst.dev ||
1775d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
17761da177e4SLinus Torvalds 				continue;
177786872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
177886872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
17791da177e4SLinus Torvalds 				continue;
178086872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
17811da177e4SLinus Torvalds 				continue;
1782d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1783c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
17841da177e4SLinus Torvalds 
178586872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
17861da177e4SLinus Torvalds 		}
17871da177e4SLinus Torvalds 	}
1788c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
17891da177e4SLinus Torvalds 
17901da177e4SLinus Torvalds 	return err;
17911da177e4SLinus Torvalds }
17921da177e4SLinus Torvalds 
17936700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
1794a6279458SYOSHIFUJI Hideaki {
1795e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
1796a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
1797e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
1798e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
1799e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
1800e8599ff4SDavid S. Miller 	struct neighbour *neigh;
180171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
18026e157b6aSDavid S. Miller 	int optlen, on_link;
18036e157b6aSDavid S. Miller 	u8 *lladdr;
1804e8599ff4SDavid S. Miller 
180529a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
180671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
1807e8599ff4SDavid S. Miller 
1808e8599ff4SDavid S. Miller 	if (optlen < 0) {
18096e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
1810e8599ff4SDavid S. Miller 		return;
1811e8599ff4SDavid S. Miller 	}
1812e8599ff4SDavid S. Miller 
181371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
1814e8599ff4SDavid S. Miller 
181571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
18166e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
1817e8599ff4SDavid S. Miller 		return;
1818e8599ff4SDavid S. Miller 	}
1819e8599ff4SDavid S. Miller 
18206e157b6aSDavid S. Miller 	on_link = 0;
182171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
1822e8599ff4SDavid S. Miller 		on_link = 1;
182371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
1824e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
18256e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
1826e8599ff4SDavid S. Miller 		return;
1827e8599ff4SDavid S. Miller 	}
1828e8599ff4SDavid S. Miller 
1829e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
1830e8599ff4SDavid S. Miller 	if (!in6_dev)
1831e8599ff4SDavid S. Miller 		return;
1832e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1833e8599ff4SDavid S. Miller 		return;
1834e8599ff4SDavid S. Miller 
1835e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
1836e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
1837e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
1838e8599ff4SDavid S. Miller 	 */
1839e8599ff4SDavid S. Miller 
184071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
1841e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1842e8599ff4SDavid S. Miller 		return;
1843e8599ff4SDavid S. Miller 	}
18446e157b6aSDavid S. Miller 
18456e157b6aSDavid S. Miller 	lladdr = NULL;
1846e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
1847e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1848e8599ff4SDavid S. Miller 					     skb->dev);
1849e8599ff4SDavid S. Miller 		if (!lladdr) {
1850e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1851e8599ff4SDavid S. Miller 			return;
1852e8599ff4SDavid S. Miller 		}
1853e8599ff4SDavid S. Miller 	}
1854e8599ff4SDavid S. Miller 
18556e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
18566e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
18576e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
18586e157b6aSDavid S. Miller 		return;
18596e157b6aSDavid S. Miller 	}
18606e157b6aSDavid S. Miller 
18616e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
18626e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
18636e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
18646e157b6aSDavid S. Miller 	 */
18656e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
18666e157b6aSDavid S. Miller 
186771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
1868e8599ff4SDavid S. Miller 	if (!neigh)
1869e8599ff4SDavid S. Miller 		return;
1870e8599ff4SDavid S. Miller 
18711da177e4SLinus Torvalds 	/*
18721da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
18731da177e4SLinus Torvalds 	 */
18741da177e4SLinus Torvalds 
18751da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
18761da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
18771da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
18781da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
18791da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
18801da177e4SLinus Torvalds 		     );
18811da177e4SLinus Torvalds 
188271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	nrt = ip6_rt_copy(rt, &msg->dest);
188338308473SDavid S. Miller 	if (!nrt)
18841da177e4SLinus Torvalds 		goto out;
18851da177e4SLinus Torvalds 
18861da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
18871da177e4SLinus Torvalds 	if (on_link)
18881da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
18891da177e4SLinus Torvalds 
18904e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
18911da177e4SLinus Torvalds 
189240e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
18931da177e4SLinus Torvalds 		goto out;
18941da177e4SLinus Torvalds 
1895d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
1896d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
189771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
189860592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
18998d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
19008d71740cSTom Tucker 
19011da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
19026e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
1903e0a1ad73SThomas Graf 		ip6_del_rt(rt);
19041da177e4SLinus Torvalds 	}
19051da177e4SLinus Torvalds 
19061da177e4SLinus Torvalds out:
1907e8599ff4SDavid S. Miller 	neigh_release(neigh);
19086e157b6aSDavid S. Miller }
19096e157b6aSDavid S. Miller 
19101da177e4SLinus Torvalds /*
19111da177e4SLinus Torvalds  *	Misc support functions
19121da177e4SLinus Torvalds  */
19131da177e4SLinus Torvalds 
19144b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
19154b32b5adSMartin KaFai Lau {
19164b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
19174b32b5adSMartin KaFai Lau 
19184b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
19194b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
19204b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
19214b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
19224b32b5adSMartin KaFai Lau }
19234b32b5adSMartin KaFai Lau 
19241716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
192521efcfa0SEric Dumazet 				    const struct in6_addr *dest)
19261da177e4SLinus Torvalds {
1927d1918542SDavid S. Miller 	struct net *net = dev_net(ort->dst.dev);
19284b32b5adSMartin KaFai Lau 	struct rt6_info *rt;
19294b32b5adSMartin KaFai Lau 
19304b32b5adSMartin KaFai Lau 	if (ort->rt6i_flags & RTF_CACHE)
19314b32b5adSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
19324b32b5adSMartin KaFai Lau 
19334b32b5adSMartin KaFai Lau 	rt = ip6_dst_alloc(net, ort->dst.dev, 0,
19348b96d22dSDavid S. Miller 			   ort->rt6i_table);
19351da177e4SLinus Torvalds 
19361da177e4SLinus Torvalds 	if (rt) {
1937d8d1f30bSChangli Gao 		rt->dst.input = ort->dst.input;
1938d8d1f30bSChangli Gao 		rt->dst.output = ort->dst.output;
19398e2ec639SYan, Zheng 		rt->dst.flags |= DST_HOST;
19401da177e4SLinus Torvalds 
19414e3fd7a0SAlexey Dobriyan 		rt->rt6i_dst.addr = *dest;
19428e2ec639SYan, Zheng 		rt->rt6i_dst.plen = 128;
1943d8d1f30bSChangli Gao 		rt->dst.error = ort->dst.error;
19441da177e4SLinus Torvalds 		rt->rt6i_idev = ort->rt6i_idev;
19451da177e4SLinus Torvalds 		if (rt->rt6i_idev)
19461da177e4SLinus Torvalds 			in6_dev_hold(rt->rt6i_idev);
1947d8d1f30bSChangli Gao 		rt->dst.lastuse = jiffies;
19481da177e4SLinus Torvalds 
1949550bab42SJulian Anastasov 		if (ort->rt6i_flags & RTF_GATEWAY)
19504e3fd7a0SAlexey Dobriyan 			rt->rt6i_gateway = ort->rt6i_gateway;
1951550bab42SJulian Anastasov 		else
1952550bab42SJulian Anastasov 			rt->rt6i_gateway = *dest;
19531716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
19541716a961SGao feng 		rt6_set_from(rt, ort);
19551da177e4SLinus Torvalds 		rt->rt6i_metric = 0;
19561da177e4SLinus Torvalds 
19571da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
19581da177e4SLinus Torvalds 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
19591da177e4SLinus Torvalds #endif
19600f6c6392SFlorian Westphal 		memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
1961c71099acSThomas Graf 		rt->rt6i_table = ort->rt6i_table;
19621da177e4SLinus Torvalds 	}
19631da177e4SLinus Torvalds 	return rt;
19641da177e4SLinus Torvalds }
19651da177e4SLinus Torvalds 
196670ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
1967efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
1968b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1969b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
197070ceb4f5SYOSHIFUJI Hideaki {
197170ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
197270ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
1973c71099acSThomas Graf 	struct fib6_table *table;
197470ceb4f5SYOSHIFUJI Hideaki 
1975efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
197638308473SDavid S. Miller 	if (!table)
1977c71099acSThomas Graf 		return NULL;
1978c71099acSThomas Graf 
19795744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
1980c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
198170ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
198270ceb4f5SYOSHIFUJI Hideaki 		goto out;
198370ceb4f5SYOSHIFUJI Hideaki 
1984d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1985d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
198670ceb4f5SYOSHIFUJI Hideaki 			continue;
198770ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
198870ceb4f5SYOSHIFUJI Hideaki 			continue;
198970ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
199070ceb4f5SYOSHIFUJI Hideaki 			continue;
1991d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
199270ceb4f5SYOSHIFUJI Hideaki 		break;
199370ceb4f5SYOSHIFUJI Hideaki 	}
199470ceb4f5SYOSHIFUJI Hideaki out:
19955744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
199670ceb4f5SYOSHIFUJI Hideaki 	return rt;
199770ceb4f5SYOSHIFUJI Hideaki }
199870ceb4f5SYOSHIFUJI Hideaki 
1999efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2000b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2001b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
200295c96174SEric Dumazet 					   unsigned int pref)
200370ceb4f5SYOSHIFUJI Hideaki {
200486872cb5SThomas Graf 	struct fib6_config cfg = {
200586872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
2006238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
200786872cb5SThomas Graf 		.fc_ifindex	= ifindex,
200886872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
200986872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
201086872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
201115e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2012efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2013efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
201486872cb5SThomas Graf 	};
201570ceb4f5SYOSHIFUJI Hideaki 
20164e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
20174e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
201886872cb5SThomas Graf 
2019e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2020e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
202186872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
202270ceb4f5SYOSHIFUJI Hideaki 
202386872cb5SThomas Graf 	ip6_route_add(&cfg);
202470ceb4f5SYOSHIFUJI Hideaki 
2025efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
202670ceb4f5SYOSHIFUJI Hideaki }
202770ceb4f5SYOSHIFUJI Hideaki #endif
202870ceb4f5SYOSHIFUJI Hideaki 
2029b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
20301da177e4SLinus Torvalds {
20311da177e4SLinus Torvalds 	struct rt6_info *rt;
2032c71099acSThomas Graf 	struct fib6_table *table;
20331da177e4SLinus Torvalds 
2034c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
203538308473SDavid S. Miller 	if (!table)
2036c71099acSThomas Graf 		return NULL;
20371da177e4SLinus Torvalds 
20385744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2039d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2040d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2041045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
20421da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
20431da177e4SLinus Torvalds 			break;
20441da177e4SLinus Torvalds 	}
20451da177e4SLinus Torvalds 	if (rt)
2046d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
20475744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
20481da177e4SLinus Torvalds 	return rt;
20491da177e4SLinus Torvalds }
20501da177e4SLinus Torvalds 
2051b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2052ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2053ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
20541da177e4SLinus Torvalds {
205586872cb5SThomas Graf 	struct fib6_config cfg = {
205686872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
2057238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
205886872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
205986872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
206086872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
206115e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
20625578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2063c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
206486872cb5SThomas Graf 	};
20651da177e4SLinus Torvalds 
20664e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
20671da177e4SLinus Torvalds 
206886872cb5SThomas Graf 	ip6_route_add(&cfg);
20691da177e4SLinus Torvalds 
20701da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
20711da177e4SLinus Torvalds }
20721da177e4SLinus Torvalds 
20737b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
20741da177e4SLinus Torvalds {
20751da177e4SLinus Torvalds 	struct rt6_info *rt;
2076c71099acSThomas Graf 	struct fib6_table *table;
2077c71099acSThomas Graf 
2078c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
20797b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
208038308473SDavid S. Miller 	if (!table)
2081c71099acSThomas Graf 		return;
20821da177e4SLinus Torvalds 
20831da177e4SLinus Torvalds restart:
2084c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2085d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
20863e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
20873e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2088d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2089c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2090e0a1ad73SThomas Graf 			ip6_del_rt(rt);
20911da177e4SLinus Torvalds 			goto restart;
20921da177e4SLinus Torvalds 		}
20931da177e4SLinus Torvalds 	}
2094c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
20951da177e4SLinus Torvalds }
20961da177e4SLinus Torvalds 
20975578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
20985578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
209986872cb5SThomas Graf 				 struct fib6_config *cfg)
210086872cb5SThomas Graf {
210186872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
210286872cb5SThomas Graf 
210386872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
210486872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
210586872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
210686872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
210786872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
210886872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
210986872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
211086872cb5SThomas Graf 
21115578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2112f1243c2dSBenjamin Thery 
21134e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
21144e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
21154e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
211686872cb5SThomas Graf }
211786872cb5SThomas Graf 
21185578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
21191da177e4SLinus Torvalds {
212086872cb5SThomas Graf 	struct fib6_config cfg;
21211da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
21221da177e4SLinus Torvalds 	int err;
21231da177e4SLinus Torvalds 
21241da177e4SLinus Torvalds 	switch (cmd) {
21251da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
21261da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2127af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
21281da177e4SLinus Torvalds 			return -EPERM;
21291da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
21301da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
21311da177e4SLinus Torvalds 		if (err)
21321da177e4SLinus Torvalds 			return -EFAULT;
21331da177e4SLinus Torvalds 
21345578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
213586872cb5SThomas Graf 
21361da177e4SLinus Torvalds 		rtnl_lock();
21371da177e4SLinus Torvalds 		switch (cmd) {
21381da177e4SLinus Torvalds 		case SIOCADDRT:
213986872cb5SThomas Graf 			err = ip6_route_add(&cfg);
21401da177e4SLinus Torvalds 			break;
21411da177e4SLinus Torvalds 		case SIOCDELRT:
214286872cb5SThomas Graf 			err = ip6_route_del(&cfg);
21431da177e4SLinus Torvalds 			break;
21441da177e4SLinus Torvalds 		default:
21451da177e4SLinus Torvalds 			err = -EINVAL;
21461da177e4SLinus Torvalds 		}
21471da177e4SLinus Torvalds 		rtnl_unlock();
21481da177e4SLinus Torvalds 
21491da177e4SLinus Torvalds 		return err;
21503ff50b79SStephen Hemminger 	}
21511da177e4SLinus Torvalds 
21521da177e4SLinus Torvalds 	return -EINVAL;
21531da177e4SLinus Torvalds }
21541da177e4SLinus Torvalds 
21551da177e4SLinus Torvalds /*
21561da177e4SLinus Torvalds  *	Drop the packet on the floor
21571da177e4SLinus Torvalds  */
21581da177e4SLinus Torvalds 
2159d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
21601da177e4SLinus Torvalds {
2161612f09e8SYOSHIFUJI Hideaki 	int type;
2162adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2163612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2164612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
21650660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
216645bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
21673bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
21683bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2169612f09e8SYOSHIFUJI Hideaki 			break;
2170612f09e8SYOSHIFUJI Hideaki 		}
2171612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2172612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
21733bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
21743bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2175612f09e8SYOSHIFUJI Hideaki 		break;
2176612f09e8SYOSHIFUJI Hideaki 	}
21773ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
21781da177e4SLinus Torvalds 	kfree_skb(skb);
21791da177e4SLinus Torvalds 	return 0;
21801da177e4SLinus Torvalds }
21811da177e4SLinus Torvalds 
21829ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
21839ce8ade0SThomas Graf {
2184612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
21859ce8ade0SThomas Graf }
21869ce8ade0SThomas Graf 
2187aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb)
21881da177e4SLinus Torvalds {
2189adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2190612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
21911da177e4SLinus Torvalds }
21921da177e4SLinus Torvalds 
21939ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
21949ce8ade0SThomas Graf {
2195612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
21969ce8ade0SThomas Graf }
21979ce8ade0SThomas Graf 
2198aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb)
21999ce8ade0SThomas Graf {
2200adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2201612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
22029ce8ade0SThomas Graf }
22039ce8ade0SThomas Graf 
22041da177e4SLinus Torvalds /*
22051da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
22061da177e4SLinus Torvalds  */
22071da177e4SLinus Torvalds 
22081da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
22091da177e4SLinus Torvalds 				    const struct in6_addr *addr,
22108f031519SDavid S. Miller 				    bool anycast)
22111da177e4SLinus Torvalds {
2212c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2213a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2214a3300ef4SHannes Frederic Sowa 					    DST_NOCOUNT, NULL);
2215a3300ef4SHannes Frederic Sowa 	if (!rt)
22161da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
22171da177e4SLinus Torvalds 
22181da177e4SLinus Torvalds 	in6_dev_hold(idev);
22191da177e4SLinus Torvalds 
222011d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2221d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2222d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
22231da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
22241da177e4SLinus Torvalds 
22251da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
222658c4fb86SYOSHIFUJI Hideaki 	if (anycast)
222758c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
222858c4fb86SYOSHIFUJI Hideaki 	else
22291da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
22301da177e4SLinus Torvalds 
2231550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
22324e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
22331da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
22345578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
22351da177e4SLinus Torvalds 
2236d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
22371da177e4SLinus Torvalds 
22381da177e4SLinus Torvalds 	return rt;
22391da177e4SLinus Torvalds }
22401da177e4SLinus Torvalds 
2241c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2242c3968a85SDaniel Walter 			struct rt6_info *rt,
2243b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2244c3968a85SDaniel Walter 			unsigned int prefs,
2245c3968a85SDaniel Walter 			struct in6_addr *saddr)
2246c3968a85SDaniel Walter {
2247e16e888bSMarkus Stenberg 	struct inet6_dev *idev =
2248e16e888bSMarkus Stenberg 		rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL;
2249c3968a85SDaniel Walter 	int err = 0;
2250e16e888bSMarkus Stenberg 	if (rt && rt->rt6i_prefsrc.plen)
22514e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2252c3968a85SDaniel Walter 	else
2253c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2254c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2255c3968a85SDaniel Walter 	return err;
2256c3968a85SDaniel Walter }
2257c3968a85SDaniel Walter 
2258c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2259c3968a85SDaniel Walter struct arg_dev_net_ip {
2260c3968a85SDaniel Walter 	struct net_device *dev;
2261c3968a85SDaniel Walter 	struct net *net;
2262c3968a85SDaniel Walter 	struct in6_addr *addr;
2263c3968a85SDaniel Walter };
2264c3968a85SDaniel Walter 
2265c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2266c3968a85SDaniel Walter {
2267c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2268c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2269c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2270c3968a85SDaniel Walter 
2271d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2272c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2273c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2274c3968a85SDaniel Walter 		/* remove prefsrc entry */
2275c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2276c3968a85SDaniel Walter 	}
2277c3968a85SDaniel Walter 	return 0;
2278c3968a85SDaniel Walter }
2279c3968a85SDaniel Walter 
2280c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2281c3968a85SDaniel Walter {
2282c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2283c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2284c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2285c3968a85SDaniel Walter 		.net = net,
2286c3968a85SDaniel Walter 		.addr = &ifp->addr,
2287c3968a85SDaniel Walter 	};
22880c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2289c3968a85SDaniel Walter }
2290c3968a85SDaniel Walter 
2291be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2292be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2293be7a010dSDuan Jiong 
2294be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2295be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2296be7a010dSDuan Jiong {
2297be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2298be7a010dSDuan Jiong 
2299be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2300be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2301be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2302be7a010dSDuan Jiong 		return -1;
2303be7a010dSDuan Jiong 	}
2304be7a010dSDuan Jiong 	return 0;
2305be7a010dSDuan Jiong }
2306be7a010dSDuan Jiong 
2307be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2308be7a010dSDuan Jiong {
2309be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2310be7a010dSDuan Jiong }
2311be7a010dSDuan Jiong 
23128ed67789SDaniel Lezcano struct arg_dev_net {
23138ed67789SDaniel Lezcano 	struct net_device *dev;
23148ed67789SDaniel Lezcano 	struct net *net;
23158ed67789SDaniel Lezcano };
23168ed67789SDaniel Lezcano 
23171da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
23181da177e4SLinus Torvalds {
2319bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2320bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
23218ed67789SDaniel Lezcano 
2322d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2323c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
23241da177e4SLinus Torvalds 		return -1;
2325c159d30cSDavid S. Miller 
23261da177e4SLinus Torvalds 	return 0;
23271da177e4SLinus Torvalds }
23281da177e4SLinus Torvalds 
2329f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
23301da177e4SLinus Torvalds {
23318ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
23328ed67789SDaniel Lezcano 		.dev = dev,
23338ed67789SDaniel Lezcano 		.net = net,
23348ed67789SDaniel Lezcano 	};
23358ed67789SDaniel Lezcano 
23360c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
23371e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
23381da177e4SLinus Torvalds }
23391da177e4SLinus Torvalds 
234095c96174SEric Dumazet struct rt6_mtu_change_arg {
23411da177e4SLinus Torvalds 	struct net_device *dev;
234295c96174SEric Dumazet 	unsigned int mtu;
23431da177e4SLinus Torvalds };
23441da177e4SLinus Torvalds 
23451da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
23461da177e4SLinus Torvalds {
23471da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
23481da177e4SLinus Torvalds 	struct inet6_dev *idev;
23491da177e4SLinus Torvalds 
23501da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
23511da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
23521da177e4SLinus Torvalds 	   We still use this lock to block changes
23531da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
23541da177e4SLinus Torvalds 	*/
23551da177e4SLinus Torvalds 
23561da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
235738308473SDavid S. Miller 	if (!idev)
23581da177e4SLinus Torvalds 		return 0;
23591da177e4SLinus Torvalds 
23601da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
23611da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
23621da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
23631da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
23641da177e4SLinus Torvalds 	 */
23651da177e4SLinus Torvalds 	/*
23661da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
23671da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
23681da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
23691da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
23701da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
23711da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
23721da177e4SLinus Torvalds 	   PMTU discouvery.
23731da177e4SLinus Torvalds 	 */
2374d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
23754b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
23764b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
23774b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
23784b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
23794b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
23804b32b5adSMartin KaFai Lau 			 * been updated.
23814b32b5adSMartin KaFai Lau 			 */
23824b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
23834b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
23844b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2385d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
23864b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2387defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2388566cfd8fSSimon Arlott 		}
23894b32b5adSMartin KaFai Lau 	}
23901da177e4SLinus Torvalds 	return 0;
23911da177e4SLinus Torvalds }
23921da177e4SLinus Torvalds 
239395c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
23941da177e4SLinus Torvalds {
2395c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2396c71099acSThomas Graf 		.dev = dev,
2397c71099acSThomas Graf 		.mtu = mtu,
2398c71099acSThomas Graf 	};
23991da177e4SLinus Torvalds 
24000c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
24011da177e4SLinus Torvalds }
24021da177e4SLinus Torvalds 
2403ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
24045176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
240586872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2406ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
240786872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
240886872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
240951ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2410c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
241186872cb5SThomas Graf };
241286872cb5SThomas Graf 
241386872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
241486872cb5SThomas Graf 			      struct fib6_config *cfg)
24151da177e4SLinus Torvalds {
241686872cb5SThomas Graf 	struct rtmsg *rtm;
241786872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2418c78ba6d6SLubomir Rintel 	unsigned int pref;
241986872cb5SThomas Graf 	int err;
24201da177e4SLinus Torvalds 
242186872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
242286872cb5SThomas Graf 	if (err < 0)
242386872cb5SThomas Graf 		goto errout;
24241da177e4SLinus Torvalds 
242586872cb5SThomas Graf 	err = -EINVAL;
242686872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
242786872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
242886872cb5SThomas Graf 
242986872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
243086872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
243186872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
243286872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
243386872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2434ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
243586872cb5SThomas Graf 
2436ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2437ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2438b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2439b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
244086872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
244186872cb5SThomas Graf 
2442ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2443ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2444ab79ad14SMaciej Żenczykowski 
24451f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
24461f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
24471f56a01fSMartin KaFai Lau 
244815e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
244986872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
24503b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
245186872cb5SThomas Graf 
245286872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
245367b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
245486872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
24551da177e4SLinus Torvalds 	}
245686872cb5SThomas Graf 
245786872cb5SThomas Graf 	if (tb[RTA_DST]) {
245886872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
245986872cb5SThomas Graf 
246086872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
246186872cb5SThomas Graf 			goto errout;
246286872cb5SThomas Graf 
246386872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
24641da177e4SLinus Torvalds 	}
246586872cb5SThomas Graf 
246686872cb5SThomas Graf 	if (tb[RTA_SRC]) {
246786872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
246886872cb5SThomas Graf 
246986872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
247086872cb5SThomas Graf 			goto errout;
247186872cb5SThomas Graf 
247286872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
24731da177e4SLinus Torvalds 	}
247486872cb5SThomas Graf 
2475c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
247667b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2477c3968a85SDaniel Walter 
247886872cb5SThomas Graf 	if (tb[RTA_OIF])
247986872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
248086872cb5SThomas Graf 
248186872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
248286872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
248386872cb5SThomas Graf 
248486872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
248586872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
248686872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
24871da177e4SLinus Torvalds 	}
248886872cb5SThomas Graf 
248986872cb5SThomas Graf 	if (tb[RTA_TABLE])
249086872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
249186872cb5SThomas Graf 
249251ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
249351ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
249451ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
249551ebd318SNicolas Dichtel 	}
249651ebd318SNicolas Dichtel 
2497c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2498c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2499c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2500c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2501c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2502c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2503c78ba6d6SLubomir Rintel 	}
2504c78ba6d6SLubomir Rintel 
250586872cb5SThomas Graf 	err = 0;
250686872cb5SThomas Graf errout:
250786872cb5SThomas Graf 	return err;
25081da177e4SLinus Torvalds }
25091da177e4SLinus Torvalds 
251051ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add)
251151ebd318SNicolas Dichtel {
251251ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
251351ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
251451ebd318SNicolas Dichtel 	int remaining;
251551ebd318SNicolas Dichtel 	int attrlen;
251651ebd318SNicolas Dichtel 	int err = 0, last_err = 0;
251751ebd318SNicolas Dichtel 
251851ebd318SNicolas Dichtel beginning:
251951ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
252051ebd318SNicolas Dichtel 	remaining = cfg->fc_mp_len;
252151ebd318SNicolas Dichtel 
252251ebd318SNicolas Dichtel 	/* Parse a Multipath Entry */
252351ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
252451ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
252551ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
252651ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
252751ebd318SNicolas Dichtel 
252851ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
252951ebd318SNicolas Dichtel 		if (attrlen > 0) {
253051ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
253151ebd318SNicolas Dichtel 
253251ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
253351ebd318SNicolas Dichtel 			if (nla) {
253467b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
253551ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
253651ebd318SNicolas Dichtel 			}
253751ebd318SNicolas Dichtel 		}
253851ebd318SNicolas Dichtel 		err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
253951ebd318SNicolas Dichtel 		if (err) {
254051ebd318SNicolas Dichtel 			last_err = err;
254151ebd318SNicolas Dichtel 			/* If we are trying to remove a route, do not stop the
254251ebd318SNicolas Dichtel 			 * loop when ip6_route_del() fails (because next hop is
254351ebd318SNicolas Dichtel 			 * already gone), we should try to remove all next hops.
254451ebd318SNicolas Dichtel 			 */
254551ebd318SNicolas Dichtel 			if (add) {
254651ebd318SNicolas Dichtel 				/* If add fails, we should try to delete all
254751ebd318SNicolas Dichtel 				 * next hops that have been already added.
254851ebd318SNicolas Dichtel 				 */
254951ebd318SNicolas Dichtel 				add = 0;
255051ebd318SNicolas Dichtel 				goto beginning;
255151ebd318SNicolas Dichtel 			}
255251ebd318SNicolas Dichtel 		}
25531a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
25541a72418bSNicolas Dichtel 		 * this flag after the first nexthop (if there is a collision,
25551a72418bSNicolas Dichtel 		 * we have already fail to add the first nexthop:
25561a72418bSNicolas Dichtel 		 * fib6_add_rt2node() has reject it).
25571a72418bSNicolas Dichtel 		 */
25581a72418bSNicolas Dichtel 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL;
255951ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
256051ebd318SNicolas Dichtel 	}
256151ebd318SNicolas Dichtel 
256251ebd318SNicolas Dichtel 	return last_err;
256351ebd318SNicolas Dichtel }
256451ebd318SNicolas Dichtel 
2565661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
25661da177e4SLinus Torvalds {
256786872cb5SThomas Graf 	struct fib6_config cfg;
256886872cb5SThomas Graf 	int err;
25691da177e4SLinus Torvalds 
257086872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
257186872cb5SThomas Graf 	if (err < 0)
257286872cb5SThomas Graf 		return err;
257386872cb5SThomas Graf 
257451ebd318SNicolas Dichtel 	if (cfg.fc_mp)
257551ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 0);
257651ebd318SNicolas Dichtel 	else
257786872cb5SThomas Graf 		return ip6_route_del(&cfg);
25781da177e4SLinus Torvalds }
25791da177e4SLinus Torvalds 
2580661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
25811da177e4SLinus Torvalds {
258286872cb5SThomas Graf 	struct fib6_config cfg;
258386872cb5SThomas Graf 	int err;
25841da177e4SLinus Torvalds 
258586872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
258686872cb5SThomas Graf 	if (err < 0)
258786872cb5SThomas Graf 		return err;
258886872cb5SThomas Graf 
258951ebd318SNicolas Dichtel 	if (cfg.fc_mp)
259051ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 1);
259151ebd318SNicolas Dichtel 	else
259286872cb5SThomas Graf 		return ip6_route_add(&cfg);
25931da177e4SLinus Torvalds }
25941da177e4SLinus Torvalds 
2595339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void)
2596339bf98fSThomas Graf {
2597339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2598339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2599339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2600339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2601339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2602339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2603339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2604339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2605339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
26066a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2607ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
2608c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
2609c78ba6d6SLubomir Rintel 	       + nla_total_size(1); /* RTA_PREF */
2610339bf98fSThomas Graf }
2611339bf98fSThomas Graf 
2612191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2613191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
26140d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
261515e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
26167bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
26171da177e4SLinus Torvalds {
26184b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
26191da177e4SLinus Torvalds 	struct rtmsg *rtm;
26201da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2621e3703b3dSThomas Graf 	long expires;
26229e762a4aSPatrick McHardy 	u32 table;
26231da177e4SLinus Torvalds 
26241da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
26251da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
26261da177e4SLinus Torvalds 			/* success since this is not a prefix route */
26271da177e4SLinus Torvalds 			return 1;
26281da177e4SLinus Torvalds 		}
26291da177e4SLinus Torvalds 	}
26301da177e4SLinus Torvalds 
263115e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
263238308473SDavid S. Miller 	if (!nlh)
263326932566SPatrick McHardy 		return -EMSGSIZE;
26342d7202bfSThomas Graf 
26352d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
26361da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
26371da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
26381da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
26391da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2640c71099acSThomas Graf 	if (rt->rt6i_table)
26419e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2642c71099acSThomas Graf 	else
26439e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
26449e762a4aSPatrick McHardy 	rtm->rtm_table = table;
2645c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
2646c78679e8SDavid S. Miller 		goto nla_put_failure;
2647ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
2648ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
2649ef2c7d7bSNicolas Dichtel 		case -EINVAL:
2650ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
2651ef2c7d7bSNicolas Dichtel 			break;
2652ef2c7d7bSNicolas Dichtel 		case -EACCES:
2653ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
2654ef2c7d7bSNicolas Dichtel 			break;
2655b4949ab2SNicolas Dichtel 		case -EAGAIN:
2656b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
2657b4949ab2SNicolas Dichtel 			break;
2658ef2c7d7bSNicolas Dichtel 		default:
26591da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
2660ef2c7d7bSNicolas Dichtel 			break;
2661ef2c7d7bSNicolas Dichtel 		}
2662ef2c7d7bSNicolas Dichtel 	}
2663ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2664ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2665d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
26661da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
26671da177e4SLinus Torvalds 	else
26681da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
26691da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
26701da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
26711da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
26721da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
26731da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
2674f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
2675f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
26761da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
2677f0396f60SDenis Ovsienko 		else
2678f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
2679f0396f60SDenis Ovsienko 	}
26801da177e4SLinus Torvalds 
26811da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
26821da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
26831da177e4SLinus Torvalds 
26841da177e4SLinus Torvalds 	if (dst) {
2685930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
2686c78679e8SDavid S. Miller 			goto nla_put_failure;
26871da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
26881da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
2689930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
2690c78679e8SDavid S. Miller 			goto nla_put_failure;
26911da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
26921da177e4SLinus Torvalds 	if (src) {
2693930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
2694c78679e8SDavid S. Miller 			goto nla_put_failure;
26951da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
2696c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
2697930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
2698c78679e8SDavid S. Miller 		goto nla_put_failure;
26991da177e4SLinus Torvalds #endif
27007bc570c8SYOSHIFUJI Hideaki 	if (iif) {
27017bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
27027bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
27038229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
27047bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
27057bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
27067bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
27077bc570c8SYOSHIFUJI Hideaki 						return 0;
27087bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
27097bc570c8SYOSHIFUJI Hideaki 				} else {
27107bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
27117bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
27127bc570c8SYOSHIFUJI Hideaki 				}
27137bc570c8SYOSHIFUJI Hideaki 			}
27147bc570c8SYOSHIFUJI Hideaki 		} else
27157bc570c8SYOSHIFUJI Hideaki #endif
2716c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
2717c78679e8SDavid S. Miller 				goto nla_put_failure;
27187bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
27191da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2720c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2721930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2722c78679e8SDavid S. Miller 			goto nla_put_failure;
2723c3968a85SDaniel Walter 	}
2724c3968a85SDaniel Walter 
2725c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2726c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
27274e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
2728930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2729c78679e8SDavid S. Miller 			goto nla_put_failure;
27301da177e4SLinus Torvalds 	}
27312d7202bfSThomas Graf 
27324b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
27334b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
27344b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
27354b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
27362d7202bfSThomas Graf 		goto nla_put_failure;
27372d7202bfSThomas Graf 
2738dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
2739930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
274094f826b8SEric Dumazet 			goto nla_put_failure;
274194f826b8SEric Dumazet 	}
27422d7202bfSThomas Graf 
2743c78679e8SDavid S. Miller 	if (rt->dst.dev &&
2744c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2745c78679e8SDavid S. Miller 		goto nla_put_failure;
2746c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2747c78679e8SDavid S. Miller 		goto nla_put_failure;
27488253947eSLi Wei 
27498253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
275069cdf8f9SYOSHIFUJI Hideaki 
275187a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
2752e3703b3dSThomas Graf 		goto nla_put_failure;
27531da177e4SLinus Torvalds 
2754c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
2755c78ba6d6SLubomir Rintel 		goto nla_put_failure;
2756c78ba6d6SLubomir Rintel 
2757053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2758053c095aSJohannes Berg 	return 0;
27592d7202bfSThomas Graf 
27602d7202bfSThomas Graf nla_put_failure:
276126932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
276226932566SPatrick McHardy 	return -EMSGSIZE;
27631da177e4SLinus Torvalds }
27641da177e4SLinus Torvalds 
27651b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
27661da177e4SLinus Torvalds {
27671da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
27681da177e4SLinus Torvalds 	int prefix;
27691da177e4SLinus Torvalds 
27702d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
27712d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
27721da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
27731da177e4SLinus Torvalds 	} else
27741da177e4SLinus Torvalds 		prefix = 0;
27751da177e4SLinus Torvalds 
2776191cd582SBrian Haley 	return rt6_fill_node(arg->net,
2777191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
277815e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
27797bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
27801da177e4SLinus Torvalds }
27811da177e4SLinus Torvalds 
2782661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
27831da177e4SLinus Torvalds {
27843b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
2785ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
27861da177e4SLinus Torvalds 	struct rt6_info *rt;
2787ab364a6fSThomas Graf 	struct sk_buff *skb;
2788ab364a6fSThomas Graf 	struct rtmsg *rtm;
27894c9483b2SDavid S. Miller 	struct flowi6 fl6;
279072331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
2791ab364a6fSThomas Graf 
2792ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2793ab364a6fSThomas Graf 	if (err < 0)
2794ab364a6fSThomas Graf 		goto errout;
2795ab364a6fSThomas Graf 
2796ab364a6fSThomas Graf 	err = -EINVAL;
27974c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2798ab364a6fSThomas Graf 
2799ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
2800ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2801ab364a6fSThomas Graf 			goto errout;
2802ab364a6fSThomas Graf 
28034e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
2804ab364a6fSThomas Graf 	}
2805ab364a6fSThomas Graf 
2806ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
2807ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2808ab364a6fSThomas Graf 			goto errout;
2809ab364a6fSThomas Graf 
28104e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
2811ab364a6fSThomas Graf 	}
2812ab364a6fSThomas Graf 
2813ab364a6fSThomas Graf 	if (tb[RTA_IIF])
2814ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
2815ab364a6fSThomas Graf 
2816ab364a6fSThomas Graf 	if (tb[RTA_OIF])
281772331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
2818ab364a6fSThomas Graf 
28192e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
28202e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
28212e47b291SLorenzo Colitti 
2822ab364a6fSThomas Graf 	if (iif) {
2823ab364a6fSThomas Graf 		struct net_device *dev;
282472331bc0SShmulik Ladkani 		int flags = 0;
282572331bc0SShmulik Ladkani 
28265578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
2827ab364a6fSThomas Graf 		if (!dev) {
2828ab364a6fSThomas Graf 			err = -ENODEV;
2829ab364a6fSThomas Graf 			goto errout;
2830ab364a6fSThomas Graf 		}
283172331bc0SShmulik Ladkani 
283272331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
283372331bc0SShmulik Ladkani 
283472331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
283572331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
283672331bc0SShmulik Ladkani 
283772331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
283872331bc0SShmulik Ladkani 							       flags);
283972331bc0SShmulik Ladkani 	} else {
284072331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
284172331bc0SShmulik Ladkani 
284272331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
2843ab364a6fSThomas Graf 	}
28441da177e4SLinus Torvalds 
28451da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
284638308473SDavid S. Miller 	if (!skb) {
284794e187c0SAmerigo Wang 		ip6_rt_put(rt);
2848ab364a6fSThomas Graf 		err = -ENOBUFS;
2849ab364a6fSThomas Graf 		goto errout;
2850ab364a6fSThomas Graf 	}
28511da177e4SLinus Torvalds 
28521da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
28531da177e4SLinus Torvalds 	   through good chunk of routing engine.
28541da177e4SLinus Torvalds 	 */
2855459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
28561da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
28571da177e4SLinus Torvalds 
2858d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
28591da177e4SLinus Torvalds 
28604c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
286115e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
28627bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
28631da177e4SLinus Torvalds 	if (err < 0) {
2864ab364a6fSThomas Graf 		kfree_skb(skb);
2865ab364a6fSThomas Graf 		goto errout;
28661da177e4SLinus Torvalds 	}
28671da177e4SLinus Torvalds 
286815e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
2869ab364a6fSThomas Graf errout:
28701da177e4SLinus Torvalds 	return err;
28711da177e4SLinus Torvalds }
28721da177e4SLinus Torvalds 
287386872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
28741da177e4SLinus Torvalds {
28751da177e4SLinus Torvalds 	struct sk_buff *skb;
28765578689aSDaniel Lezcano 	struct net *net = info->nl_net;
2877528c4cebSDenis V. Lunev 	u32 seq;
2878528c4cebSDenis V. Lunev 	int err;
28790d51aa80SJamal Hadi Salim 
2880528c4cebSDenis V. Lunev 	err = -ENOBUFS;
288138308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
288286872cb5SThomas Graf 
2883339bf98fSThomas Graf 	skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
288438308473SDavid S. Miller 	if (!skb)
288521713ebcSThomas Graf 		goto errout;
28861da177e4SLinus Torvalds 
2887191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
288815e47304SEric W. Biederman 				event, info->portid, seq, 0, 0, 0);
288926932566SPatrick McHardy 	if (err < 0) {
289026932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
289126932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
289226932566SPatrick McHardy 		kfree_skb(skb);
289326932566SPatrick McHardy 		goto errout;
289426932566SPatrick McHardy 	}
289515e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
28965578689aSDaniel Lezcano 		    info->nlh, gfp_any());
28971ce85fe4SPablo Neira Ayuso 	return;
289821713ebcSThomas Graf errout:
289921713ebcSThomas Graf 	if (err < 0)
29005578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
29011da177e4SLinus Torvalds }
29021da177e4SLinus Torvalds 
29038ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
2904351638e7SJiri Pirko 				unsigned long event, void *ptr)
29058ed67789SDaniel Lezcano {
2906351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
2907c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
29088ed67789SDaniel Lezcano 
29098ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
2910d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
29118ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
29128ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2913d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
29148ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
2915d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
29168ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
29178ed67789SDaniel Lezcano #endif
29188ed67789SDaniel Lezcano 	}
29198ed67789SDaniel Lezcano 
29208ed67789SDaniel Lezcano 	return NOTIFY_OK;
29218ed67789SDaniel Lezcano }
29228ed67789SDaniel Lezcano 
29231da177e4SLinus Torvalds /*
29241da177e4SLinus Torvalds  *	/proc
29251da177e4SLinus Torvalds  */
29261da177e4SLinus Torvalds 
29271da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
29281da177e4SLinus Torvalds 
292933120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
293033120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
293133120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
293233120b30SAlexey Dobriyan 	.read		= seq_read,
293333120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
29348d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
293533120b30SAlexey Dobriyan };
293633120b30SAlexey Dobriyan 
29371da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
29381da177e4SLinus Torvalds {
293969ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
29401da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
294169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
294269ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
294369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
294469ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
294569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
2946fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
294769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
29481da177e4SLinus Torvalds 
29491da177e4SLinus Torvalds 	return 0;
29501da177e4SLinus Torvalds }
29511da177e4SLinus Torvalds 
29521da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
29531da177e4SLinus Torvalds {
2954de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
295569ddb805SDaniel Lezcano }
295669ddb805SDaniel Lezcano 
29579a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
29581da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
29591da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
29601da177e4SLinus Torvalds 	.read	 = seq_read,
29611da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
2962b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
29631da177e4SLinus Torvalds };
29641da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
29651da177e4SLinus Torvalds 
29661da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
29671da177e4SLinus Torvalds 
29681da177e4SLinus Torvalds static
2969fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
29701da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
29711da177e4SLinus Torvalds {
2972c486da34SLucian Adrian Grijincu 	struct net *net;
2973c486da34SLucian Adrian Grijincu 	int delay;
2974c486da34SLucian Adrian Grijincu 	if (!write)
2975c486da34SLucian Adrian Grijincu 		return -EINVAL;
2976c486da34SLucian Adrian Grijincu 
2977c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
2978c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
29798d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
29802ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
29811da177e4SLinus Torvalds 	return 0;
29821da177e4SLinus Torvalds }
29831da177e4SLinus Torvalds 
2984fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
29851da177e4SLinus Torvalds 	{
29861da177e4SLinus Torvalds 		.procname	=	"flush",
29874990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
29881da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
298989c8b3a1SDave Jones 		.mode		=	0200,
29906d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
29911da177e4SLinus Torvalds 	},
29921da177e4SLinus Torvalds 	{
29931da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
29949a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
29951da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
29961da177e4SLinus Torvalds 		.mode		=	0644,
29976d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
29981da177e4SLinus Torvalds 	},
29991da177e4SLinus Torvalds 	{
30001da177e4SLinus Torvalds 		.procname	=	"max_size",
30014990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
30021da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30031da177e4SLinus Torvalds 		.mode		=	0644,
30046d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
30051da177e4SLinus Torvalds 	},
30061da177e4SLinus Torvalds 	{
30071da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
30084990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
30091da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30101da177e4SLinus Torvalds 		.mode		=	0644,
30116d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30121da177e4SLinus Torvalds 	},
30131da177e4SLinus Torvalds 	{
30141da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
30154990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
30161da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30171da177e4SLinus Torvalds 		.mode		=	0644,
30186d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30191da177e4SLinus Torvalds 	},
30201da177e4SLinus Torvalds 	{
30211da177e4SLinus Torvalds 		.procname	=	"gc_interval",
30224990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
30231da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30241da177e4SLinus Torvalds 		.mode		=	0644,
30256d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30261da177e4SLinus Torvalds 	},
30271da177e4SLinus Torvalds 	{
30281da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
30294990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
30301da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30311da177e4SLinus Torvalds 		.mode		=	0644,
3032f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
30331da177e4SLinus Torvalds 	},
30341da177e4SLinus Torvalds 	{
30351da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
30364990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
30371da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30381da177e4SLinus Torvalds 		.mode		=	0644,
30396d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30401da177e4SLinus Torvalds 	},
30411da177e4SLinus Torvalds 	{
30421da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
30434990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
30441da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30451da177e4SLinus Torvalds 		.mode		=	0644,
3046f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
30471da177e4SLinus Torvalds 	},
30481da177e4SLinus Torvalds 	{
30491da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
30504990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
30511da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30521da177e4SLinus Torvalds 		.mode		=	0644,
30536d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
30541da177e4SLinus Torvalds 	},
3055f8572d8fSEric W. Biederman 	{ }
30561da177e4SLinus Torvalds };
30571da177e4SLinus Torvalds 
30582c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3059760f2d01SDaniel Lezcano {
3060760f2d01SDaniel Lezcano 	struct ctl_table *table;
3061760f2d01SDaniel Lezcano 
3062760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3063760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3064760f2d01SDaniel Lezcano 			GFP_KERNEL);
30655ee09105SYOSHIFUJI Hideaki 
30665ee09105SYOSHIFUJI Hideaki 	if (table) {
30675ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3068c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
306986393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
30705ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
30715ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
30725ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
30735ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
30745ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
30755ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
30765ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
30779c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3078464dc801SEric W. Biederman 
3079464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3080464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3081464dc801SEric W. Biederman 			table[0].procname = NULL;
30825ee09105SYOSHIFUJI Hideaki 	}
30835ee09105SYOSHIFUJI Hideaki 
3084760f2d01SDaniel Lezcano 	return table;
3085760f2d01SDaniel Lezcano }
30861da177e4SLinus Torvalds #endif
30871da177e4SLinus Torvalds 
30882c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3089cdb18761SDaniel Lezcano {
3090633d424bSPavel Emelyanov 	int ret = -ENOMEM;
30918ed67789SDaniel Lezcano 
309286393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
309386393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3094f2fc6a54SBenjamin Thery 
3095fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3096fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3097fc66f95cSEric Dumazet 
30988ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
30998ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
31008ed67789SDaniel Lezcano 					   GFP_KERNEL);
31018ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3102fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3103d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
31048ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3105d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
310662fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
310762fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
31088ed67789SDaniel Lezcano 
31098ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
31108ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
31118ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
31128ed67789SDaniel Lezcano 					       GFP_KERNEL);
311368fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
311468fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3115d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
31168ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3117d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
311862fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
311962fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
31208ed67789SDaniel Lezcano 
31218ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
31228ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
31238ed67789SDaniel Lezcano 					       GFP_KERNEL);
312468fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
312568fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3126d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
31278ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3128d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
312962fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
313062fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
31318ed67789SDaniel Lezcano #endif
31328ed67789SDaniel Lezcano 
3133b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3134b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3135b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3136b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3137b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3138b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3139b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3140b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3141b339a47cSPeter Zijlstra 
31426891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
31436891a346SBenjamin Thery 
31448ed67789SDaniel Lezcano 	ret = 0;
31458ed67789SDaniel Lezcano out:
31468ed67789SDaniel Lezcano 	return ret;
3147f2fc6a54SBenjamin Thery 
314868fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
314968fffc67SPeter Zijlstra out_ip6_prohibit_entry:
315068fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
315168fffc67SPeter Zijlstra out_ip6_null_entry:
315268fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
315368fffc67SPeter Zijlstra #endif
3154fc66f95cSEric Dumazet out_ip6_dst_entries:
3155fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3156f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3157f2fc6a54SBenjamin Thery 	goto out;
3158cdb18761SDaniel Lezcano }
3159cdb18761SDaniel Lezcano 
31602c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3161cdb18761SDaniel Lezcano {
31628ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
31638ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
31648ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
31658ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
31668ed67789SDaniel Lezcano #endif
316741bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3168cdb18761SDaniel Lezcano }
3169cdb18761SDaniel Lezcano 
3170d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3171d189634eSThomas Graf {
3172d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3173d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3174d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3175d189634eSThomas Graf #endif
3176d189634eSThomas Graf 	return 0;
3177d189634eSThomas Graf }
3178d189634eSThomas Graf 
3179d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3180d189634eSThomas Graf {
3181d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3182ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3183ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3184d189634eSThomas Graf #endif
3185d189634eSThomas Graf }
3186d189634eSThomas Graf 
3187cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3188cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3189cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3190cdb18761SDaniel Lezcano };
3191cdb18761SDaniel Lezcano 
3192c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3193c3426b47SDavid S. Miller {
3194c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3195c3426b47SDavid S. Miller 
3196c3426b47SDavid S. Miller 	if (!bp)
3197c3426b47SDavid S. Miller 		return -ENOMEM;
3198c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3199c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3200c3426b47SDavid S. Miller 	return 0;
3201c3426b47SDavid S. Miller }
3202c3426b47SDavid S. Miller 
3203c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3204c3426b47SDavid S. Miller {
3205c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3206c3426b47SDavid S. Miller 
3207c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
320856a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3209c3426b47SDavid S. Miller 	kfree(bp);
3210c3426b47SDavid S. Miller }
3211c3426b47SDavid S. Miller 
32122b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3213c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3214c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3215c3426b47SDavid S. Miller };
3216c3426b47SDavid S. Miller 
3217d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3218d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3219d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3220d189634eSThomas Graf };
3221d189634eSThomas Graf 
32228ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
32238ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
32248ed67789SDaniel Lezcano 	.priority = 0,
32258ed67789SDaniel Lezcano };
32268ed67789SDaniel Lezcano 
3227433d49c3SDaniel Lezcano int __init ip6_route_init(void)
32281da177e4SLinus Torvalds {
3229433d49c3SDaniel Lezcano 	int ret;
3230433d49c3SDaniel Lezcano 
32319a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
32329a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
32339a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
32349a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
32359a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3236c19a28e1SFernando Carrijo 		goto out;
323714e50e57SDavid S. Miller 
3238fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
32398ed67789SDaniel Lezcano 	if (ret)
3240bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3241bdb3289fSDaniel Lezcano 
3242c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3243c3426b47SDavid S. Miller 	if (ret)
3244e8803b6cSDavid S. Miller 		goto out_dst_entries;
32452a0c451aSThomas Graf 
32467e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
32477e52b33bSDavid S. Miller 	if (ret)
32487e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3249c3426b47SDavid S. Miller 
32505dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
32515dc121e9SArnaud Ebalard 
32528ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
32538ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
32548ed67789SDaniel Lezcano 	 * manually for init_net */
3255d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
32568ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3257bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3258d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
32598ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3260d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
32618ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3262bdb3289fSDaniel Lezcano   #endif
3263e8803b6cSDavid S. Miller 	ret = fib6_init();
3264433d49c3SDaniel Lezcano 	if (ret)
32658ed67789SDaniel Lezcano 		goto out_register_subsys;
3266433d49c3SDaniel Lezcano 
3267433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3268433d49c3SDaniel Lezcano 	if (ret)
3269e8803b6cSDavid S. Miller 		goto out_fib6_init;
3270c35b7e72SDaniel Lezcano 
3271433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3272433d49c3SDaniel Lezcano 	if (ret)
3273433d49c3SDaniel Lezcano 		goto xfrm6_init;
32747e5449c2SDaniel Lezcano 
3275d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3276d189634eSThomas Graf 	if (ret)
3277d189634eSThomas Graf 		goto fib6_rules_init;
3278d189634eSThomas Graf 
3279433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3280c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3281c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3282c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3283d189634eSThomas Graf 		goto out_register_late_subsys;
3284433d49c3SDaniel Lezcano 
32858ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3286cdb18761SDaniel Lezcano 	if (ret)
3287d189634eSThomas Graf 		goto out_register_late_subsys;
32888ed67789SDaniel Lezcano 
3289433d49c3SDaniel Lezcano out:
3290433d49c3SDaniel Lezcano 	return ret;
3291433d49c3SDaniel Lezcano 
3292d189634eSThomas Graf out_register_late_subsys:
3293d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3294433d49c3SDaniel Lezcano fib6_rules_init:
3295433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3296433d49c3SDaniel Lezcano xfrm6_init:
3297433d49c3SDaniel Lezcano 	xfrm6_fini();
32982a0c451aSThomas Graf out_fib6_init:
32992a0c451aSThomas Graf 	fib6_gc_cleanup();
33008ed67789SDaniel Lezcano out_register_subsys:
33018ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
33027e52b33bSDavid S. Miller out_register_inetpeer:
33037e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3304fc66f95cSEric Dumazet out_dst_entries:
3305fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3306433d49c3SDaniel Lezcano out_kmem_cache:
3307f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3308433d49c3SDaniel Lezcano 	goto out;
33091da177e4SLinus Torvalds }
33101da177e4SLinus Torvalds 
33111da177e4SLinus Torvalds void ip6_route_cleanup(void)
33121da177e4SLinus Torvalds {
33138ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3314d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3315101367c2SThomas Graf 	fib6_rules_cleanup();
33161da177e4SLinus Torvalds 	xfrm6_fini();
33171da177e4SLinus Torvalds 	fib6_gc_cleanup();
3318c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
33198ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
332041bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3321f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
33221da177e4SLinus Torvalds }
3323