xref: /openbmc/linux/net/ipv6/route.c (revision 4b32b5ad31a68a661f761c76dfd0d076636d3ae9)
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);
95*4b32b5adSMartin 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 
108e8243534Sstephen hemminger static void rt6_bind_peer(struct rt6_info *rt, int create)
109e8243534Sstephen hemminger {
110e8243534Sstephen hemminger 	struct inet_peer_base *base;
111e8243534Sstephen hemminger 	struct inet_peer *peer;
112e8243534Sstephen hemminger 
113e8243534Sstephen hemminger 	base = inetpeer_base_ptr(rt->_rt6i_peer);
114e8243534Sstephen hemminger 	if (!base)
115e8243534Sstephen hemminger 		return;
116e8243534Sstephen hemminger 
117e8243534Sstephen hemminger 	peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
118e8243534Sstephen hemminger 	if (peer) {
119e8243534Sstephen hemminger 		if (!rt6_set_peer(rt, peer))
120e8243534Sstephen hemminger 			inet_putpeer(peer);
121e8243534Sstephen hemminger 	}
122e8243534Sstephen hemminger }
123e8243534Sstephen hemminger 
124e8243534Sstephen hemminger static struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create)
125e8243534Sstephen hemminger {
126e8243534Sstephen hemminger 	if (rt6_has_peer(rt))
127e8243534Sstephen hemminger 		return rt6_peer_ptr(rt);
128e8243534Sstephen hemminger 
129e8243534Sstephen hemminger 	rt6_bind_peer(rt, create);
130e8243534Sstephen hemminger 	return (rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL);
131e8243534Sstephen hemminger }
132e8243534Sstephen hemminger 
133e8243534Sstephen hemminger static struct inet_peer *rt6_get_peer_create(struct rt6_info *rt)
134e8243534Sstephen hemminger {
135e8243534Sstephen hemminger 	return __rt6_get_peer(rt, 1);
136e8243534Sstephen hemminger }
137e8243534Sstephen hemminger 
13806582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
13906582540SDavid S. Miller {
14006582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *)dst;
14106582540SDavid S. Miller 
142*4b32b5adSMartin KaFai Lau 	if (rt->rt6i_flags & RTF_CACHE)
143*4b32b5adSMartin KaFai Lau 		return NULL;
144*4b32b5adSMartin KaFai Lau 	else
1453b471175SMartin KaFai Lau 		return dst_cow_metrics_generic(dst, old);
14606582540SDavid S. Miller }
14706582540SDavid S. Miller 
148f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
149f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
150f894cbf8SDavid S. Miller 					     const void *daddr)
15139232973SDavid S. Miller {
15239232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
15339232973SDavid S. Miller 
154a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
15539232973SDavid S. Miller 		return (const void *) p;
156f894cbf8SDavid S. Miller 	else if (skb)
157f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
15839232973SDavid S. Miller 	return daddr;
15939232973SDavid S. Miller }
16039232973SDavid S. Miller 
161f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
162f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
163f894cbf8SDavid S. Miller 					  const void *daddr)
164d3aaeb38SDavid S. Miller {
16539232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
16639232973SDavid S. Miller 	struct neighbour *n;
16739232973SDavid S. Miller 
168f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
1698e022ee6SYOSHIFUJI Hideaki / 吉藤英明 	n = __ipv6_neigh_lookup(dst->dev, daddr);
170f83c7790SDavid S. Miller 	if (n)
171f83c7790SDavid S. Miller 		return n;
172f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
173f83c7790SDavid S. Miller }
174f83c7790SDavid S. Miller 
1759a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
1761da177e4SLinus Torvalds 	.family			=	AF_INET6,
1771da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
1781da177e4SLinus Torvalds 	.gc_thresh		=	1024,
1791da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
1800dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
181ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
18206582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
1831da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
1841da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
1851da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
1861da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
1871da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
1886e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
1891ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
190d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
1911da177e4SLinus Torvalds };
1921da177e4SLinus Torvalds 
193ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
194ec831ea7SRoland Dreier {
195618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
196618f9bc7SSteffen Klassert 
197618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
198ec831ea7SRoland Dreier }
199ec831ea7SRoland Dreier 
2006700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
2016700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
20214e50e57SDavid S. Miller {
20314e50e57SDavid S. Miller }
20414e50e57SDavid S. Miller 
2056700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
2066700c270SDavid S. Miller 				      struct sk_buff *skb)
207b587ee3bSDavid S. Miller {
208b587ee3bSDavid S. Miller }
209b587ee3bSDavid S. Miller 
2100972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
2110972ddb2SHeld Bernhard 					 unsigned long old)
2120972ddb2SHeld Bernhard {
2130972ddb2SHeld Bernhard 	return NULL;
2140972ddb2SHeld Bernhard }
2150972ddb2SHeld Bernhard 
21614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
21714e50e57SDavid S. Miller 	.family			=	AF_INET6,
21814e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
21914e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
220ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
221214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
22214e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
223b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2240972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
225d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
22614e50e57SDavid S. Miller };
22714e50e57SDavid S. Miller 
22862fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
22914edd87dSLi RongQing 	[RTAX_HOPLIMIT - 1] = 0,
23062fa8a84SDavid S. Miller };
23162fa8a84SDavid S. Miller 
232fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = {
2331da177e4SLinus Torvalds 	.dst = {
2341da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2351da177e4SLinus Torvalds 		.__use		= 1,
2362c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
2371da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2381da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2391da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2401da177e4SLinus Torvalds 	},
2411da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2424f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2431da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2441da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2451da177e4SLinus Torvalds };
2461da177e4SLinus Torvalds 
247101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
248101367c2SThomas Graf 
249fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = {
250101367c2SThomas Graf 	.dst = {
251101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
252101367c2SThomas Graf 		.__use		= 1,
2532c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
254101367c2SThomas Graf 		.error		= -EACCES,
2559ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2569ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
257101367c2SThomas Graf 	},
258101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2594f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
260101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
261101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
262101367c2SThomas Graf };
263101367c2SThomas Graf 
264fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = {
265101367c2SThomas Graf 	.dst = {
266101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
267101367c2SThomas Graf 		.__use		= 1,
2682c20cbd7SNicolas Dichtel 		.obsolete	= DST_OBSOLETE_FORCE_CHK,
269101367c2SThomas Graf 		.error		= -EINVAL,
270352e512cSHerbert Xu 		.input		= dst_discard,
271aad88724SEric Dumazet 		.output		= dst_discard_sk,
272101367c2SThomas Graf 	},
273101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2744f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
275101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
276101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
277101367c2SThomas Graf };
278101367c2SThomas Graf 
279101367c2SThomas Graf #endif
280101367c2SThomas Graf 
2811da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
28297bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net,
283957c665fSDavid S. Miller 					     struct net_device *dev,
2848b96d22dSDavid S. Miller 					     int flags,
2858b96d22dSDavid S. Miller 					     struct fib6_table *table)
2861da177e4SLinus Torvalds {
28797bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
2886f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
289cf911662SDavid S. Miller 
29097bab73fSDavid S. Miller 	if (rt) {
2918104891bSSteffen Klassert 		struct dst_entry *dst = &rt->dst;
2928104891bSSteffen Klassert 
2938104891bSSteffen Klassert 		memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
2948b96d22dSDavid S. Miller 		rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
29551ebd318SNicolas Dichtel 		INIT_LIST_HEAD(&rt->rt6i_siblings);
29697bab73fSDavid S. Miller 	}
297cf911662SDavid S. Miller 	return rt;
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3031da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
304ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	struct dst_entry *from = dst->from;
3051da177e4SLinus Torvalds 
3068e2ec639SYan, Zheng 	dst_destroy_metrics_generic(dst);
3078e2ec639SYan, Zheng 
30838308473SDavid S. Miller 	if (idev) {
3091da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3101da177e4SLinus Torvalds 		in6_dev_put(idev);
3111da177e4SLinus Torvalds 	}
3121716a961SGao feng 
313ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst->from = NULL;
314ecd98837SYOSHIFUJI Hideaki / 吉藤英明 	dst_release(from);
315b3419363SDavid S. Miller }
316b3419363SDavid S. Miller 
3171da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3181da177e4SLinus Torvalds 			   int how)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3211da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3225a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
323c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3241da177e4SLinus Torvalds 
32597cac082SDavid S. Miller 	if (dev != loopback_dev) {
32697cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3275a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3285a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
32938308473SDavid S. Miller 			if (loopback_idev) {
3301da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
3311da177e4SLinus Torvalds 				in6_dev_put(idev);
3321da177e4SLinus Torvalds 			}
3331da177e4SLinus Torvalds 		}
33497cac082SDavid S. Miller 	}
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
337a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
3381da177e4SLinus Torvalds {
3391716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
3401716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
341a50feda5SEric Dumazet 			return true;
3421716a961SGao feng 	} else if (rt->dst.from) {
3433fd91fb3SLi RongQing 		return rt6_check_expired((struct rt6_info *) rt->dst.from);
3441716a961SGao feng 	}
345a50feda5SEric Dumazet 	return false;
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds 
34851ebd318SNicolas Dichtel /* Multipath route selection:
34951ebd318SNicolas Dichtel  *   Hash based function using packet header and flowlabel.
35051ebd318SNicolas Dichtel  * Adapted from fib_info_hashfn()
35151ebd318SNicolas Dichtel  */
35251ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count,
35351ebd318SNicolas Dichtel 			       const struct flowi6 *fl6)
35451ebd318SNicolas Dichtel {
35551ebd318SNicolas Dichtel 	unsigned int val = fl6->flowi6_proto;
35651ebd318SNicolas Dichtel 
357c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->daddr);
358c08977bbSYOSHIFUJI Hideaki / 吉藤英明 	val ^= ipv6_addr_hash(&fl6->saddr);
35951ebd318SNicolas Dichtel 
36051ebd318SNicolas Dichtel 	/* Work only if this not encapsulated */
36151ebd318SNicolas Dichtel 	switch (fl6->flowi6_proto) {
36251ebd318SNicolas Dichtel 	case IPPROTO_UDP:
36351ebd318SNicolas Dichtel 	case IPPROTO_TCP:
36451ebd318SNicolas Dichtel 	case IPPROTO_SCTP:
365b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_sport;
366b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_dport;
36751ebd318SNicolas Dichtel 		break;
36851ebd318SNicolas Dichtel 
36951ebd318SNicolas Dichtel 	case IPPROTO_ICMPV6:
370b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_type;
371b3ce5ae1SNicolas Dichtel 		val ^= (__force u16)fl6->fl6_icmp_code;
37251ebd318SNicolas Dichtel 		break;
37351ebd318SNicolas Dichtel 	}
37451ebd318SNicolas Dichtel 	/* RFC6438 recommands to use flowlabel */
375b3ce5ae1SNicolas Dichtel 	val ^= (__force u32)fl6->flowlabel;
37651ebd318SNicolas Dichtel 
37751ebd318SNicolas Dichtel 	/* Perhaps, we need to tune, this function? */
37851ebd318SNicolas Dichtel 	val = val ^ (val >> 7) ^ (val >> 12);
37951ebd318SNicolas Dichtel 	return val % candidate_count;
38051ebd318SNicolas Dichtel }
38151ebd318SNicolas Dichtel 
38251ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
38352bd4c0cSNicolas Dichtel 					     struct flowi6 *fl6, int oif,
38452bd4c0cSNicolas Dichtel 					     int strict)
38551ebd318SNicolas Dichtel {
38651ebd318SNicolas Dichtel 	struct rt6_info *sibling, *next_sibling;
38751ebd318SNicolas Dichtel 	int route_choosen;
38851ebd318SNicolas Dichtel 
38951ebd318SNicolas Dichtel 	route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
39051ebd318SNicolas Dichtel 	/* Don't change the route, if route_choosen == 0
39151ebd318SNicolas Dichtel 	 * (siblings does not include ourself)
39251ebd318SNicolas Dichtel 	 */
39351ebd318SNicolas Dichtel 	if (route_choosen)
39451ebd318SNicolas Dichtel 		list_for_each_entry_safe(sibling, next_sibling,
39551ebd318SNicolas Dichtel 				&match->rt6i_siblings, rt6i_siblings) {
39651ebd318SNicolas Dichtel 			route_choosen--;
39751ebd318SNicolas Dichtel 			if (route_choosen == 0) {
39852bd4c0cSNicolas Dichtel 				if (rt6_score_route(sibling, oif, strict) < 0)
39952bd4c0cSNicolas Dichtel 					break;
40051ebd318SNicolas Dichtel 				match = sibling;
40151ebd318SNicolas Dichtel 				break;
40251ebd318SNicolas Dichtel 			}
40351ebd318SNicolas Dichtel 		}
40451ebd318SNicolas Dichtel 	return match;
40551ebd318SNicolas Dichtel }
40651ebd318SNicolas Dichtel 
4071da177e4SLinus Torvalds /*
408c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
4091da177e4SLinus Torvalds  */
4101da177e4SLinus Torvalds 
4118ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
4128ed67789SDaniel Lezcano 						    struct rt6_info *rt,
413b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
4141da177e4SLinus Torvalds 						    int oif,
415d420895eSYOSHIFUJI Hideaki 						    int flags)
4161da177e4SLinus Torvalds {
4171da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4181da177e4SLinus Torvalds 	struct rt6_info *sprt;
4191da177e4SLinus Torvalds 
420dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
421dd3abc4eSYOSHIFUJI Hideaki 		goto out;
422dd3abc4eSYOSHIFUJI Hideaki 
423d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
424d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
425dd3abc4eSYOSHIFUJI Hideaki 
426dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4271da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4281da177e4SLinus Torvalds 				return sprt;
4291da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
43038308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4311da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
432d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
4331da177e4SLinus Torvalds 						continue;
4341da177e4SLinus Torvalds 					if (local && (!oif ||
4351da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
4361da177e4SLinus Torvalds 						continue;
4371da177e4SLinus Torvalds 				}
4381da177e4SLinus Torvalds 				local = sprt;
4391da177e4SLinus Torvalds 			}
440dd3abc4eSYOSHIFUJI Hideaki 		} else {
441dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
442dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
443dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
444dd3abc4eSYOSHIFUJI Hideaki 		}
4451da177e4SLinus Torvalds 	}
4461da177e4SLinus Torvalds 
447dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
4481da177e4SLinus Torvalds 		if (local)
4491da177e4SLinus Torvalds 			return local;
4501da177e4SLinus Torvalds 
451d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
4528ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
4531da177e4SLinus Torvalds 	}
454dd3abc4eSYOSHIFUJI Hideaki out:
4551da177e4SLinus Torvalds 	return rt;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
45827097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
459c2f17e82SHannes Frederic Sowa struct __rt6_probe_work {
460c2f17e82SHannes Frederic Sowa 	struct work_struct work;
461c2f17e82SHannes Frederic Sowa 	struct in6_addr target;
462c2f17e82SHannes Frederic Sowa 	struct net_device *dev;
463c2f17e82SHannes Frederic Sowa };
464c2f17e82SHannes Frederic Sowa 
465c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w)
466c2f17e82SHannes Frederic Sowa {
467c2f17e82SHannes Frederic Sowa 	struct in6_addr mcaddr;
468c2f17e82SHannes Frederic Sowa 	struct __rt6_probe_work *work =
469c2f17e82SHannes Frederic Sowa 		container_of(w, struct __rt6_probe_work, work);
470c2f17e82SHannes Frederic Sowa 
471c2f17e82SHannes Frederic Sowa 	addrconf_addr_solict_mult(&work->target, &mcaddr);
472c2f17e82SHannes Frederic Sowa 	ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
473c2f17e82SHannes Frederic Sowa 	dev_put(work->dev);
474662f5533SMichael Büsch 	kfree(work);
475c2f17e82SHannes Frederic Sowa }
476c2f17e82SHannes Frederic Sowa 
47727097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
47827097255SYOSHIFUJI Hideaki {
479f2c31e32SEric Dumazet 	struct neighbour *neigh;
48027097255SYOSHIFUJI Hideaki 	/*
48127097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
48227097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
48327097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
48427097255SYOSHIFUJI Hideaki 	 *
48527097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
48627097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
48727097255SYOSHIFUJI Hideaki 	 */
4882152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
489fdd6681dSAmerigo Wang 		return;
4902152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
4912152caeaSYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
4922152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
4932152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_lock(&neigh->lock);
4942152caeaSYOSHIFUJI Hideaki / 吉藤英明 		if (neigh->nud_state & NUD_VALID)
4952152caeaSYOSHIFUJI Hideaki / 吉藤英明 			goto out;
4967ff74a59SYOSHIFUJI Hideaki / 吉藤英明 	}
4972152caeaSYOSHIFUJI Hideaki / 吉藤英明 
4982152caeaSYOSHIFUJI Hideaki / 吉藤英明 	if (!neigh ||
49952e16356SYOSHIFUJI Hideaki 	    time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
500c2f17e82SHannes Frederic Sowa 		struct __rt6_probe_work *work;
50127097255SYOSHIFUJI Hideaki 
502c2f17e82SHannes Frederic Sowa 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
503c2f17e82SHannes Frederic Sowa 
504c2f17e82SHannes Frederic Sowa 		if (neigh && work)
5057e980569SJiri Benc 			__neigh_set_probe_once(neigh);
5062152caeaSYOSHIFUJI Hideaki / 吉藤英明 
507c2f17e82SHannes Frederic Sowa 		if (neigh)
508c2f17e82SHannes Frederic Sowa 			write_unlock(&neigh->lock);
509c2f17e82SHannes Frederic Sowa 
510c2f17e82SHannes Frederic Sowa 		if (work) {
511c2f17e82SHannes Frederic Sowa 			INIT_WORK(&work->work, rt6_probe_deferred);
512c2f17e82SHannes Frederic Sowa 			work->target = rt->rt6i_gateway;
513c2f17e82SHannes Frederic Sowa 			dev_hold(rt->dst.dev);
514c2f17e82SHannes Frederic Sowa 			work->dev = rt->dst.dev;
515c2f17e82SHannes Frederic Sowa 			schedule_work(&work->work);
516c2f17e82SHannes Frederic Sowa 		}
517f2c31e32SEric Dumazet 	} else {
5182152caeaSYOSHIFUJI Hideaki / 吉藤英明 out:
5192152caeaSYOSHIFUJI Hideaki / 吉藤英明 		write_unlock(&neigh->lock);
52027097255SYOSHIFUJI Hideaki 	}
5212152caeaSYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
522f2c31e32SEric Dumazet }
52327097255SYOSHIFUJI Hideaki #else
52427097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
52527097255SYOSHIFUJI Hideaki {
52627097255SYOSHIFUJI Hideaki }
52727097255SYOSHIFUJI Hideaki #endif
52827097255SYOSHIFUJI Hideaki 
5291da177e4SLinus Torvalds /*
530554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
5311da177e4SLinus Torvalds  */
532b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
5331da177e4SLinus Torvalds {
534d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
535161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
536554cfb7eSYOSHIFUJI Hideaki 		return 2;
537161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
538161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
539161980f4SDavid S. Miller 		return 1;
540554cfb7eSYOSHIFUJI Hideaki 	return 0;
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
543afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
5441da177e4SLinus Torvalds {
545f2c31e32SEric Dumazet 	struct neighbour *neigh;
546afc154e9SHannes Frederic Sowa 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
547f2c31e32SEric Dumazet 
5484d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
5494d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
550afc154e9SHannes Frederic Sowa 		return RT6_NUD_SUCCEED;
551145a3621SYOSHIFUJI Hideaki / 吉藤英明 
552145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_lock_bh();
553145a3621SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
554145a3621SYOSHIFUJI Hideaki / 吉藤英明 	if (neigh) {
555145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_lock(&neigh->lock);
556554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
557afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
558398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
559a5a81f0bSPaul Marks 		else if (!(neigh->nud_state & NUD_FAILED))
560afc154e9SHannes Frederic Sowa 			ret = RT6_NUD_SUCCEED;
5617e980569SJiri Benc 		else
5627e980569SJiri Benc 			ret = RT6_NUD_FAIL_PROBE;
563398bcbebSYOSHIFUJI Hideaki #endif
564145a3621SYOSHIFUJI Hideaki / 吉藤英明 		read_unlock(&neigh->lock);
565afc154e9SHannes Frederic Sowa 	} else {
566afc154e9SHannes Frederic Sowa 		ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
5677e980569SJiri Benc 		      RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
568a5a81f0bSPaul Marks 	}
569145a3621SYOSHIFUJI Hideaki / 吉藤英明 	rcu_read_unlock_bh();
570145a3621SYOSHIFUJI Hideaki / 吉藤英明 
571a5a81f0bSPaul Marks 	return ret;
5721da177e4SLinus Torvalds }
5731da177e4SLinus Torvalds 
574554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
575554cfb7eSYOSHIFUJI Hideaki 			   int strict)
576554cfb7eSYOSHIFUJI Hideaki {
577a5a81f0bSPaul Marks 	int m;
5784d0c5911SYOSHIFUJI Hideaki 
5794d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
58077d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
581afc154e9SHannes Frederic Sowa 		return RT6_NUD_FAIL_HARD;
582ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
583ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
584ebacaaa0SYOSHIFUJI Hideaki #endif
585afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE) {
586afc154e9SHannes Frederic Sowa 		int n = rt6_check_neigh(rt);
587afc154e9SHannes Frederic Sowa 		if (n < 0)
588afc154e9SHannes Frederic Sowa 			return n;
589afc154e9SHannes Frederic Sowa 	}
590554cfb7eSYOSHIFUJI Hideaki 	return m;
591554cfb7eSYOSHIFUJI Hideaki }
592554cfb7eSYOSHIFUJI Hideaki 
593f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
594afc154e9SHannes Frederic Sowa 				   int *mpri, struct rt6_info *match,
595afc154e9SHannes Frederic Sowa 				   bool *do_rr)
596554cfb7eSYOSHIFUJI Hideaki {
597554cfb7eSYOSHIFUJI Hideaki 	int m;
598afc154e9SHannes Frederic Sowa 	bool match_do_rr = false;
599554cfb7eSYOSHIFUJI Hideaki 
600554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
601f11e6659SDavid S. Miller 		goto out;
602554cfb7eSYOSHIFUJI Hideaki 
603554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
6047e980569SJiri Benc 	if (m == RT6_NUD_FAIL_DO_RR) {
605afc154e9SHannes Frederic Sowa 		match_do_rr = true;
606afc154e9SHannes Frederic Sowa 		m = 0; /* lowest valid score */
6077e980569SJiri Benc 	} else if (m == RT6_NUD_FAIL_HARD) {
608f11e6659SDavid S. Miller 		goto out;
6091da177e4SLinus Torvalds 	}
610f11e6659SDavid S. Miller 
611afc154e9SHannes Frederic Sowa 	if (strict & RT6_LOOKUP_F_REACHABLE)
612afc154e9SHannes Frederic Sowa 		rt6_probe(rt);
613afc154e9SHannes Frederic Sowa 
6147e980569SJiri Benc 	/* note that m can be RT6_NUD_FAIL_PROBE at this point */
615afc154e9SHannes Frederic Sowa 	if (m > *mpri) {
616afc154e9SHannes Frederic Sowa 		*do_rr = match_do_rr;
617afc154e9SHannes Frederic Sowa 		*mpri = m;
618afc154e9SHannes Frederic Sowa 		match = rt;
619afc154e9SHannes Frederic Sowa 	}
620f11e6659SDavid S. Miller out:
621f11e6659SDavid S. Miller 	return match;
6221da177e4SLinus Torvalds }
6231da177e4SLinus Torvalds 
624f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
625f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
626afc154e9SHannes Frederic Sowa 				     u32 metric, int oif, int strict,
627afc154e9SHannes Frederic Sowa 				     bool *do_rr)
628f11e6659SDavid S. Miller {
6299fbdcfafSSteffen Klassert 	struct rt6_info *rt, *match, *cont;
630f11e6659SDavid S. Miller 	int mpri = -1;
631f11e6659SDavid S. Miller 
632f11e6659SDavid S. Miller 	match = NULL;
6339fbdcfafSSteffen Klassert 	cont = NULL;
6349fbdcfafSSteffen Klassert 	for (rt = rr_head; rt; rt = rt->dst.rt6_next) {
6359fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6369fbdcfafSSteffen Klassert 			cont = rt;
6379fbdcfafSSteffen Klassert 			break;
6389fbdcfafSSteffen Klassert 		}
6399fbdcfafSSteffen Klassert 
640afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6419fbdcfafSSteffen Klassert 	}
6429fbdcfafSSteffen Klassert 
6439fbdcfafSSteffen Klassert 	for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
6449fbdcfafSSteffen Klassert 		if (rt->rt6i_metric != metric) {
6459fbdcfafSSteffen Klassert 			cont = rt;
6469fbdcfafSSteffen Klassert 			break;
6479fbdcfafSSteffen Klassert 		}
6489fbdcfafSSteffen Klassert 
6499fbdcfafSSteffen Klassert 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
6509fbdcfafSSteffen Klassert 	}
6519fbdcfafSSteffen Klassert 
6529fbdcfafSSteffen Klassert 	if (match || !cont)
6539fbdcfafSSteffen Klassert 		return match;
6549fbdcfafSSteffen Klassert 
6559fbdcfafSSteffen Klassert 	for (rt = cont; rt; rt = rt->dst.rt6_next)
656afc154e9SHannes Frederic Sowa 		match = find_match(rt, oif, strict, &mpri, match, do_rr);
657f11e6659SDavid S. Miller 
658f11e6659SDavid S. Miller 	return match;
659f11e6659SDavid S. Miller }
660f11e6659SDavid S. Miller 
661f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
662f11e6659SDavid S. Miller {
663f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
6648ed67789SDaniel Lezcano 	struct net *net;
665afc154e9SHannes Frederic Sowa 	bool do_rr = false;
666f11e6659SDavid S. Miller 
667f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
668f11e6659SDavid S. Miller 	if (!rt0)
669f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
670f11e6659SDavid S. Miller 
671afc154e9SHannes Frederic Sowa 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
672afc154e9SHannes Frederic Sowa 			     &do_rr);
673f11e6659SDavid S. Miller 
674afc154e9SHannes Frederic Sowa 	if (do_rr) {
675d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
676f11e6659SDavid S. Miller 
677554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
678f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
679f11e6659SDavid S. Miller 			next = fn->leaf;
680f11e6659SDavid S. Miller 
681f11e6659SDavid S. Miller 		if (next != rt0)
682f11e6659SDavid S. Miller 			fn->rr_ptr = next;
683554cfb7eSYOSHIFUJI Hideaki 	}
684554cfb7eSYOSHIFUJI Hideaki 
685d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
686a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds 
68970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
69070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
691b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
69270ceb4f5SYOSHIFUJI Hideaki {
693c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
69470ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
69570ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
69670ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
6974bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
69870ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
69970ceb4f5SYOSHIFUJI Hideaki 
70070ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
70170ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
70270ceb4f5SYOSHIFUJI Hideaki 	}
70370ceb4f5SYOSHIFUJI Hideaki 
70470ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
70570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
70670ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
70770ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
70870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
70970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
71070ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
71170ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
71270ceb4f5SYOSHIFUJI Hideaki 		}
71370ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
71470ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
71570ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
71670ceb4f5SYOSHIFUJI Hideaki 		}
71770ceb4f5SYOSHIFUJI Hideaki 	}
71870ceb4f5SYOSHIFUJI Hideaki 
71970ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
72070ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
7213933fc95SJens Rosenboom 		return -EINVAL;
72270ceb4f5SYOSHIFUJI Hideaki 
7234bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
72470ceb4f5SYOSHIFUJI Hideaki 
72570ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
72670ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
72770ceb4f5SYOSHIFUJI Hideaki 	else {
72870ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
72970ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
73070ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
73170ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
73270ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
73370ceb4f5SYOSHIFUJI Hideaki 	}
73470ceb4f5SYOSHIFUJI Hideaki 
735f104a567SDuan Jiong 	if (rinfo->prefix_len == 0)
736f104a567SDuan Jiong 		rt = rt6_get_dflt_router(gwaddr, dev);
737f104a567SDuan Jiong 	else
738f104a567SDuan Jiong 		rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
739f104a567SDuan Jiong 					gwaddr, dev->ifindex);
74070ceb4f5SYOSHIFUJI Hideaki 
74170ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
742e0a1ad73SThomas Graf 		ip6_del_rt(rt);
74370ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
74470ceb4f5SYOSHIFUJI Hideaki 	}
74570ceb4f5SYOSHIFUJI Hideaki 
74670ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
747efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
74870ceb4f5SYOSHIFUJI Hideaki 					pref);
74970ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
75070ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
75170ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
75270ceb4f5SYOSHIFUJI Hideaki 
75370ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
7541716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
7551716a961SGao feng 			rt6_clean_expires(rt);
7561716a961SGao feng 		else
7571716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
7581716a961SGao feng 
75994e187c0SAmerigo Wang 		ip6_rt_put(rt);
76070ceb4f5SYOSHIFUJI Hideaki 	}
76170ceb4f5SYOSHIFUJI Hideaki 	return 0;
76270ceb4f5SYOSHIFUJI Hideaki }
76370ceb4f5SYOSHIFUJI Hideaki #endif
76470ceb4f5SYOSHIFUJI Hideaki 
765a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
766a3c00e46SMartin KaFai Lau 					struct in6_addr *saddr)
767a3c00e46SMartin KaFai Lau {
768a3c00e46SMartin KaFai Lau 	struct fib6_node *pn;
769a3c00e46SMartin KaFai Lau 	while (1) {
770a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_TL_ROOT)
771a3c00e46SMartin KaFai Lau 			return NULL;
772a3c00e46SMartin KaFai Lau 		pn = fn->parent;
773a3c00e46SMartin KaFai Lau 		if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
774a3c00e46SMartin KaFai Lau 			fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
775a3c00e46SMartin KaFai Lau 		else
776a3c00e46SMartin KaFai Lau 			fn = pn;
777a3c00e46SMartin KaFai Lau 		if (fn->fn_flags & RTN_RTINFO)
778a3c00e46SMartin KaFai Lau 			return fn;
779a3c00e46SMartin KaFai Lau 	}
780a3c00e46SMartin KaFai Lau }
781c71099acSThomas Graf 
7828ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
7838ed67789SDaniel Lezcano 					     struct fib6_table *table,
7844c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
7851da177e4SLinus Torvalds {
7861da177e4SLinus Torvalds 	struct fib6_node *fn;
7871da177e4SLinus Torvalds 	struct rt6_info *rt;
7881da177e4SLinus Torvalds 
789c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
7904c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
791c71099acSThomas Graf restart:
792c71099acSThomas Graf 	rt = fn->leaf;
7934c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
79451ebd318SNicolas Dichtel 	if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
79552bd4c0cSNicolas Dichtel 		rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
796a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
797a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
798a3c00e46SMartin KaFai Lau 		if (fn)
799a3c00e46SMartin KaFai Lau 			goto restart;
800a3c00e46SMartin KaFai Lau 	}
801d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
802c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8031da177e4SLinus Torvalds 	return rt;
804c71099acSThomas Graf 
805c71099acSThomas Graf }
806c71099acSThomas Graf 
807ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
808ea6e574eSFlorian Westphal 				    int flags)
809ea6e574eSFlorian Westphal {
810ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
811ea6e574eSFlorian Westphal }
812ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
813ea6e574eSFlorian Westphal 
8149acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
8159acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
816c71099acSThomas Graf {
8174c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8184c9483b2SDavid S. Miller 		.flowi6_oif = oif,
8194c9483b2SDavid S. Miller 		.daddr = *daddr,
820c71099acSThomas Graf 	};
821c71099acSThomas Graf 	struct dst_entry *dst;
82277d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
823c71099acSThomas Graf 
824adaa70bbSThomas Graf 	if (saddr) {
8254c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
826adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
827adaa70bbSThomas Graf 	}
828adaa70bbSThomas Graf 
8294c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
830c71099acSThomas Graf 	if (dst->error == 0)
831c71099acSThomas Graf 		return (struct rt6_info *) dst;
832c71099acSThomas Graf 
833c71099acSThomas Graf 	dst_release(dst);
834c71099acSThomas Graf 
8351da177e4SLinus Torvalds 	return NULL;
8361da177e4SLinus Torvalds }
8377159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
8387159039aSYOSHIFUJI Hideaki 
839c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
8401da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
8411da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
8421da177e4SLinus Torvalds    be destroyed.
8431da177e4SLinus Torvalds  */
8441da177e4SLinus Torvalds 
845e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
846e715b6d3SFlorian Westphal 			struct mx6_config *mxc)
8471da177e4SLinus Torvalds {
8481da177e4SLinus Torvalds 	int err;
849c71099acSThomas Graf 	struct fib6_table *table;
8501da177e4SLinus Torvalds 
851c71099acSThomas Graf 	table = rt->rt6i_table;
852c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
853e715b6d3SFlorian Westphal 	err = fib6_add(&table->tb6_root, rt, info, mxc);
854c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
8551da177e4SLinus Torvalds 
8561da177e4SLinus Torvalds 	return err;
8571da177e4SLinus Torvalds }
8581da177e4SLinus Torvalds 
85940e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
86040e22e8fSThomas Graf {
861e715b6d3SFlorian Westphal 	struct nl_info info = {	.nl_net = dev_net(rt->dst.dev), };
862e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
863e715b6d3SFlorian Westphal 
864e715b6d3SFlorian Westphal 	return __ip6_ins_rt(rt, &info, &mxc);
86540e22e8fSThomas Graf }
86640e22e8fSThomas Graf 
8671716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
86821efcfa0SEric Dumazet 				      const struct in6_addr *daddr,
869b71d1d42SEric Dumazet 				      const struct in6_addr *saddr)
8701da177e4SLinus Torvalds {
8711da177e4SLinus Torvalds 	struct rt6_info *rt;
8721da177e4SLinus Torvalds 
8731da177e4SLinus Torvalds 	/*
8741da177e4SLinus Torvalds 	 *	Clone the route.
8751da177e4SLinus Torvalds 	 */
8761da177e4SLinus Torvalds 
87721efcfa0SEric Dumazet 	rt = ip6_rt_copy(ort, daddr);
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 	if (rt) {
880bb3c3686SDavid S. Miller 		if (ort->rt6i_dst.plen != 128 &&
88121efcfa0SEric Dumazet 		    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
88258c4fb86SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_ANYCAST;
8831da177e4SLinus Torvalds 
8841da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_CACHE;
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
8871da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
8884e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
8891da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
8901da177e4SLinus Torvalds 		}
8911da177e4SLinus Torvalds #endif
89295a9a5baSYOSHIFUJI Hideaki 	}
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 	return rt;
8951da177e4SLinus Torvalds }
89695a9a5baSYOSHIFUJI Hideaki 
89721efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
89821efcfa0SEric Dumazet 					const struct in6_addr *daddr)
899299d9939SYOSHIFUJI Hideaki {
90021efcfa0SEric Dumazet 	struct rt6_info *rt = ip6_rt_copy(ort, daddr);
90121efcfa0SEric Dumazet 
902887c95ccSYOSHIFUJI Hideaki / 吉藤英明 	if (rt)
903299d9939SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_CACHE;
904299d9939SYOSHIFUJI Hideaki 	return rt;
905299d9939SYOSHIFUJI Hideaki }
906299d9939SYOSHIFUJI Hideaki 
9078ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
9084c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
9091da177e4SLinus Torvalds {
910367efcb9SMartin KaFai Lau 	struct fib6_node *fn, *saved_fn;
911519fbd87SYOSHIFUJI Hideaki 	struct rt6_info *rt, *nrt;
912c71099acSThomas Graf 	int strict = 0;
9131da177e4SLinus Torvalds 	int attempts = 3;
914519fbd87SYOSHIFUJI Hideaki 	int err;
9151da177e4SLinus Torvalds 
91677d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
917367efcb9SMartin KaFai Lau 	if (net->ipv6.devconf_all->forwarding == 0)
918367efcb9SMartin KaFai Lau 		strict |= RT6_LOOKUP_F_REACHABLE;
9191da177e4SLinus Torvalds 
920a3c00e46SMartin KaFai Lau redo_fib6_lookup_lock:
921c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
9221da177e4SLinus Torvalds 
9234c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
924367efcb9SMartin KaFai Lau 	saved_fn = fn;
9251da177e4SLinus Torvalds 
926a3c00e46SMartin KaFai Lau redo_rt6_select:
927367efcb9SMartin KaFai Lau 	rt = rt6_select(fn, oif, strict);
92852bd4c0cSNicolas Dichtel 	if (rt->rt6i_nsiblings)
929367efcb9SMartin KaFai Lau 		rt = rt6_multipath_select(rt, fl6, oif, strict);
930a3c00e46SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
931a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
932a3c00e46SMartin KaFai Lau 		if (fn)
933a3c00e46SMartin KaFai Lau 			goto redo_rt6_select;
934367efcb9SMartin KaFai Lau 		else if (strict & RT6_LOOKUP_F_REACHABLE) {
935367efcb9SMartin KaFai Lau 			/* also consider unreachable route */
936367efcb9SMartin KaFai Lau 			strict &= ~RT6_LOOKUP_F_REACHABLE;
937367efcb9SMartin KaFai Lau 			fn = saved_fn;
938367efcb9SMartin KaFai Lau 			goto redo_rt6_select;
939367efcb9SMartin KaFai Lau 		} else {
940367efcb9SMartin KaFai Lau 			dst_hold(&rt->dst);
941367efcb9SMartin KaFai Lau 			read_unlock_bh(&table->tb6_lock);
942367efcb9SMartin KaFai Lau 			goto out2;
943367efcb9SMartin KaFai Lau 		}
944a3c00e46SMartin KaFai Lau 	}
945a3c00e46SMartin KaFai Lau 
946d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
947c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
9481da177e4SLinus Torvalds 
94994c77bb4SMartin KaFai Lau 	if (rt->rt6i_flags & RTF_CACHE)
95094c77bb4SMartin KaFai Lau 		goto out2;
95194c77bb4SMartin KaFai Lau 
952c440f160SYOSHIFUJI Hideaki / 吉藤英明 	if (!(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)))
9534c9483b2SDavid S. Miller 		nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
954653437d0SMartin KaFai Lau 	else if (!(rt->dst.flags & DST_HOST) || !(rt->dst.flags & RTF_LOCAL))
9554c9483b2SDavid S. Miller 		nrt = rt6_alloc_clone(rt, &fl6->daddr);
9567343ff31SDavid S. Miller 	else
9577343ff31SDavid S. Miller 		goto out2;
9581da177e4SLinus Torvalds 
95994e187c0SAmerigo Wang 	ip6_rt_put(rt);
9608ed67789SDaniel Lezcano 	rt = nrt ? : net->ipv6.ip6_null_entry;
9611da177e4SLinus Torvalds 
962d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
963e40cf353SYOSHIFUJI Hideaki 	if (nrt) {
96440e22e8fSThomas Graf 		err = ip6_ins_rt(nrt);
965e40cf353SYOSHIFUJI Hideaki 		if (!err)
966e40cf353SYOSHIFUJI Hideaki 			goto out2;
967e40cf353SYOSHIFUJI Hideaki 	}
968e40cf353SYOSHIFUJI Hideaki 
969e40cf353SYOSHIFUJI Hideaki 	if (--attempts <= 0)
9701da177e4SLinus Torvalds 		goto out2;
9711da177e4SLinus Torvalds 
972519fbd87SYOSHIFUJI Hideaki 	/*
973c71099acSThomas Graf 	 * Race condition! In the gap, when table->tb6_lock was
974519fbd87SYOSHIFUJI Hideaki 	 * released someone could insert this route.  Relookup.
9751da177e4SLinus Torvalds 	 */
97694e187c0SAmerigo Wang 	ip6_rt_put(rt);
977a3c00e46SMartin KaFai Lau 	goto redo_fib6_lookup_lock;
978e40cf353SYOSHIFUJI Hideaki 
9791da177e4SLinus Torvalds out2:
980*4b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
981d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
982d8d1f30bSChangli Gao 	rt->dst.__use++;
983c71099acSThomas Graf 
984c71099acSThomas Graf 	return rt;
985c71099acSThomas Graf }
986c71099acSThomas Graf 
9878ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
9884c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
9894acad72dSPavel Emelyanov {
9904c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
9914acad72dSPavel Emelyanov }
9924acad72dSPavel Emelyanov 
99372331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
99472331bc0SShmulik Ladkani 						struct net_device *dev,
99572331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
99672331bc0SShmulik Ladkani {
99772331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
99872331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
99972331bc0SShmulik Ladkani 
100072331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
100172331bc0SShmulik Ladkani }
100272331bc0SShmulik Ladkani 
1003c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
1004c71099acSThomas Graf {
1005b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1006c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
1007adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
10084c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
10094c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
10104c9483b2SDavid S. Miller 		.daddr = iph->daddr,
10114c9483b2SDavid S. Miller 		.saddr = iph->saddr,
10126502ca52SYOSHIFUJI Hideaki / 吉藤英明 		.flowlabel = ip6_flowinfo(iph),
10134c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
10144c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
1015c71099acSThomas Graf 	};
1016adaa70bbSThomas Graf 
101772331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
1018c71099acSThomas Graf }
1019c71099acSThomas Graf 
10208ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
10214c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
1022c71099acSThomas Graf {
10234c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
1024c71099acSThomas Graf }
1025c71099acSThomas Graf 
10269c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
10274c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
1028c71099acSThomas Graf {
1029c71099acSThomas Graf 	int flags = 0;
1030c71099acSThomas Graf 
10311fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
10324dc27d1cSDavid McCullough 
10334c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
103477d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
1035c71099acSThomas Graf 
10364c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
1037adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
10380c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
10390c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
1040adaa70bbSThomas Graf 
10414c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
10421da177e4SLinus Torvalds }
10437159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
10441da177e4SLinus Torvalds 
10452774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
104614e50e57SDavid S. Miller {
10475c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
104814e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
104914e50e57SDavid S. Miller 
1050f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
105114e50e57SDavid S. Miller 	if (rt) {
1052d8d1f30bSChangli Gao 		new = &rt->dst;
105314e50e57SDavid S. Miller 
10548104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
10558104891bSSteffen Klassert 		rt6_init_peer(rt, net->ipv6.peers);
10568104891bSSteffen Klassert 
105714e50e57SDavid S. Miller 		new->__use = 1;
1058352e512cSHerbert Xu 		new->input = dst_discard;
1059aad88724SEric Dumazet 		new->output = dst_discard_sk;
106014e50e57SDavid S. Miller 
106121efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
106221efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
106321efcfa0SEric Dumazet 		else
1064defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
106514e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
106614e50e57SDavid S. Miller 		if (rt->rt6i_idev)
106714e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
106814e50e57SDavid S. Miller 
10694e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
10701716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
107114e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
107214e50e57SDavid S. Miller 
107314e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
107414e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
107514e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
107614e50e57SDavid S. Miller #endif
107714e50e57SDavid S. Miller 
107814e50e57SDavid S. Miller 		dst_free(new);
107914e50e57SDavid S. Miller 	}
108014e50e57SDavid S. Miller 
108169ead7afSDavid S. Miller 	dst_release(dst_orig);
108269ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
108314e50e57SDavid S. Miller }
108414e50e57SDavid S. Miller 
10851da177e4SLinus Torvalds /*
10861da177e4SLinus Torvalds  *	Destination cache support functions
10871da177e4SLinus Torvalds  */
10881da177e4SLinus Torvalds 
1089*4b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt)
1090*4b32b5adSMartin KaFai Lau {
1091*4b32b5adSMartin KaFai Lau 	if (rt->dst.from &&
1092*4b32b5adSMartin KaFai Lau 	    dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from))
1093*4b32b5adSMartin KaFai Lau 		dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true);
1094*4b32b5adSMartin KaFai Lau }
1095*4b32b5adSMartin KaFai Lau 
10961da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
10971da177e4SLinus Torvalds {
10981da177e4SLinus Torvalds 	struct rt6_info *rt;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
11011da177e4SLinus Torvalds 
11026f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
11036f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
11046f3118b5SNicolas Dichtel 	 * into this function always.
11056f3118b5SNicolas Dichtel 	 */
1106e3bc10bdSHannes Frederic Sowa 	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
11071da177e4SLinus Torvalds 		return NULL;
1108e3bc10bdSHannes Frederic Sowa 
1109e3bc10bdSHannes Frederic Sowa 	if (rt6_check_expired(rt))
1110e3bc10bdSHannes Frederic Sowa 		return NULL;
1111e3bc10bdSHannes Frederic Sowa 
1112*4b32b5adSMartin KaFai Lau 	rt6_dst_from_metrics_check(rt);
1113*4b32b5adSMartin KaFai Lau 
1114e3bc10bdSHannes Frederic Sowa 	return dst;
11151da177e4SLinus Torvalds }
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
11181da177e4SLinus Torvalds {
11191da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 	if (rt) {
112254c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
112354c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1124e0a1ad73SThomas Graf 				ip6_del_rt(rt);
112554c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
11261da177e4SLinus Torvalds 			}
112754c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
112854c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
112954c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
113054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
113154c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
113254c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
11331da177e4SLinus Torvalds }
11341da177e4SLinus Torvalds 
11351da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
11361da177e4SLinus Torvalds {
11371da177e4SLinus Torvalds 	struct rt6_info *rt;
11381da177e4SLinus Torvalds 
11393ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
11401da177e4SLinus Torvalds 
1141adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
11421da177e4SLinus Torvalds 	if (rt) {
11431eb4f758SHannes Frederic Sowa 		if (rt->rt6i_flags & RTF_CACHE) {
11441eb4f758SHannes Frederic Sowa 			dst_hold(&rt->dst);
11451eb4f758SHannes Frederic Sowa 			if (ip6_del_rt(rt))
11461eb4f758SHannes Frederic Sowa 				dst_free(&rt->dst);
11471eb4f758SHannes Frederic Sowa 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
11481da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
11491da177e4SLinus Torvalds 		}
11501da177e4SLinus Torvalds 	}
11511eb4f758SHannes Frederic Sowa }
11521da177e4SLinus Torvalds 
11536700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
11546700c270SDavid S. Miller 			       struct sk_buff *skb, u32 mtu)
11551da177e4SLinus Torvalds {
11561da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info *)dst;
11571da177e4SLinus Torvalds 
115881aded24SDavid S. Miller 	dst_confirm(dst);
1159653437d0SMartin KaFai Lau 	if (mtu < dst_mtu(dst) && (rt6->rt6i_flags & RTF_CACHE)) {
116081aded24SDavid S. Miller 		struct net *net = dev_net(dst->dev);
116181aded24SDavid S. Miller 
11621da177e4SLinus Torvalds 		rt6->rt6i_flags |= RTF_MODIFIED;
11639d289715SHagen Paul Pfeifer 		if (mtu < IPV6_MIN_MTU)
11641da177e4SLinus Torvalds 			mtu = IPV6_MIN_MTU;
11659d289715SHagen Paul Pfeifer 
1166*4b32b5adSMartin KaFai Lau 		rt6->rt6i_pmtu = mtu;
116781aded24SDavid S. Miller 		rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
11681da177e4SLinus Torvalds 	}
11691da177e4SLinus Torvalds }
11701da177e4SLinus Torvalds 
117142ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
117242ae66c8SDavid S. Miller 		     int oif, u32 mark)
117381aded24SDavid S. Miller {
117481aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
117581aded24SDavid S. Miller 	struct dst_entry *dst;
117681aded24SDavid S. Miller 	struct flowi6 fl6;
117781aded24SDavid S. Miller 
117881aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
117981aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
11801b3c61dcSLorenzo Colitti 	fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
118181aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
118281aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
11836502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
118481aded24SDavid S. Miller 
118581aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
118681aded24SDavid S. Miller 	if (!dst->error)
11876700c270SDavid S. Miller 		ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
118881aded24SDavid S. Miller 	dst_release(dst);
118981aded24SDavid S. Miller }
119081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
119181aded24SDavid S. Miller 
119281aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
119381aded24SDavid S. Miller {
119481aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
119581aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
119681aded24SDavid S. Miller }
119781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
119881aded24SDavid S. Miller 
1199b55b76b2SDuan Jiong /* Handle redirects */
1200b55b76b2SDuan Jiong struct ip6rd_flowi {
1201b55b76b2SDuan Jiong 	struct flowi6 fl6;
1202b55b76b2SDuan Jiong 	struct in6_addr gateway;
1203b55b76b2SDuan Jiong };
1204b55b76b2SDuan Jiong 
1205b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net,
1206b55b76b2SDuan Jiong 					     struct fib6_table *table,
1207b55b76b2SDuan Jiong 					     struct flowi6 *fl6,
1208b55b76b2SDuan Jiong 					     int flags)
1209b55b76b2SDuan Jiong {
1210b55b76b2SDuan Jiong 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1211b55b76b2SDuan Jiong 	struct rt6_info *rt;
1212b55b76b2SDuan Jiong 	struct fib6_node *fn;
1213b55b76b2SDuan Jiong 
1214b55b76b2SDuan Jiong 	/* Get the "current" route for this destination and
1215b55b76b2SDuan Jiong 	 * check if the redirect has come from approriate router.
1216b55b76b2SDuan Jiong 	 *
1217b55b76b2SDuan Jiong 	 * RFC 4861 specifies that redirects should only be
1218b55b76b2SDuan Jiong 	 * accepted if they come from the nexthop to the target.
1219b55b76b2SDuan Jiong 	 * Due to the way the routes are chosen, this notion
1220b55b76b2SDuan Jiong 	 * is a bit fuzzy and one might need to check all possible
1221b55b76b2SDuan Jiong 	 * routes.
1222b55b76b2SDuan Jiong 	 */
1223b55b76b2SDuan Jiong 
1224b55b76b2SDuan Jiong 	read_lock_bh(&table->tb6_lock);
1225b55b76b2SDuan Jiong 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1226b55b76b2SDuan Jiong restart:
1227b55b76b2SDuan Jiong 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1228b55b76b2SDuan Jiong 		if (rt6_check_expired(rt))
1229b55b76b2SDuan Jiong 			continue;
1230b55b76b2SDuan Jiong 		if (rt->dst.error)
1231b55b76b2SDuan Jiong 			break;
1232b55b76b2SDuan Jiong 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1233b55b76b2SDuan Jiong 			continue;
1234b55b76b2SDuan Jiong 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1235b55b76b2SDuan Jiong 			continue;
1236b55b76b2SDuan Jiong 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1237b55b76b2SDuan Jiong 			continue;
1238b55b76b2SDuan Jiong 		break;
1239b55b76b2SDuan Jiong 	}
1240b55b76b2SDuan Jiong 
1241b55b76b2SDuan Jiong 	if (!rt)
1242b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1243b55b76b2SDuan Jiong 	else if (rt->dst.error) {
1244b55b76b2SDuan Jiong 		rt = net->ipv6.ip6_null_entry;
1245b0a1ba59SMartin KaFai Lau 		goto out;
1246b0a1ba59SMartin KaFai Lau 	}
1247b0a1ba59SMartin KaFai Lau 
1248b0a1ba59SMartin KaFai Lau 	if (rt == net->ipv6.ip6_null_entry) {
1249a3c00e46SMartin KaFai Lau 		fn = fib6_backtrack(fn, &fl6->saddr);
1250a3c00e46SMartin KaFai Lau 		if (fn)
1251a3c00e46SMartin KaFai Lau 			goto restart;
1252b55b76b2SDuan Jiong 	}
1253a3c00e46SMartin KaFai Lau 
1254b0a1ba59SMartin KaFai Lau out:
1255b55b76b2SDuan Jiong 	dst_hold(&rt->dst);
1256b55b76b2SDuan Jiong 
1257b55b76b2SDuan Jiong 	read_unlock_bh(&table->tb6_lock);
1258b55b76b2SDuan Jiong 
1259b55b76b2SDuan Jiong 	return rt;
1260b55b76b2SDuan Jiong };
1261b55b76b2SDuan Jiong 
1262b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net,
1263b55b76b2SDuan Jiong 					const struct flowi6 *fl6,
1264b55b76b2SDuan Jiong 					const struct in6_addr *gateway)
1265b55b76b2SDuan Jiong {
1266b55b76b2SDuan Jiong 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1267b55b76b2SDuan Jiong 	struct ip6rd_flowi rdfl;
1268b55b76b2SDuan Jiong 
1269b55b76b2SDuan Jiong 	rdfl.fl6 = *fl6;
1270b55b76b2SDuan Jiong 	rdfl.gateway = *gateway;
1271b55b76b2SDuan Jiong 
1272b55b76b2SDuan Jiong 	return fib6_rule_lookup(net, &rdfl.fl6,
1273b55b76b2SDuan Jiong 				flags, __ip6_route_redirect);
1274b55b76b2SDuan Jiong }
1275b55b76b2SDuan Jiong 
12763a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
12773a5ad2eeSDavid S. Miller {
12783a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
12793a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
12803a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
12813a5ad2eeSDavid S. Miller 
12823a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
1283e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
12843a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
12853a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
12863a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
12873a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
12886502ca52SYOSHIFUJI Hideaki / 吉藤英明 	fl6.flowlabel = ip6_flowinfo(iph);
12893a5ad2eeSDavid S. Miller 
1290b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
12916700c270SDavid S. Miller 	rt6_do_redirect(dst, NULL, skb);
12923a5ad2eeSDavid S. Miller 	dst_release(dst);
12933a5ad2eeSDavid S. Miller }
12943a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
12953a5ad2eeSDavid S. Miller 
1296c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
1297c92a59ecSDuan Jiong 			    u32 mark)
1298c92a59ecSDuan Jiong {
1299c92a59ecSDuan Jiong 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1300c92a59ecSDuan Jiong 	const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
1301c92a59ecSDuan Jiong 	struct dst_entry *dst;
1302c92a59ecSDuan Jiong 	struct flowi6 fl6;
1303c92a59ecSDuan Jiong 
1304c92a59ecSDuan Jiong 	memset(&fl6, 0, sizeof(fl6));
1305e374c618SJulian Anastasov 	fl6.flowi6_iif = LOOPBACK_IFINDEX;
1306c92a59ecSDuan Jiong 	fl6.flowi6_oif = oif;
1307c92a59ecSDuan Jiong 	fl6.flowi6_mark = mark;
1308c92a59ecSDuan Jiong 	fl6.daddr = msg->dest;
1309c92a59ecSDuan Jiong 	fl6.saddr = iph->daddr;
1310c92a59ecSDuan Jiong 
1311b55b76b2SDuan Jiong 	dst = ip6_route_redirect(net, &fl6, &iph->saddr);
1312c92a59ecSDuan Jiong 	rt6_do_redirect(dst, NULL, skb);
1313c92a59ecSDuan Jiong 	dst_release(dst);
1314c92a59ecSDuan Jiong }
1315c92a59ecSDuan Jiong 
13163a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
13173a5ad2eeSDavid S. Miller {
13183a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
13193a5ad2eeSDavid S. Miller }
13203a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
13213a5ad2eeSDavid S. Miller 
13220dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
13231da177e4SLinus Torvalds {
13240dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
13250dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
13260dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
13270dbaee3bSDavid S. Miller 
13281da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
13291da177e4SLinus Torvalds 
13305578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
13315578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 	/*
13341da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
13351da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
13361da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
13371da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
13381da177e4SLinus Torvalds 	 */
13391da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
13401da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
13411da177e4SLinus Torvalds 	return mtu;
13421da177e4SLinus Torvalds }
13431da177e4SLinus Torvalds 
1344ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1345d33e4553SDavid S. Miller {
1346*4b32b5adSMartin KaFai Lau 	const struct rt6_info *rt = (const struct rt6_info *)dst;
1347*4b32b5adSMartin KaFai Lau 	unsigned int mtu = rt->rt6i_pmtu;
1348d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1349618f9bc7SSteffen Klassert 
1350618f9bc7SSteffen Klassert 	if (mtu)
135130f78d8eSEric Dumazet 		goto out;
1352618f9bc7SSteffen Klassert 
1353*4b32b5adSMartin KaFai Lau 	mtu = dst_metric_raw(dst, RTAX_MTU);
1354*4b32b5adSMartin KaFai Lau 	if (mtu)
1355*4b32b5adSMartin KaFai Lau 		goto out;
1356*4b32b5adSMartin KaFai Lau 
1357618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1358d33e4553SDavid S. Miller 
1359d33e4553SDavid S. Miller 	rcu_read_lock();
1360d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1361d33e4553SDavid S. Miller 	if (idev)
1362d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1363d33e4553SDavid S. Miller 	rcu_read_unlock();
1364d33e4553SDavid S. Miller 
136530f78d8eSEric Dumazet out:
136630f78d8eSEric Dumazet 	return min_t(unsigned int, mtu, IP6_MAX_MTU);
1367d33e4553SDavid S. Miller }
1368d33e4553SDavid S. Miller 
13693b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
13703b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
13715d0bbeebSThomas Graf 
13723b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
137387a11578SDavid S. Miller 				  struct flowi6 *fl6)
13741da177e4SLinus Torvalds {
137587a11578SDavid S. Miller 	struct dst_entry *dst;
13761da177e4SLinus Torvalds 	struct rt6_info *rt;
13771da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1378c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13791da177e4SLinus Torvalds 
138038308473SDavid S. Miller 	if (unlikely(!idev))
1381122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
13821da177e4SLinus Torvalds 
13838b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, dev, 0, NULL);
138438308473SDavid S. Miller 	if (unlikely(!rt)) {
13851da177e4SLinus Torvalds 		in6_dev_put(idev);
138687a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
13871da177e4SLinus Torvalds 		goto out;
13881da177e4SLinus Torvalds 	}
13891da177e4SLinus Torvalds 
13908e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
13918e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
1392d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
1393550bab42SJulian Anastasov 	rt->rt6i_gateway  = fl6->daddr;
139487a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
13958e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
13968e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
139714edd87dSLi RongQing 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
13981da177e4SLinus Torvalds 
13993b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1400d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1401d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
14023b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
14031da177e4SLinus Torvalds 
14045578689aSDaniel Lezcano 	fib6_force_start_gc(net);
14051da177e4SLinus Torvalds 
140687a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
140787a11578SDavid S. Miller 
14081da177e4SLinus Torvalds out:
140987a11578SDavid S. Miller 	return dst;
14101da177e4SLinus Torvalds }
14111da177e4SLinus Torvalds 
14123d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
14131da177e4SLinus Torvalds {
1414e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
14153d0f24a7SStephen Hemminger 	int more = 0;
14161da177e4SLinus Torvalds 
14173b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
14183b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
14195d0bbeebSThomas Graf 
14201da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
14211da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
14221da177e4SLinus Torvalds 			*pprev = dst->next;
14231da177e4SLinus Torvalds 			dst_free(dst);
14241da177e4SLinus Torvalds 		} else {
14251da177e4SLinus Torvalds 			pprev = &dst->next;
14263d0f24a7SStephen Hemminger 			++more;
14271da177e4SLinus Torvalds 		}
14281da177e4SLinus Torvalds 	}
14291da177e4SLinus Torvalds 
14303b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
14315d0bbeebSThomas Graf 
14323d0f24a7SStephen Hemminger 	return more;
14331da177e4SLinus Torvalds }
14341da177e4SLinus Torvalds 
14351e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
14361e493d19SDavid S. Miller 			    void *arg)
14371e493d19SDavid S. Miller {
14381e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
14391e493d19SDavid S. Miller 
14401e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
14411e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
14421e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
14431e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
14441e493d19SDavid S. Miller 		if (func(rt, arg)) {
14451e493d19SDavid S. Miller 			*pprev = dst->next;
14461e493d19SDavid S. Miller 			dst_free(dst);
14471e493d19SDavid S. Miller 		} else {
14481e493d19SDavid S. Miller 			pprev = &dst->next;
14491e493d19SDavid S. Miller 		}
14501e493d19SDavid S. Miller 	}
14511e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
14521e493d19SDavid S. Miller }
14531e493d19SDavid S. Miller 
1454569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
14551da177e4SLinus Torvalds {
145686393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
14577019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
14587019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
14597019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
14607019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
14617019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1462fc66f95cSEric Dumazet 	int entries;
14631da177e4SLinus Torvalds 
1464fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
146549a18d86SMichal Kubeček 	if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
1466fc66f95cSEric Dumazet 	    entries <= rt_max_size)
14671da177e4SLinus Torvalds 		goto out;
14681da177e4SLinus Torvalds 
14696891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
147014956643SLi RongQing 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
1471fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1472fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
14737019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
14741da177e4SLinus Torvalds out:
14757019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1476fc66f95cSEric Dumazet 	return entries > rt_max_size;
14771da177e4SLinus Torvalds }
14781da177e4SLinus Torvalds 
1479e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc,
1480e715b6d3SFlorian Westphal 			       const struct fib6_config *cfg)
1481e715b6d3SFlorian Westphal {
1482e715b6d3SFlorian Westphal 	struct nlattr *nla;
1483e715b6d3SFlorian Westphal 	int remaining;
1484e715b6d3SFlorian Westphal 	u32 *mp;
1485e715b6d3SFlorian Westphal 
148663159f29SIan Morris 	if (!cfg->fc_mx)
1487e715b6d3SFlorian Westphal 		return 0;
1488e715b6d3SFlorian Westphal 
1489e715b6d3SFlorian Westphal 	mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1490e715b6d3SFlorian Westphal 	if (unlikely(!mp))
1491e715b6d3SFlorian Westphal 		return -ENOMEM;
1492e715b6d3SFlorian Westphal 
1493e715b6d3SFlorian Westphal 	nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
1494e715b6d3SFlorian Westphal 		int type = nla_type(nla);
1495e715b6d3SFlorian Westphal 
1496e715b6d3SFlorian Westphal 		if (type) {
1497ea697639SDaniel Borkmann 			u32 val;
1498ea697639SDaniel Borkmann 
1499e715b6d3SFlorian Westphal 			if (unlikely(type > RTAX_MAX))
1500e715b6d3SFlorian Westphal 				goto err;
1501ea697639SDaniel Borkmann 			if (type == RTAX_CC_ALGO) {
1502ea697639SDaniel Borkmann 				char tmp[TCP_CA_NAME_MAX];
1503e715b6d3SFlorian Westphal 
1504ea697639SDaniel Borkmann 				nla_strlcpy(tmp, nla, sizeof(tmp));
1505ea697639SDaniel Borkmann 				val = tcp_ca_get_key_by_name(tmp);
1506ea697639SDaniel Borkmann 				if (val == TCP_CA_UNSPEC)
1507ea697639SDaniel Borkmann 					goto err;
1508ea697639SDaniel Borkmann 			} else {
1509ea697639SDaniel Borkmann 				val = nla_get_u32(nla);
1510ea697639SDaniel Borkmann 			}
1511ea697639SDaniel Borkmann 
1512ea697639SDaniel Borkmann 			mp[type - 1] = val;
1513e715b6d3SFlorian Westphal 			__set_bit(type - 1, mxc->mx_valid);
1514e715b6d3SFlorian Westphal 		}
1515e715b6d3SFlorian Westphal 	}
1516e715b6d3SFlorian Westphal 
1517e715b6d3SFlorian Westphal 	mxc->mx = mp;
1518e715b6d3SFlorian Westphal 
1519e715b6d3SFlorian Westphal 	return 0;
1520e715b6d3SFlorian Westphal  err:
1521e715b6d3SFlorian Westphal 	kfree(mp);
1522e715b6d3SFlorian Westphal 	return -EINVAL;
1523e715b6d3SFlorian Westphal }
15241da177e4SLinus Torvalds 
152586872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
15261da177e4SLinus Torvalds {
15271da177e4SLinus Torvalds 	int err;
15285578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
15291da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
15301da177e4SLinus Torvalds 	struct net_device *dev = NULL;
15311da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1532c71099acSThomas Graf 	struct fib6_table *table;
1533e715b6d3SFlorian Westphal 	struct mx6_config mxc = { .mx = NULL, };
15341da177e4SLinus Torvalds 	int addr_type;
15351da177e4SLinus Torvalds 
153686872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
15371da177e4SLinus Torvalds 		return -EINVAL;
15381da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
153986872cb5SThomas Graf 	if (cfg->fc_src_len)
15401da177e4SLinus Torvalds 		return -EINVAL;
15411da177e4SLinus Torvalds #endif
154286872cb5SThomas Graf 	if (cfg->fc_ifindex) {
15431da177e4SLinus Torvalds 		err = -ENODEV;
15445578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
15451da177e4SLinus Torvalds 		if (!dev)
15461da177e4SLinus Torvalds 			goto out;
15471da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
15481da177e4SLinus Torvalds 		if (!idev)
15491da177e4SLinus Torvalds 			goto out;
15501da177e4SLinus Torvalds 	}
15511da177e4SLinus Torvalds 
155286872cb5SThomas Graf 	if (cfg->fc_metric == 0)
155386872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
15541da177e4SLinus Torvalds 
1555c71099acSThomas Graf 	err = -ENOBUFS;
155638308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1557d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1558d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
155938308473SDavid S. Miller 		if (!table) {
1560f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1561d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1562d71314b4SMatti Vaittinen 		}
1563d71314b4SMatti Vaittinen 	} else {
1564d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1565d71314b4SMatti Vaittinen 	}
156638308473SDavid S. Miller 
156738308473SDavid S. Miller 	if (!table)
1568c71099acSThomas Graf 		goto out;
1569c71099acSThomas Graf 
1570c88507fbSSabrina Dubroca 	rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table);
15711da177e4SLinus Torvalds 
157238308473SDavid S. Miller 	if (!rt) {
15731da177e4SLinus Torvalds 		err = -ENOMEM;
15741da177e4SLinus Torvalds 		goto out;
15751da177e4SLinus Torvalds 	}
15761da177e4SLinus Torvalds 
15771716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
15781716a961SGao feng 		rt6_set_expires(rt, jiffies +
15791716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
15801716a961SGao feng 	else
15811716a961SGao feng 		rt6_clean_expires(rt);
15821da177e4SLinus Torvalds 
158386872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
158486872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
158586872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
158686872cb5SThomas Graf 
158786872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
15881da177e4SLinus Torvalds 
15891da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1590d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1591ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1592ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
15931da177e4SLinus Torvalds 	else
1594d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
15951da177e4SLinus Torvalds 
1596d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
15971da177e4SLinus Torvalds 
159886872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
159986872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
1600e5fd387aSMichal Kubeček 	if (rt->rt6i_dst.plen == 128) {
160111d53b49SDavid S. Miller 		rt->dst.flags |= DST_HOST;
1602e5fd387aSMichal Kubeček 		dst_metrics_set_force_overwrite(&rt->dst);
1603e5fd387aSMichal Kubeček 	}
16041da177e4SLinus Torvalds 
16051da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
160686872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
160786872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
16081da177e4SLinus Torvalds #endif
16091da177e4SLinus Torvalds 
161086872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
16111da177e4SLinus Torvalds 
16121da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
16131da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
16141da177e4SLinus Torvalds 	 */
161586872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
161638308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
161738308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
161838308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
16191da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
16205578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
16211da177e4SLinus Torvalds 			if (dev) {
16221da177e4SLinus Torvalds 				dev_put(dev);
16231da177e4SLinus Torvalds 				in6_dev_put(idev);
16241da177e4SLinus Torvalds 			}
16255578689aSDaniel Lezcano 			dev = net->loopback_dev;
16261da177e4SLinus Torvalds 			dev_hold(dev);
16271da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
16281da177e4SLinus Torvalds 			if (!idev) {
16291da177e4SLinus Torvalds 				err = -ENODEV;
16301da177e4SLinus Torvalds 				goto out;
16311da177e4SLinus Torvalds 			}
16321da177e4SLinus Torvalds 		}
16331da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1634ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1635ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1636ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1637aad88724SEric Dumazet 			rt->dst.output = dst_discard_sk;
16387150aedeSKamala R 			rt->dst.input = dst_discard;
1639ef2c7d7bSNicolas Dichtel 			break;
1640ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1641ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
16427150aedeSKamala R 			rt->dst.output = ip6_pkt_prohibit_out;
16437150aedeSKamala R 			rt->dst.input = ip6_pkt_prohibit;
1644ef2c7d7bSNicolas Dichtel 			break;
1645b4949ab2SNicolas Dichtel 		case RTN_THROW:
1646ef2c7d7bSNicolas Dichtel 		default:
16477150aedeSKamala R 			rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN
16487150aedeSKamala R 					: -ENETUNREACH;
16497150aedeSKamala R 			rt->dst.output = ip6_pkt_discard_out;
16507150aedeSKamala R 			rt->dst.input = ip6_pkt_discard;
1651ef2c7d7bSNicolas Dichtel 			break;
1652ef2c7d7bSNicolas Dichtel 		}
16531da177e4SLinus Torvalds 		goto install_route;
16541da177e4SLinus Torvalds 	}
16551da177e4SLinus Torvalds 
165686872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1657b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
16581da177e4SLinus Torvalds 		int gwa_type;
16591da177e4SLinus Torvalds 
166086872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
16614e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
16621da177e4SLinus Torvalds 		gwa_type = ipv6_addr_type(gw_addr);
16631da177e4SLinus Torvalds 
16641da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
16651da177e4SLinus Torvalds 			struct rt6_info *grt;
16661da177e4SLinus Torvalds 
16671da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
16681da177e4SLinus Torvalds 			   addresses as nexthop address.
16691da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
16701da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
16711da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
16721da177e4SLinus Torvalds 			   some exceptions. --ANK
16731da177e4SLinus Torvalds 			 */
16741da177e4SLinus Torvalds 			err = -EINVAL;
16751da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
16761da177e4SLinus Torvalds 				goto out;
16771da177e4SLinus Torvalds 
16785578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
16791da177e4SLinus Torvalds 
16801da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
168138308473SDavid S. Miller 			if (!grt)
16821da177e4SLinus Torvalds 				goto out;
16831da177e4SLinus Torvalds 			if (dev) {
1684d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
168594e187c0SAmerigo Wang 					ip6_rt_put(grt);
16861da177e4SLinus Torvalds 					goto out;
16871da177e4SLinus Torvalds 				}
16881da177e4SLinus Torvalds 			} else {
1689d1918542SDavid S. Miller 				dev = grt->dst.dev;
16901da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
16911da177e4SLinus Torvalds 				dev_hold(dev);
16921da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
16931da177e4SLinus Torvalds 			}
16941da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
16951da177e4SLinus Torvalds 				err = 0;
169694e187c0SAmerigo Wang 			ip6_rt_put(grt);
16971da177e4SLinus Torvalds 
16981da177e4SLinus Torvalds 			if (err)
16991da177e4SLinus Torvalds 				goto out;
17001da177e4SLinus Torvalds 		}
17011da177e4SLinus Torvalds 		err = -EINVAL;
170238308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
17031da177e4SLinus Torvalds 			goto out;
17041da177e4SLinus Torvalds 	}
17051da177e4SLinus Torvalds 
17061da177e4SLinus Torvalds 	err = -ENODEV;
170738308473SDavid S. Miller 	if (!dev)
17081da177e4SLinus Torvalds 		goto out;
17091da177e4SLinus Torvalds 
1710c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1711c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1712c3968a85SDaniel Walter 			err = -EINVAL;
1713c3968a85SDaniel Walter 			goto out;
1714c3968a85SDaniel Walter 		}
17154e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1716c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1717c3968a85SDaniel Walter 	} else
1718c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1719c3968a85SDaniel Walter 
172086872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
17211da177e4SLinus Torvalds 
17221da177e4SLinus Torvalds install_route:
1723d8d1f30bSChangli Gao 	rt->dst.dev = dev;
17241da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1725c71099acSThomas Graf 	rt->rt6i_table = table;
172663152fc0SDaniel Lezcano 
1727c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
172863152fc0SDaniel Lezcano 
1729e715b6d3SFlorian Westphal 	err = ip6_convert_metrics(&mxc, cfg);
1730e715b6d3SFlorian Westphal 	if (err)
1731e715b6d3SFlorian Westphal 		goto out;
17321da177e4SLinus Torvalds 
1733e715b6d3SFlorian Westphal 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc);
1734e715b6d3SFlorian Westphal 
1735e715b6d3SFlorian Westphal 	kfree(mxc.mx);
1736e715b6d3SFlorian Westphal 	return err;
17371da177e4SLinus Torvalds out:
17381da177e4SLinus Torvalds 	if (dev)
17391da177e4SLinus Torvalds 		dev_put(dev);
17401da177e4SLinus Torvalds 	if (idev)
17411da177e4SLinus Torvalds 		in6_dev_put(idev);
17421da177e4SLinus Torvalds 	if (rt)
1743d8d1f30bSChangli Gao 		dst_free(&rt->dst);
17441da177e4SLinus Torvalds 	return err;
17451da177e4SLinus Torvalds }
17461da177e4SLinus Torvalds 
174786872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
17481da177e4SLinus Torvalds {
17491da177e4SLinus Torvalds 	int err;
1750c71099acSThomas Graf 	struct fib6_table *table;
1751d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
17521da177e4SLinus Torvalds 
17536825a26cSGao feng 	if (rt == net->ipv6.ip6_null_entry) {
17546825a26cSGao feng 		err = -ENOENT;
17556825a26cSGao feng 		goto out;
17566825a26cSGao feng 	}
17576c813a72SPatrick McHardy 
1758c71099acSThomas Graf 	table = rt->rt6i_table;
1759c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
176086872cb5SThomas Graf 	err = fib6_del(rt, info);
1761c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
17621da177e4SLinus Torvalds 
17636825a26cSGao feng out:
176494e187c0SAmerigo Wang 	ip6_rt_put(rt);
17651da177e4SLinus Torvalds 	return err;
17661da177e4SLinus Torvalds }
17671da177e4SLinus Torvalds 
1768e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1769e0a1ad73SThomas Graf {
17704d1169c1SDenis V. Lunev 	struct nl_info info = {
1771d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
17724d1169c1SDenis V. Lunev 	};
1773528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1774e0a1ad73SThomas Graf }
1775e0a1ad73SThomas Graf 
177686872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
17771da177e4SLinus Torvalds {
1778c71099acSThomas Graf 	struct fib6_table *table;
17791da177e4SLinus Torvalds 	struct fib6_node *fn;
17801da177e4SLinus Torvalds 	struct rt6_info *rt;
17811da177e4SLinus Torvalds 	int err = -ESRCH;
17821da177e4SLinus Torvalds 
17835578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
178438308473SDavid S. Miller 	if (!table)
1785c71099acSThomas Graf 		return err;
17861da177e4SLinus Torvalds 
1787c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1788c71099acSThomas Graf 
1789c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
179086872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
179186872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
17921da177e4SLinus Torvalds 
17931da177e4SLinus Torvalds 	if (fn) {
1794d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
17951f56a01fSMartin KaFai Lau 			if ((rt->rt6i_flags & RTF_CACHE) &&
17961f56a01fSMartin KaFai Lau 			    !(cfg->fc_flags & RTF_CACHE))
17971f56a01fSMartin KaFai Lau 				continue;
179886872cb5SThomas Graf 			if (cfg->fc_ifindex &&
1799d1918542SDavid S. Miller 			    (!rt->dst.dev ||
1800d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
18011da177e4SLinus Torvalds 				continue;
180286872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
180386872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
18041da177e4SLinus Torvalds 				continue;
180586872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
18061da177e4SLinus Torvalds 				continue;
1807d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1808c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
18091da177e4SLinus Torvalds 
181086872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
18111da177e4SLinus Torvalds 		}
18121da177e4SLinus Torvalds 	}
1813c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
18141da177e4SLinus Torvalds 
18151da177e4SLinus Torvalds 	return err;
18161da177e4SLinus Torvalds }
18171da177e4SLinus Torvalds 
18186700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
1819a6279458SYOSHIFUJI Hideaki {
1820e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
1821a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
1822e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
1823e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
1824e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
1825e8599ff4SDavid S. Miller 	struct neighbour *neigh;
182671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
18276e157b6aSDavid S. Miller 	int optlen, on_link;
18286e157b6aSDavid S. Miller 	u8 *lladdr;
1829e8599ff4SDavid S. Miller 
183029a3cad5SSimon Horman 	optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
183171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	optlen -= sizeof(*msg);
1832e8599ff4SDavid S. Miller 
1833e8599ff4SDavid S. Miller 	if (optlen < 0) {
18346e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
1835e8599ff4SDavid S. Miller 		return;
1836e8599ff4SDavid S. Miller 	}
1837e8599ff4SDavid S. Miller 
183871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	msg = (struct rd_msg *)icmp6_hdr(skb);
1839e8599ff4SDavid S. Miller 
184071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_is_multicast(&msg->dest)) {
18416e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
1842e8599ff4SDavid S. Miller 		return;
1843e8599ff4SDavid S. Miller 	}
1844e8599ff4SDavid S. Miller 
18456e157b6aSDavid S. Miller 	on_link = 0;
184671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_equal(&msg->dest, &msg->target)) {
1847e8599ff4SDavid S. Miller 		on_link = 1;
184871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	} else if (ipv6_addr_type(&msg->target) !=
1849e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
18506e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
1851e8599ff4SDavid S. Miller 		return;
1852e8599ff4SDavid S. Miller 	}
1853e8599ff4SDavid S. Miller 
1854e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
1855e8599ff4SDavid S. Miller 	if (!in6_dev)
1856e8599ff4SDavid S. Miller 		return;
1857e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1858e8599ff4SDavid S. Miller 		return;
1859e8599ff4SDavid S. Miller 
1860e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
1861e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
1862e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
1863e8599ff4SDavid S. Miller 	 */
1864e8599ff4SDavid S. Miller 
186571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
1866e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1867e8599ff4SDavid S. Miller 		return;
1868e8599ff4SDavid S. Miller 	}
18696e157b6aSDavid S. Miller 
18706e157b6aSDavid S. Miller 	lladdr = NULL;
1871e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
1872e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1873e8599ff4SDavid S. Miller 					     skb->dev);
1874e8599ff4SDavid S. Miller 		if (!lladdr) {
1875e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1876e8599ff4SDavid S. Miller 			return;
1877e8599ff4SDavid S. Miller 		}
1878e8599ff4SDavid S. Miller 	}
1879e8599ff4SDavid S. Miller 
18806e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
18816e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
18826e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
18836e157b6aSDavid S. Miller 		return;
18846e157b6aSDavid S. Miller 	}
18856e157b6aSDavid S. Miller 
18866e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
18876e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
18886e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
18896e157b6aSDavid S. Miller 	 */
18906e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
18916e157b6aSDavid S. Miller 
189271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
1893e8599ff4SDavid S. Miller 	if (!neigh)
1894e8599ff4SDavid S. Miller 		return;
1895e8599ff4SDavid S. Miller 
18961da177e4SLinus Torvalds 	/*
18971da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
18981da177e4SLinus Torvalds 	 */
18991da177e4SLinus Torvalds 
19001da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
19011da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
19021da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
19031da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
19041da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
19051da177e4SLinus Torvalds 		     );
19061da177e4SLinus Torvalds 
190771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	nrt = ip6_rt_copy(rt, &msg->dest);
190838308473SDavid S. Miller 	if (!nrt)
19091da177e4SLinus Torvalds 		goto out;
19101da177e4SLinus Torvalds 
19111da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
19121da177e4SLinus Torvalds 	if (on_link)
19131da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
19141da177e4SLinus Torvalds 
19154e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
19161da177e4SLinus Torvalds 
191740e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
19181da177e4SLinus Torvalds 		goto out;
19191da177e4SLinus Torvalds 
1920d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
1921d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
192271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	netevent.daddr = &msg->dest;
192360592833SYOSHIFUJI Hideaki / 吉藤英明 	netevent.neigh = neigh;
19248d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
19258d71740cSTom Tucker 
19261da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
19276e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
1928e0a1ad73SThomas Graf 		ip6_del_rt(rt);
19291da177e4SLinus Torvalds 	}
19301da177e4SLinus Torvalds 
19311da177e4SLinus Torvalds out:
1932e8599ff4SDavid S. Miller 	neigh_release(neigh);
19336e157b6aSDavid S. Miller }
19346e157b6aSDavid S. Miller 
19351da177e4SLinus Torvalds /*
19361da177e4SLinus Torvalds  *	Misc support functions
19371da177e4SLinus Torvalds  */
19381da177e4SLinus Torvalds 
1939*4b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
1940*4b32b5adSMartin KaFai Lau {
1941*4b32b5adSMartin KaFai Lau 	BUG_ON(from->dst.from);
1942*4b32b5adSMartin KaFai Lau 
1943*4b32b5adSMartin KaFai Lau 	rt->rt6i_flags &= ~RTF_EXPIRES;
1944*4b32b5adSMartin KaFai Lau 	dst_hold(&from->dst);
1945*4b32b5adSMartin KaFai Lau 	rt->dst.from = &from->dst;
1946*4b32b5adSMartin KaFai Lau 	dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true);
1947*4b32b5adSMartin KaFai Lau }
1948*4b32b5adSMartin KaFai Lau 
19491716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
195021efcfa0SEric Dumazet 				    const struct in6_addr *dest)
19511da177e4SLinus Torvalds {
1952d1918542SDavid S. Miller 	struct net *net = dev_net(ort->dst.dev);
1953*4b32b5adSMartin KaFai Lau 	struct rt6_info *rt;
1954*4b32b5adSMartin KaFai Lau 
1955*4b32b5adSMartin KaFai Lau 	if (ort->rt6i_flags & RTF_CACHE)
1956*4b32b5adSMartin KaFai Lau 		ort = (struct rt6_info *)ort->dst.from;
1957*4b32b5adSMartin KaFai Lau 
1958*4b32b5adSMartin KaFai Lau 	rt = ip6_dst_alloc(net, ort->dst.dev, 0,
19598b96d22dSDavid S. Miller 			   ort->rt6i_table);
19601da177e4SLinus Torvalds 
19611da177e4SLinus Torvalds 	if (rt) {
1962d8d1f30bSChangli Gao 		rt->dst.input = ort->dst.input;
1963d8d1f30bSChangli Gao 		rt->dst.output = ort->dst.output;
19648e2ec639SYan, Zheng 		rt->dst.flags |= DST_HOST;
19651da177e4SLinus Torvalds 
19664e3fd7a0SAlexey Dobriyan 		rt->rt6i_dst.addr = *dest;
19678e2ec639SYan, Zheng 		rt->rt6i_dst.plen = 128;
1968d8d1f30bSChangli Gao 		rt->dst.error = ort->dst.error;
19691da177e4SLinus Torvalds 		rt->rt6i_idev = ort->rt6i_idev;
19701da177e4SLinus Torvalds 		if (rt->rt6i_idev)
19711da177e4SLinus Torvalds 			in6_dev_hold(rt->rt6i_idev);
1972d8d1f30bSChangli Gao 		rt->dst.lastuse = jiffies;
19731da177e4SLinus Torvalds 
1974550bab42SJulian Anastasov 		if (ort->rt6i_flags & RTF_GATEWAY)
19754e3fd7a0SAlexey Dobriyan 			rt->rt6i_gateway = ort->rt6i_gateway;
1976550bab42SJulian Anastasov 		else
1977550bab42SJulian Anastasov 			rt->rt6i_gateway = *dest;
19781716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
19791716a961SGao feng 		rt6_set_from(rt, ort);
19801da177e4SLinus Torvalds 		rt->rt6i_metric = 0;
19811da177e4SLinus Torvalds 
19821da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
19831da177e4SLinus Torvalds 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
19841da177e4SLinus Torvalds #endif
19850f6c6392SFlorian Westphal 		memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
1986c71099acSThomas Graf 		rt->rt6i_table = ort->rt6i_table;
19871da177e4SLinus Torvalds 	}
19881da177e4SLinus Torvalds 	return rt;
19891da177e4SLinus Torvalds }
19901da177e4SLinus Torvalds 
199170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
1992efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
1993b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1994b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
199570ceb4f5SYOSHIFUJI Hideaki {
199670ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
199770ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
1998c71099acSThomas Graf 	struct fib6_table *table;
199970ceb4f5SYOSHIFUJI Hideaki 
2000efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
200138308473SDavid S. Miller 	if (!table)
2002c71099acSThomas Graf 		return NULL;
2003c71099acSThomas Graf 
20045744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2005c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0);
200670ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
200770ceb4f5SYOSHIFUJI Hideaki 		goto out;
200870ceb4f5SYOSHIFUJI Hideaki 
2009d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
2010d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
201170ceb4f5SYOSHIFUJI Hideaki 			continue;
201270ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
201370ceb4f5SYOSHIFUJI Hideaki 			continue;
201470ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
201570ceb4f5SYOSHIFUJI Hideaki 			continue;
2016d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
201770ceb4f5SYOSHIFUJI Hideaki 		break;
201870ceb4f5SYOSHIFUJI Hideaki 	}
201970ceb4f5SYOSHIFUJI Hideaki out:
20205744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
202170ceb4f5SYOSHIFUJI Hideaki 	return rt;
202270ceb4f5SYOSHIFUJI Hideaki }
202370ceb4f5SYOSHIFUJI Hideaki 
2024efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
2025b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
2026b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
202795c96174SEric Dumazet 					   unsigned int pref)
202870ceb4f5SYOSHIFUJI Hideaki {
202986872cb5SThomas Graf 	struct fib6_config cfg = {
203086872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
2031238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
203286872cb5SThomas Graf 		.fc_ifindex	= ifindex,
203386872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
203486872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
203586872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
203615e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
2037efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2038efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
203986872cb5SThomas Graf 	};
204070ceb4f5SYOSHIFUJI Hideaki 
20414e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
20424e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
204386872cb5SThomas Graf 
2044e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
2045e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
204686872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
204770ceb4f5SYOSHIFUJI Hideaki 
204886872cb5SThomas Graf 	ip6_route_add(&cfg);
204970ceb4f5SYOSHIFUJI Hideaki 
2050efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
205170ceb4f5SYOSHIFUJI Hideaki }
205270ceb4f5SYOSHIFUJI Hideaki #endif
205370ceb4f5SYOSHIFUJI Hideaki 
2054b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
20551da177e4SLinus Torvalds {
20561da177e4SLinus Torvalds 	struct rt6_info *rt;
2057c71099acSThomas Graf 	struct fib6_table *table;
20581da177e4SLinus Torvalds 
2059c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
206038308473SDavid S. Miller 	if (!table)
2061c71099acSThomas Graf 		return NULL;
20621da177e4SLinus Torvalds 
20635744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
2064d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
2065d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
2066045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
20671da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
20681da177e4SLinus Torvalds 			break;
20691da177e4SLinus Torvalds 	}
20701da177e4SLinus Torvalds 	if (rt)
2071d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
20725744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
20731da177e4SLinus Torvalds 	return rt;
20741da177e4SLinus Torvalds }
20751da177e4SLinus Torvalds 
2076b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
2077ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
2078ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
20791da177e4SLinus Torvalds {
208086872cb5SThomas Graf 	struct fib6_config cfg = {
208186872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
2082238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
208386872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
208486872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
208586872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
208615e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
20875578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
2088c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
208986872cb5SThomas Graf 	};
20901da177e4SLinus Torvalds 
20914e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
20921da177e4SLinus Torvalds 
209386872cb5SThomas Graf 	ip6_route_add(&cfg);
20941da177e4SLinus Torvalds 
20951da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
20961da177e4SLinus Torvalds }
20971da177e4SLinus Torvalds 
20987b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
20991da177e4SLinus Torvalds {
21001da177e4SLinus Torvalds 	struct rt6_info *rt;
2101c71099acSThomas Graf 	struct fib6_table *table;
2102c71099acSThomas Graf 
2103c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
21047b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
210538308473SDavid S. Miller 	if (!table)
2106c71099acSThomas Graf 		return;
21071da177e4SLinus Torvalds 
21081da177e4SLinus Torvalds restart:
2109c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
2110d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
21113e8b0ac3SLorenzo Colitti 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
21123e8b0ac3SLorenzo Colitti 		    (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) {
2113d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
2114c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
2115e0a1ad73SThomas Graf 			ip6_del_rt(rt);
21161da177e4SLinus Torvalds 			goto restart;
21171da177e4SLinus Torvalds 		}
21181da177e4SLinus Torvalds 	}
2119c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
21201da177e4SLinus Torvalds }
21211da177e4SLinus Torvalds 
21225578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
21235578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
212486872cb5SThomas Graf 				 struct fib6_config *cfg)
212586872cb5SThomas Graf {
212686872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
212786872cb5SThomas Graf 
212886872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
212986872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
213086872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
213186872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
213286872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
213386872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
213486872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
213586872cb5SThomas Graf 
21365578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
2137f1243c2dSBenjamin Thery 
21384e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
21394e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
21404e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
214186872cb5SThomas Graf }
214286872cb5SThomas Graf 
21435578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
21441da177e4SLinus Torvalds {
214586872cb5SThomas Graf 	struct fib6_config cfg;
21461da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
21471da177e4SLinus Torvalds 	int err;
21481da177e4SLinus Torvalds 
21491da177e4SLinus Torvalds 	switch (cmd) {
21501da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
21511da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
2152af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
21531da177e4SLinus Torvalds 			return -EPERM;
21541da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
21551da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
21561da177e4SLinus Torvalds 		if (err)
21571da177e4SLinus Torvalds 			return -EFAULT;
21581da177e4SLinus Torvalds 
21595578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
216086872cb5SThomas Graf 
21611da177e4SLinus Torvalds 		rtnl_lock();
21621da177e4SLinus Torvalds 		switch (cmd) {
21631da177e4SLinus Torvalds 		case SIOCADDRT:
216486872cb5SThomas Graf 			err = ip6_route_add(&cfg);
21651da177e4SLinus Torvalds 			break;
21661da177e4SLinus Torvalds 		case SIOCDELRT:
216786872cb5SThomas Graf 			err = ip6_route_del(&cfg);
21681da177e4SLinus Torvalds 			break;
21691da177e4SLinus Torvalds 		default:
21701da177e4SLinus Torvalds 			err = -EINVAL;
21711da177e4SLinus Torvalds 		}
21721da177e4SLinus Torvalds 		rtnl_unlock();
21731da177e4SLinus Torvalds 
21741da177e4SLinus Torvalds 		return err;
21753ff50b79SStephen Hemminger 	}
21761da177e4SLinus Torvalds 
21771da177e4SLinus Torvalds 	return -EINVAL;
21781da177e4SLinus Torvalds }
21791da177e4SLinus Torvalds 
21801da177e4SLinus Torvalds /*
21811da177e4SLinus Torvalds  *	Drop the packet on the floor
21821da177e4SLinus Torvalds  */
21831da177e4SLinus Torvalds 
2184d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
21851da177e4SLinus Torvalds {
2186612f09e8SYOSHIFUJI Hideaki 	int type;
2187adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2188612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2189612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
21900660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
219145bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
21923bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
21933bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2194612f09e8SYOSHIFUJI Hideaki 			break;
2195612f09e8SYOSHIFUJI Hideaki 		}
2196612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2197612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
21983bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
21993bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2200612f09e8SYOSHIFUJI Hideaki 		break;
2201612f09e8SYOSHIFUJI Hideaki 	}
22023ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
22031da177e4SLinus Torvalds 	kfree_skb(skb);
22041da177e4SLinus Torvalds 	return 0;
22051da177e4SLinus Torvalds }
22061da177e4SLinus Torvalds 
22079ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
22089ce8ade0SThomas Graf {
2209612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
22109ce8ade0SThomas Graf }
22119ce8ade0SThomas Graf 
2212aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb)
22131da177e4SLinus Torvalds {
2214adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2215612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
22161da177e4SLinus Torvalds }
22171da177e4SLinus Torvalds 
22189ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
22199ce8ade0SThomas Graf {
2220612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
22219ce8ade0SThomas Graf }
22229ce8ade0SThomas Graf 
2223aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb)
22249ce8ade0SThomas Graf {
2225adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2226612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
22279ce8ade0SThomas Graf }
22289ce8ade0SThomas Graf 
22291da177e4SLinus Torvalds /*
22301da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
22311da177e4SLinus Torvalds  */
22321da177e4SLinus Torvalds 
22331da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
22341da177e4SLinus Torvalds 				    const struct in6_addr *addr,
22358f031519SDavid S. Miller 				    bool anycast)
22361da177e4SLinus Torvalds {
2237c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
2238a3300ef4SHannes Frederic Sowa 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
2239a3300ef4SHannes Frederic Sowa 					    DST_NOCOUNT, NULL);
2240a3300ef4SHannes Frederic Sowa 	if (!rt)
22411da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
22421da177e4SLinus Torvalds 
22431da177e4SLinus Torvalds 	in6_dev_hold(idev);
22441da177e4SLinus Torvalds 
224511d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2246d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2247d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
22481da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
22491da177e4SLinus Torvalds 
22501da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
225158c4fb86SYOSHIFUJI Hideaki 	if (anycast)
225258c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
225358c4fb86SYOSHIFUJI Hideaki 	else
22541da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
22551da177e4SLinus Torvalds 
2256550bab42SJulian Anastasov 	rt->rt6i_gateway  = *addr;
22574e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
22581da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
22595578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
22601da177e4SLinus Torvalds 
2261d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
22621da177e4SLinus Torvalds 
22631da177e4SLinus Torvalds 	return rt;
22641da177e4SLinus Torvalds }
22651da177e4SLinus Torvalds 
2266c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2267c3968a85SDaniel Walter 			struct rt6_info *rt,
2268b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2269c3968a85SDaniel Walter 			unsigned int prefs,
2270c3968a85SDaniel Walter 			struct in6_addr *saddr)
2271c3968a85SDaniel Walter {
2272c3968a85SDaniel Walter 	struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
2273c3968a85SDaniel Walter 	int err = 0;
2274c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen)
22754e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2276c3968a85SDaniel Walter 	else
2277c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2278c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2279c3968a85SDaniel Walter 	return err;
2280c3968a85SDaniel Walter }
2281c3968a85SDaniel Walter 
2282c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2283c3968a85SDaniel Walter struct arg_dev_net_ip {
2284c3968a85SDaniel Walter 	struct net_device *dev;
2285c3968a85SDaniel Walter 	struct net *net;
2286c3968a85SDaniel Walter 	struct in6_addr *addr;
2287c3968a85SDaniel Walter };
2288c3968a85SDaniel Walter 
2289c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2290c3968a85SDaniel Walter {
2291c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2292c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2293c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2294c3968a85SDaniel Walter 
2295d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2296c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2297c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2298c3968a85SDaniel Walter 		/* remove prefsrc entry */
2299c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2300c3968a85SDaniel Walter 	}
2301c3968a85SDaniel Walter 	return 0;
2302c3968a85SDaniel Walter }
2303c3968a85SDaniel Walter 
2304c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2305c3968a85SDaniel Walter {
2306c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2307c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2308c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2309c3968a85SDaniel Walter 		.net = net,
2310c3968a85SDaniel Walter 		.addr = &ifp->addr,
2311c3968a85SDaniel Walter 	};
23120c3584d5SLi RongQing 	fib6_clean_all(net, fib6_remove_prefsrc, &adni);
2313c3968a85SDaniel Walter }
2314c3968a85SDaniel Walter 
2315be7a010dSDuan Jiong #define RTF_RA_ROUTER		(RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY)
2316be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY	(RTF_GATEWAY | RTF_CACHE)
2317be7a010dSDuan Jiong 
2318be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */
2319be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
2320be7a010dSDuan Jiong {
2321be7a010dSDuan Jiong 	struct in6_addr *gateway = (struct in6_addr *)arg;
2322be7a010dSDuan Jiong 
2323be7a010dSDuan Jiong 	if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) ||
2324be7a010dSDuan Jiong 	     ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) &&
2325be7a010dSDuan Jiong 	     ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
2326be7a010dSDuan Jiong 		return -1;
2327be7a010dSDuan Jiong 	}
2328be7a010dSDuan Jiong 	return 0;
2329be7a010dSDuan Jiong }
2330be7a010dSDuan Jiong 
2331be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
2332be7a010dSDuan Jiong {
2333be7a010dSDuan Jiong 	fib6_clean_all(net, fib6_clean_tohost, gateway);
2334be7a010dSDuan Jiong }
2335be7a010dSDuan Jiong 
23368ed67789SDaniel Lezcano struct arg_dev_net {
23378ed67789SDaniel Lezcano 	struct net_device *dev;
23388ed67789SDaniel Lezcano 	struct net *net;
23398ed67789SDaniel Lezcano };
23408ed67789SDaniel Lezcano 
23411da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
23421da177e4SLinus Torvalds {
2343bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2344bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
23458ed67789SDaniel Lezcano 
2346d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2347c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
23481da177e4SLinus Torvalds 		return -1;
2349c159d30cSDavid S. Miller 
23501da177e4SLinus Torvalds 	return 0;
23511da177e4SLinus Torvalds }
23521da177e4SLinus Torvalds 
2353f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
23541da177e4SLinus Torvalds {
23558ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
23568ed67789SDaniel Lezcano 		.dev = dev,
23578ed67789SDaniel Lezcano 		.net = net,
23588ed67789SDaniel Lezcano 	};
23598ed67789SDaniel Lezcano 
23600c3584d5SLi RongQing 	fib6_clean_all(net, fib6_ifdown, &adn);
23611e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
23621da177e4SLinus Torvalds }
23631da177e4SLinus Torvalds 
236495c96174SEric Dumazet struct rt6_mtu_change_arg {
23651da177e4SLinus Torvalds 	struct net_device *dev;
236695c96174SEric Dumazet 	unsigned int mtu;
23671da177e4SLinus Torvalds };
23681da177e4SLinus Torvalds 
23691da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
23701da177e4SLinus Torvalds {
23711da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
23721da177e4SLinus Torvalds 	struct inet6_dev *idev;
23731da177e4SLinus Torvalds 
23741da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
23751da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
23761da177e4SLinus Torvalds 	   We still use this lock to block changes
23771da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
23781da177e4SLinus Torvalds 	*/
23791da177e4SLinus Torvalds 
23801da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
238138308473SDavid S. Miller 	if (!idev)
23821da177e4SLinus Torvalds 		return 0;
23831da177e4SLinus Torvalds 
23841da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
23851da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
23861da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
23871da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
23881da177e4SLinus Torvalds 	 */
23891da177e4SLinus Torvalds 	/*
23901da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
23911da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
23921da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
23931da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
23941da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
23951da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
23961da177e4SLinus Torvalds 	   PMTU discouvery.
23971da177e4SLinus Torvalds 	 */
2398d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
2399*4b32b5adSMartin KaFai Lau 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
2400*4b32b5adSMartin KaFai Lau 		if (rt->rt6i_flags & RTF_CACHE) {
2401*4b32b5adSMartin KaFai Lau 			/* For RTF_CACHE with rt6i_pmtu == 0
2402*4b32b5adSMartin KaFai Lau 			 * (i.e. a redirected route),
2403*4b32b5adSMartin KaFai Lau 			 * the metrics of its rt->dst.from has already
2404*4b32b5adSMartin KaFai Lau 			 * been updated.
2405*4b32b5adSMartin KaFai Lau 			 */
2406*4b32b5adSMartin KaFai Lau 			if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu)
2407*4b32b5adSMartin KaFai Lau 				rt->rt6i_pmtu = arg->mtu;
2408*4b32b5adSMartin KaFai Lau 		} else if (dst_mtu(&rt->dst) >= arg->mtu ||
2409d8d1f30bSChangli Gao 			   (dst_mtu(&rt->dst) < arg->mtu &&
2410*4b32b5adSMartin KaFai Lau 			    dst_mtu(&rt->dst) == idev->cnf.mtu6)) {
2411defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2412566cfd8fSSimon Arlott 		}
2413*4b32b5adSMartin KaFai Lau 	}
24141da177e4SLinus Torvalds 	return 0;
24151da177e4SLinus Torvalds }
24161da177e4SLinus Torvalds 
241795c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
24181da177e4SLinus Torvalds {
2419c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2420c71099acSThomas Graf 		.dev = dev,
2421c71099acSThomas Graf 		.mtu = mtu,
2422c71099acSThomas Graf 	};
24231da177e4SLinus Torvalds 
24240c3584d5SLi RongQing 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
24251da177e4SLinus Torvalds }
24261da177e4SLinus Torvalds 
2427ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
24285176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
242986872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2430ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
243186872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
243286872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
243351ebd318SNicolas Dichtel 	[RTA_MULTIPATH]		= { .len = sizeof(struct rtnexthop) },
2434c78ba6d6SLubomir Rintel 	[RTA_PREF]              = { .type = NLA_U8 },
243586872cb5SThomas Graf };
243686872cb5SThomas Graf 
243786872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
243886872cb5SThomas Graf 			      struct fib6_config *cfg)
24391da177e4SLinus Torvalds {
244086872cb5SThomas Graf 	struct rtmsg *rtm;
244186872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
2442c78ba6d6SLubomir Rintel 	unsigned int pref;
244386872cb5SThomas Graf 	int err;
24441da177e4SLinus Torvalds 
244586872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
244686872cb5SThomas Graf 	if (err < 0)
244786872cb5SThomas Graf 		goto errout;
24481da177e4SLinus Torvalds 
244986872cb5SThomas Graf 	err = -EINVAL;
245086872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
245186872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
245286872cb5SThomas Graf 
245386872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
245486872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
245586872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
245686872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
245786872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2458ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
245986872cb5SThomas Graf 
2460ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2461ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2462b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2463b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
246486872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
246586872cb5SThomas Graf 
2466ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2467ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2468ab79ad14SMaciej Żenczykowski 
24691f56a01fSMartin KaFai Lau 	if (rtm->rtm_flags & RTM_F_CLONED)
24701f56a01fSMartin KaFai Lau 		cfg->fc_flags |= RTF_CACHE;
24711f56a01fSMartin KaFai Lau 
247215e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
247386872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
24743b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
247586872cb5SThomas Graf 
247686872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
247767b61f6cSJiri Benc 		cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
247886872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
24791da177e4SLinus Torvalds 	}
248086872cb5SThomas Graf 
248186872cb5SThomas Graf 	if (tb[RTA_DST]) {
248286872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
248386872cb5SThomas Graf 
248486872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
248586872cb5SThomas Graf 			goto errout;
248686872cb5SThomas Graf 
248786872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
24881da177e4SLinus Torvalds 	}
248986872cb5SThomas Graf 
249086872cb5SThomas Graf 	if (tb[RTA_SRC]) {
249186872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
249286872cb5SThomas Graf 
249386872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
249486872cb5SThomas Graf 			goto errout;
249586872cb5SThomas Graf 
249686872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
24971da177e4SLinus Torvalds 	}
249886872cb5SThomas Graf 
2499c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
250067b61f6cSJiri Benc 		cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
2501c3968a85SDaniel Walter 
250286872cb5SThomas Graf 	if (tb[RTA_OIF])
250386872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
250486872cb5SThomas Graf 
250586872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
250686872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
250786872cb5SThomas Graf 
250886872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
250986872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
251086872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
25111da177e4SLinus Torvalds 	}
251286872cb5SThomas Graf 
251386872cb5SThomas Graf 	if (tb[RTA_TABLE])
251486872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
251586872cb5SThomas Graf 
251651ebd318SNicolas Dichtel 	if (tb[RTA_MULTIPATH]) {
251751ebd318SNicolas Dichtel 		cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
251851ebd318SNicolas Dichtel 		cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
251951ebd318SNicolas Dichtel 	}
252051ebd318SNicolas Dichtel 
2521c78ba6d6SLubomir Rintel 	if (tb[RTA_PREF]) {
2522c78ba6d6SLubomir Rintel 		pref = nla_get_u8(tb[RTA_PREF]);
2523c78ba6d6SLubomir Rintel 		if (pref != ICMPV6_ROUTER_PREF_LOW &&
2524c78ba6d6SLubomir Rintel 		    pref != ICMPV6_ROUTER_PREF_HIGH)
2525c78ba6d6SLubomir Rintel 			pref = ICMPV6_ROUTER_PREF_MEDIUM;
2526c78ba6d6SLubomir Rintel 		cfg->fc_flags |= RTF_PREF(pref);
2527c78ba6d6SLubomir Rintel 	}
2528c78ba6d6SLubomir Rintel 
252986872cb5SThomas Graf 	err = 0;
253086872cb5SThomas Graf errout:
253186872cb5SThomas Graf 	return err;
25321da177e4SLinus Torvalds }
25331da177e4SLinus Torvalds 
253451ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add)
253551ebd318SNicolas Dichtel {
253651ebd318SNicolas Dichtel 	struct fib6_config r_cfg;
253751ebd318SNicolas Dichtel 	struct rtnexthop *rtnh;
253851ebd318SNicolas Dichtel 	int remaining;
253951ebd318SNicolas Dichtel 	int attrlen;
254051ebd318SNicolas Dichtel 	int err = 0, last_err = 0;
254151ebd318SNicolas Dichtel 
254251ebd318SNicolas Dichtel beginning:
254351ebd318SNicolas Dichtel 	rtnh = (struct rtnexthop *)cfg->fc_mp;
254451ebd318SNicolas Dichtel 	remaining = cfg->fc_mp_len;
254551ebd318SNicolas Dichtel 
254651ebd318SNicolas Dichtel 	/* Parse a Multipath Entry */
254751ebd318SNicolas Dichtel 	while (rtnh_ok(rtnh, remaining)) {
254851ebd318SNicolas Dichtel 		memcpy(&r_cfg, cfg, sizeof(*cfg));
254951ebd318SNicolas Dichtel 		if (rtnh->rtnh_ifindex)
255051ebd318SNicolas Dichtel 			r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
255151ebd318SNicolas Dichtel 
255251ebd318SNicolas Dichtel 		attrlen = rtnh_attrlen(rtnh);
255351ebd318SNicolas Dichtel 		if (attrlen > 0) {
255451ebd318SNicolas Dichtel 			struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
255551ebd318SNicolas Dichtel 
255651ebd318SNicolas Dichtel 			nla = nla_find(attrs, attrlen, RTA_GATEWAY);
255751ebd318SNicolas Dichtel 			if (nla) {
255867b61f6cSJiri Benc 				r_cfg.fc_gateway = nla_get_in6_addr(nla);
255951ebd318SNicolas Dichtel 				r_cfg.fc_flags |= RTF_GATEWAY;
256051ebd318SNicolas Dichtel 			}
256151ebd318SNicolas Dichtel 		}
256251ebd318SNicolas Dichtel 		err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
256351ebd318SNicolas Dichtel 		if (err) {
256451ebd318SNicolas Dichtel 			last_err = err;
256551ebd318SNicolas Dichtel 			/* If we are trying to remove a route, do not stop the
256651ebd318SNicolas Dichtel 			 * loop when ip6_route_del() fails (because next hop is
256751ebd318SNicolas Dichtel 			 * already gone), we should try to remove all next hops.
256851ebd318SNicolas Dichtel 			 */
256951ebd318SNicolas Dichtel 			if (add) {
257051ebd318SNicolas Dichtel 				/* If add fails, we should try to delete all
257151ebd318SNicolas Dichtel 				 * next hops that have been already added.
257251ebd318SNicolas Dichtel 				 */
257351ebd318SNicolas Dichtel 				add = 0;
257451ebd318SNicolas Dichtel 				goto beginning;
257551ebd318SNicolas Dichtel 			}
257651ebd318SNicolas Dichtel 		}
25771a72418bSNicolas Dichtel 		/* Because each route is added like a single route we remove
25781a72418bSNicolas Dichtel 		 * this flag after the first nexthop (if there is a collision,
25791a72418bSNicolas Dichtel 		 * we have already fail to add the first nexthop:
25801a72418bSNicolas Dichtel 		 * fib6_add_rt2node() has reject it).
25811a72418bSNicolas Dichtel 		 */
25821a72418bSNicolas Dichtel 		cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL;
258351ebd318SNicolas Dichtel 		rtnh = rtnh_next(rtnh, &remaining);
258451ebd318SNicolas Dichtel 	}
258551ebd318SNicolas Dichtel 
258651ebd318SNicolas Dichtel 	return last_err;
258751ebd318SNicolas Dichtel }
258851ebd318SNicolas Dichtel 
2589661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
25901da177e4SLinus Torvalds {
259186872cb5SThomas Graf 	struct fib6_config cfg;
259286872cb5SThomas Graf 	int err;
25931da177e4SLinus Torvalds 
259486872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
259586872cb5SThomas Graf 	if (err < 0)
259686872cb5SThomas Graf 		return err;
259786872cb5SThomas Graf 
259851ebd318SNicolas Dichtel 	if (cfg.fc_mp)
259951ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 0);
260051ebd318SNicolas Dichtel 	else
260186872cb5SThomas Graf 		return ip6_route_del(&cfg);
26021da177e4SLinus Torvalds }
26031da177e4SLinus Torvalds 
2604661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
26051da177e4SLinus Torvalds {
260686872cb5SThomas Graf 	struct fib6_config cfg;
260786872cb5SThomas Graf 	int err;
26081da177e4SLinus Torvalds 
260986872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
261086872cb5SThomas Graf 	if (err < 0)
261186872cb5SThomas Graf 		return err;
261286872cb5SThomas Graf 
261351ebd318SNicolas Dichtel 	if (cfg.fc_mp)
261451ebd318SNicolas Dichtel 		return ip6_route_multipath(&cfg, 1);
261551ebd318SNicolas Dichtel 	else
261686872cb5SThomas Graf 		return ip6_route_add(&cfg);
26171da177e4SLinus Torvalds }
26181da177e4SLinus Torvalds 
2619339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void)
2620339bf98fSThomas Graf {
2621339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2622339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2623339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2624339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2625339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2626339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2627339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2628339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2629339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
26306a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2631ea697639SDaniel Borkmann 	       + nla_total_size(sizeof(struct rta_cacheinfo))
2632c78ba6d6SLubomir Rintel 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
2633c78ba6d6SLubomir Rintel 	       + nla_total_size(1); /* RTA_PREF */
2634339bf98fSThomas Graf }
2635339bf98fSThomas Graf 
2636191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2637191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
26380d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
263915e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
26407bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
26411da177e4SLinus Torvalds {
2642*4b32b5adSMartin KaFai Lau 	u32 metrics[RTAX_MAX];
26431da177e4SLinus Torvalds 	struct rtmsg *rtm;
26441da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2645e3703b3dSThomas Graf 	long expires;
26469e762a4aSPatrick McHardy 	u32 table;
26471da177e4SLinus Torvalds 
26481da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
26491da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
26501da177e4SLinus Torvalds 			/* success since this is not a prefix route */
26511da177e4SLinus Torvalds 			return 1;
26521da177e4SLinus Torvalds 		}
26531da177e4SLinus Torvalds 	}
26541da177e4SLinus Torvalds 
265515e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
265638308473SDavid S. Miller 	if (!nlh)
265726932566SPatrick McHardy 		return -EMSGSIZE;
26582d7202bfSThomas Graf 
26592d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
26601da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
26611da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
26621da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
26631da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2664c71099acSThomas Graf 	if (rt->rt6i_table)
26659e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2666c71099acSThomas Graf 	else
26679e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
26689e762a4aSPatrick McHardy 	rtm->rtm_table = table;
2669c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
2670c78679e8SDavid S. Miller 		goto nla_put_failure;
2671ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
2672ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
2673ef2c7d7bSNicolas Dichtel 		case -EINVAL:
2674ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
2675ef2c7d7bSNicolas Dichtel 			break;
2676ef2c7d7bSNicolas Dichtel 		case -EACCES:
2677ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
2678ef2c7d7bSNicolas Dichtel 			break;
2679b4949ab2SNicolas Dichtel 		case -EAGAIN:
2680b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
2681b4949ab2SNicolas Dichtel 			break;
2682ef2c7d7bSNicolas Dichtel 		default:
26831da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
2684ef2c7d7bSNicolas Dichtel 			break;
2685ef2c7d7bSNicolas Dichtel 		}
2686ef2c7d7bSNicolas Dichtel 	}
2687ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2688ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2689d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
26901da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
26911da177e4SLinus Torvalds 	else
26921da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
26931da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
26941da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
26951da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
26961da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
26971da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
2698f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
2699f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
27001da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
2701f0396f60SDenis Ovsienko 		else
2702f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
2703f0396f60SDenis Ovsienko 	}
27041da177e4SLinus Torvalds 
27051da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
27061da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
27071da177e4SLinus Torvalds 
27081da177e4SLinus Torvalds 	if (dst) {
2709930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, dst))
2710c78679e8SDavid S. Miller 			goto nla_put_failure;
27111da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
27121da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
2713930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr))
2714c78679e8SDavid S. Miller 			goto nla_put_failure;
27151da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
27161da177e4SLinus Torvalds 	if (src) {
2717930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_SRC, src))
2718c78679e8SDavid S. Miller 			goto nla_put_failure;
27191da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
2720c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
2721930345eaSJiri Benc 		   nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr))
2722c78679e8SDavid S. Miller 		goto nla_put_failure;
27231da177e4SLinus Torvalds #endif
27247bc570c8SYOSHIFUJI Hideaki 	if (iif) {
27257bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
27267bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
27278229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
27287bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
27297bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
27307bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
27317bc570c8SYOSHIFUJI Hideaki 						return 0;
27327bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
27337bc570c8SYOSHIFUJI Hideaki 				} else {
27347bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
27357bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
27367bc570c8SYOSHIFUJI Hideaki 				}
27377bc570c8SYOSHIFUJI Hideaki 			}
27387bc570c8SYOSHIFUJI Hideaki 		} else
27397bc570c8SYOSHIFUJI Hideaki #endif
2740c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
2741c78679e8SDavid S. Miller 				goto nla_put_failure;
27427bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
27431da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2744c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2745930345eaSJiri Benc 		    nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2746c78679e8SDavid S. Miller 			goto nla_put_failure;
2747c3968a85SDaniel Walter 	}
2748c3968a85SDaniel Walter 
2749c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2750c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
27514e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
2752930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
2753c78679e8SDavid S. Miller 			goto nla_put_failure;
27541da177e4SLinus Torvalds 	}
27552d7202bfSThomas Graf 
2756*4b32b5adSMartin KaFai Lau 	memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
2757*4b32b5adSMartin KaFai Lau 	if (rt->rt6i_pmtu)
2758*4b32b5adSMartin KaFai Lau 		metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
2759*4b32b5adSMartin KaFai Lau 	if (rtnetlink_put_metrics(skb, metrics) < 0)
27602d7202bfSThomas Graf 		goto nla_put_failure;
27612d7202bfSThomas Graf 
2762dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 	if (rt->rt6i_flags & RTF_GATEWAY) {
2763930345eaSJiri Benc 		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
276494f826b8SEric Dumazet 			goto nla_put_failure;
276594f826b8SEric Dumazet 	}
27662d7202bfSThomas Graf 
2767c78679e8SDavid S. Miller 	if (rt->dst.dev &&
2768c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2769c78679e8SDavid S. Miller 		goto nla_put_failure;
2770c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2771c78679e8SDavid S. Miller 		goto nla_put_failure;
27728253947eSLi Wei 
27738253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
277469cdf8f9SYOSHIFUJI Hideaki 
277587a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
2776e3703b3dSThomas Graf 		goto nla_put_failure;
27771da177e4SLinus Torvalds 
2778c78ba6d6SLubomir Rintel 	if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
2779c78ba6d6SLubomir Rintel 		goto nla_put_failure;
2780c78ba6d6SLubomir Rintel 
2781053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2782053c095aSJohannes Berg 	return 0;
27832d7202bfSThomas Graf 
27842d7202bfSThomas Graf nla_put_failure:
278526932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
278626932566SPatrick McHardy 	return -EMSGSIZE;
27871da177e4SLinus Torvalds }
27881da177e4SLinus Torvalds 
27891b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
27901da177e4SLinus Torvalds {
27911da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
27921da177e4SLinus Torvalds 	int prefix;
27931da177e4SLinus Torvalds 
27942d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
27952d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
27961da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
27971da177e4SLinus Torvalds 	} else
27981da177e4SLinus Torvalds 		prefix = 0;
27991da177e4SLinus Torvalds 
2800191cd582SBrian Haley 	return rt6_fill_node(arg->net,
2801191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
280215e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
28037bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
28041da177e4SLinus Torvalds }
28051da177e4SLinus Torvalds 
2806661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
28071da177e4SLinus Torvalds {
28083b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
2809ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
28101da177e4SLinus Torvalds 	struct rt6_info *rt;
2811ab364a6fSThomas Graf 	struct sk_buff *skb;
2812ab364a6fSThomas Graf 	struct rtmsg *rtm;
28134c9483b2SDavid S. Miller 	struct flowi6 fl6;
281472331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
2815ab364a6fSThomas Graf 
2816ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2817ab364a6fSThomas Graf 	if (err < 0)
2818ab364a6fSThomas Graf 		goto errout;
2819ab364a6fSThomas Graf 
2820ab364a6fSThomas Graf 	err = -EINVAL;
28214c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2822ab364a6fSThomas Graf 
2823ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
2824ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2825ab364a6fSThomas Graf 			goto errout;
2826ab364a6fSThomas Graf 
28274e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
2828ab364a6fSThomas Graf 	}
2829ab364a6fSThomas Graf 
2830ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
2831ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2832ab364a6fSThomas Graf 			goto errout;
2833ab364a6fSThomas Graf 
28344e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
2835ab364a6fSThomas Graf 	}
2836ab364a6fSThomas Graf 
2837ab364a6fSThomas Graf 	if (tb[RTA_IIF])
2838ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
2839ab364a6fSThomas Graf 
2840ab364a6fSThomas Graf 	if (tb[RTA_OIF])
284172331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
2842ab364a6fSThomas Graf 
28432e47b291SLorenzo Colitti 	if (tb[RTA_MARK])
28442e47b291SLorenzo Colitti 		fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
28452e47b291SLorenzo Colitti 
2846ab364a6fSThomas Graf 	if (iif) {
2847ab364a6fSThomas Graf 		struct net_device *dev;
284872331bc0SShmulik Ladkani 		int flags = 0;
284972331bc0SShmulik Ladkani 
28505578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
2851ab364a6fSThomas Graf 		if (!dev) {
2852ab364a6fSThomas Graf 			err = -ENODEV;
2853ab364a6fSThomas Graf 			goto errout;
2854ab364a6fSThomas Graf 		}
285572331bc0SShmulik Ladkani 
285672331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
285772331bc0SShmulik Ladkani 
285872331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
285972331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
286072331bc0SShmulik Ladkani 
286172331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
286272331bc0SShmulik Ladkani 							       flags);
286372331bc0SShmulik Ladkani 	} else {
286472331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
286572331bc0SShmulik Ladkani 
286672331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
2867ab364a6fSThomas Graf 	}
28681da177e4SLinus Torvalds 
28691da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
287038308473SDavid S. Miller 	if (!skb) {
287194e187c0SAmerigo Wang 		ip6_rt_put(rt);
2872ab364a6fSThomas Graf 		err = -ENOBUFS;
2873ab364a6fSThomas Graf 		goto errout;
2874ab364a6fSThomas Graf 	}
28751da177e4SLinus Torvalds 
28761da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
28771da177e4SLinus Torvalds 	   through good chunk of routing engine.
28781da177e4SLinus Torvalds 	 */
2879459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
28801da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
28811da177e4SLinus Torvalds 
2882d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
28831da177e4SLinus Torvalds 
28844c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
288515e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
28867bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
28871da177e4SLinus Torvalds 	if (err < 0) {
2888ab364a6fSThomas Graf 		kfree_skb(skb);
2889ab364a6fSThomas Graf 		goto errout;
28901da177e4SLinus Torvalds 	}
28911da177e4SLinus Torvalds 
289215e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
2893ab364a6fSThomas Graf errout:
28941da177e4SLinus Torvalds 	return err;
28951da177e4SLinus Torvalds }
28961da177e4SLinus Torvalds 
289786872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
28981da177e4SLinus Torvalds {
28991da177e4SLinus Torvalds 	struct sk_buff *skb;
29005578689aSDaniel Lezcano 	struct net *net = info->nl_net;
2901528c4cebSDenis V. Lunev 	u32 seq;
2902528c4cebSDenis V. Lunev 	int err;
29030d51aa80SJamal Hadi Salim 
2904528c4cebSDenis V. Lunev 	err = -ENOBUFS;
290538308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
290686872cb5SThomas Graf 
2907339bf98fSThomas Graf 	skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
290838308473SDavid S. Miller 	if (!skb)
290921713ebcSThomas Graf 		goto errout;
29101da177e4SLinus Torvalds 
2911191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
291215e47304SEric W. Biederman 				event, info->portid, seq, 0, 0, 0);
291326932566SPatrick McHardy 	if (err < 0) {
291426932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
291526932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
291626932566SPatrick McHardy 		kfree_skb(skb);
291726932566SPatrick McHardy 		goto errout;
291826932566SPatrick McHardy 	}
291915e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
29205578689aSDaniel Lezcano 		    info->nlh, gfp_any());
29211ce85fe4SPablo Neira Ayuso 	return;
292221713ebcSThomas Graf errout:
292321713ebcSThomas Graf 	if (err < 0)
29245578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
29251da177e4SLinus Torvalds }
29261da177e4SLinus Torvalds 
29278ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
2928351638e7SJiri Pirko 				unsigned long event, void *ptr)
29298ed67789SDaniel Lezcano {
2930351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
2931c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
29328ed67789SDaniel Lezcano 
29338ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
2934d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
29358ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
29368ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2937d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
29388ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
2939d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
29408ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
29418ed67789SDaniel Lezcano #endif
29428ed67789SDaniel Lezcano 	}
29438ed67789SDaniel Lezcano 
29448ed67789SDaniel Lezcano 	return NOTIFY_OK;
29458ed67789SDaniel Lezcano }
29468ed67789SDaniel Lezcano 
29471da177e4SLinus Torvalds /*
29481da177e4SLinus Torvalds  *	/proc
29491da177e4SLinus Torvalds  */
29501da177e4SLinus Torvalds 
29511da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
29521da177e4SLinus Torvalds 
295333120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
295433120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
295533120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
295633120b30SAlexey Dobriyan 	.read		= seq_read,
295733120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
29588d2ca1d7SHannes Frederic Sowa 	.release	= seq_release_net,
295933120b30SAlexey Dobriyan };
296033120b30SAlexey Dobriyan 
29611da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
29621da177e4SLinus Torvalds {
296369ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
29641da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
296569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
296669ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
296769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
296869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
296969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
2970fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
297169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
29721da177e4SLinus Torvalds 
29731da177e4SLinus Torvalds 	return 0;
29741da177e4SLinus Torvalds }
29751da177e4SLinus Torvalds 
29761da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
29771da177e4SLinus Torvalds {
2978de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
297969ddb805SDaniel Lezcano }
298069ddb805SDaniel Lezcano 
29819a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
29821da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
29831da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
29841da177e4SLinus Torvalds 	.read	 = seq_read,
29851da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
2986b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
29871da177e4SLinus Torvalds };
29881da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
29891da177e4SLinus Torvalds 
29901da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
29911da177e4SLinus Torvalds 
29921da177e4SLinus Torvalds static
2993fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
29941da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
29951da177e4SLinus Torvalds {
2996c486da34SLucian Adrian Grijincu 	struct net *net;
2997c486da34SLucian Adrian Grijincu 	int delay;
2998c486da34SLucian Adrian Grijincu 	if (!write)
2999c486da34SLucian Adrian Grijincu 		return -EINVAL;
3000c486da34SLucian Adrian Grijincu 
3001c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
3002c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
30038d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
30042ac3ac8fSMichal Kubeček 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
30051da177e4SLinus Torvalds 	return 0;
30061da177e4SLinus Torvalds }
30071da177e4SLinus Torvalds 
3008fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = {
30091da177e4SLinus Torvalds 	{
30101da177e4SLinus Torvalds 		.procname	=	"flush",
30114990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
30121da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
301389c8b3a1SDave Jones 		.mode		=	0200,
30146d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
30151da177e4SLinus Torvalds 	},
30161da177e4SLinus Torvalds 	{
30171da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
30189a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
30191da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30201da177e4SLinus Torvalds 		.mode		=	0644,
30216d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
30221da177e4SLinus Torvalds 	},
30231da177e4SLinus Torvalds 	{
30241da177e4SLinus Torvalds 		.procname	=	"max_size",
30254990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
30261da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30271da177e4SLinus Torvalds 		.mode		=	0644,
30286d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
30291da177e4SLinus Torvalds 	},
30301da177e4SLinus Torvalds 	{
30311da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
30324990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
30331da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30341da177e4SLinus Torvalds 		.mode		=	0644,
30356d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30361da177e4SLinus Torvalds 	},
30371da177e4SLinus Torvalds 	{
30381da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
30394990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
30401da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30411da177e4SLinus Torvalds 		.mode		=	0644,
30426d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30431da177e4SLinus Torvalds 	},
30441da177e4SLinus Torvalds 	{
30451da177e4SLinus Torvalds 		.procname	=	"gc_interval",
30464990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
30471da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30481da177e4SLinus Torvalds 		.mode		=	0644,
30496d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30501da177e4SLinus Torvalds 	},
30511da177e4SLinus Torvalds 	{
30521da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
30534990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
30541da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30551da177e4SLinus Torvalds 		.mode		=	0644,
3056f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
30571da177e4SLinus Torvalds 	},
30581da177e4SLinus Torvalds 	{
30591da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
30604990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
30611da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30621da177e4SLinus Torvalds 		.mode		=	0644,
30636d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
30641da177e4SLinus Torvalds 	},
30651da177e4SLinus Torvalds 	{
30661da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
30674990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
30681da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30691da177e4SLinus Torvalds 		.mode		=	0644,
3070f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
30711da177e4SLinus Torvalds 	},
30721da177e4SLinus Torvalds 	{
30731da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
30744990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
30751da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
30761da177e4SLinus Torvalds 		.mode		=	0644,
30776d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
30781da177e4SLinus Torvalds 	},
3079f8572d8fSEric W. Biederman 	{ }
30801da177e4SLinus Torvalds };
30811da177e4SLinus Torvalds 
30822c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
3083760f2d01SDaniel Lezcano {
3084760f2d01SDaniel Lezcano 	struct ctl_table *table;
3085760f2d01SDaniel Lezcano 
3086760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
3087760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
3088760f2d01SDaniel Lezcano 			GFP_KERNEL);
30895ee09105SYOSHIFUJI Hideaki 
30905ee09105SYOSHIFUJI Hideaki 	if (table) {
30915ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
3092c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
309386393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
30945ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
30955ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
30965ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
30975ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
30985ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
30995ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
31005ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
31019c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
3102464dc801SEric W. Biederman 
3103464dc801SEric W. Biederman 		/* Don't export sysctls to unprivileged users */
3104464dc801SEric W. Biederman 		if (net->user_ns != &init_user_ns)
3105464dc801SEric W. Biederman 			table[0].procname = NULL;
31065ee09105SYOSHIFUJI Hideaki 	}
31075ee09105SYOSHIFUJI Hideaki 
3108760f2d01SDaniel Lezcano 	return table;
3109760f2d01SDaniel Lezcano }
31101da177e4SLinus Torvalds #endif
31111da177e4SLinus Torvalds 
31122c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
3113cdb18761SDaniel Lezcano {
3114633d424bSPavel Emelyanov 	int ret = -ENOMEM;
31158ed67789SDaniel Lezcano 
311686393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
311786393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
3118f2fc6a54SBenjamin Thery 
3119fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
3120fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
3121fc66f95cSEric Dumazet 
31228ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
31238ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
31248ed67789SDaniel Lezcano 					   GFP_KERNEL);
31258ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
3126fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
3127d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
31288ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
3129d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
313062fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
313162fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
31328ed67789SDaniel Lezcano 
31338ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
31348ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
31358ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
31368ed67789SDaniel Lezcano 					       GFP_KERNEL);
313768fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
313868fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
3139d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
31408ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
3141d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
314262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
314362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
31448ed67789SDaniel Lezcano 
31458ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
31468ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
31478ed67789SDaniel Lezcano 					       GFP_KERNEL);
314868fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
314968fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
3150d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
31518ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
3152d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
315362fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
315462fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
31558ed67789SDaniel Lezcano #endif
31568ed67789SDaniel Lezcano 
3157b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
3158b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
3159b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3160b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3161b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3162b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3163b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3164b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3165b339a47cSPeter Zijlstra 
31666891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
31676891a346SBenjamin Thery 
31688ed67789SDaniel Lezcano 	ret = 0;
31698ed67789SDaniel Lezcano out:
31708ed67789SDaniel Lezcano 	return ret;
3171f2fc6a54SBenjamin Thery 
317268fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
317368fffc67SPeter Zijlstra out_ip6_prohibit_entry:
317468fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
317568fffc67SPeter Zijlstra out_ip6_null_entry:
317668fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
317768fffc67SPeter Zijlstra #endif
3178fc66f95cSEric Dumazet out_ip6_dst_entries:
3179fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3180f2fc6a54SBenjamin Thery out_ip6_dst_ops:
3181f2fc6a54SBenjamin Thery 	goto out;
3182cdb18761SDaniel Lezcano }
3183cdb18761SDaniel Lezcano 
31842c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
3185cdb18761SDaniel Lezcano {
31868ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
31878ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
31888ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
31898ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
31908ed67789SDaniel Lezcano #endif
319141bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
3192cdb18761SDaniel Lezcano }
3193cdb18761SDaniel Lezcano 
3194d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
3195d189634eSThomas Graf {
3196d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3197d4beaa66SGao feng 	proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops);
3198d4beaa66SGao feng 	proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops);
3199d189634eSThomas Graf #endif
3200d189634eSThomas Graf 	return 0;
3201d189634eSThomas Graf }
3202d189634eSThomas Graf 
3203d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
3204d189634eSThomas Graf {
3205d189634eSThomas Graf #ifdef CONFIG_PROC_FS
3206ece31ffdSGao feng 	remove_proc_entry("ipv6_route", net->proc_net);
3207ece31ffdSGao feng 	remove_proc_entry("rt6_stats", net->proc_net);
3208d189634eSThomas Graf #endif
3209d189634eSThomas Graf }
3210d189634eSThomas Graf 
3211cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
3212cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
3213cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
3214cdb18761SDaniel Lezcano };
3215cdb18761SDaniel Lezcano 
3216c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
3217c3426b47SDavid S. Miller {
3218c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3219c3426b47SDavid S. Miller 
3220c3426b47SDavid S. Miller 	if (!bp)
3221c3426b47SDavid S. Miller 		return -ENOMEM;
3222c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
3223c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
3224c3426b47SDavid S. Miller 	return 0;
3225c3426b47SDavid S. Miller }
3226c3426b47SDavid S. Miller 
3227c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
3228c3426b47SDavid S. Miller {
3229c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
3230c3426b47SDavid S. Miller 
3231c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
323256a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3233c3426b47SDavid S. Miller 	kfree(bp);
3234c3426b47SDavid S. Miller }
3235c3426b47SDavid S. Miller 
32362b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3237c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3238c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3239c3426b47SDavid S. Miller };
3240c3426b47SDavid S. Miller 
3241d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3242d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3243d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3244d189634eSThomas Graf };
3245d189634eSThomas Graf 
32468ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
32478ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
32488ed67789SDaniel Lezcano 	.priority = 0,
32498ed67789SDaniel Lezcano };
32508ed67789SDaniel Lezcano 
3251433d49c3SDaniel Lezcano int __init ip6_route_init(void)
32521da177e4SLinus Torvalds {
3253433d49c3SDaniel Lezcano 	int ret;
3254433d49c3SDaniel Lezcano 
32559a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
32569a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
32579a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
32589a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
32599a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3260c19a28e1SFernando Carrijo 		goto out;
326114e50e57SDavid S. Miller 
3262fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
32638ed67789SDaniel Lezcano 	if (ret)
3264bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3265bdb3289fSDaniel Lezcano 
3266c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3267c3426b47SDavid S. Miller 	if (ret)
3268e8803b6cSDavid S. Miller 		goto out_dst_entries;
32692a0c451aSThomas Graf 
32707e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
32717e52b33bSDavid S. Miller 	if (ret)
32727e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3273c3426b47SDavid S. Miller 
32745dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
32755dc121e9SArnaud Ebalard 
32768ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
32778ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
32788ed67789SDaniel Lezcano 	 * manually for init_net */
3279d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
32808ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3281bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3282d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
32838ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3284d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
32858ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3286bdb3289fSDaniel Lezcano   #endif
3287e8803b6cSDavid S. Miller 	ret = fib6_init();
3288433d49c3SDaniel Lezcano 	if (ret)
32898ed67789SDaniel Lezcano 		goto out_register_subsys;
3290433d49c3SDaniel Lezcano 
3291433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3292433d49c3SDaniel Lezcano 	if (ret)
3293e8803b6cSDavid S. Miller 		goto out_fib6_init;
3294c35b7e72SDaniel Lezcano 
3295433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3296433d49c3SDaniel Lezcano 	if (ret)
3297433d49c3SDaniel Lezcano 		goto xfrm6_init;
32987e5449c2SDaniel Lezcano 
3299d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3300d189634eSThomas Graf 	if (ret)
3301d189634eSThomas Graf 		goto fib6_rules_init;
3302d189634eSThomas Graf 
3303433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3304c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3305c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3306c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3307d189634eSThomas Graf 		goto out_register_late_subsys;
3308433d49c3SDaniel Lezcano 
33098ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3310cdb18761SDaniel Lezcano 	if (ret)
3311d189634eSThomas Graf 		goto out_register_late_subsys;
33128ed67789SDaniel Lezcano 
3313433d49c3SDaniel Lezcano out:
3314433d49c3SDaniel Lezcano 	return ret;
3315433d49c3SDaniel Lezcano 
3316d189634eSThomas Graf out_register_late_subsys:
3317d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3318433d49c3SDaniel Lezcano fib6_rules_init:
3319433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3320433d49c3SDaniel Lezcano xfrm6_init:
3321433d49c3SDaniel Lezcano 	xfrm6_fini();
33222a0c451aSThomas Graf out_fib6_init:
33232a0c451aSThomas Graf 	fib6_gc_cleanup();
33248ed67789SDaniel Lezcano out_register_subsys:
33258ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
33267e52b33bSDavid S. Miller out_register_inetpeer:
33277e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3328fc66f95cSEric Dumazet out_dst_entries:
3329fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3330433d49c3SDaniel Lezcano out_kmem_cache:
3331f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3332433d49c3SDaniel Lezcano 	goto out;
33331da177e4SLinus Torvalds }
33341da177e4SLinus Torvalds 
33351da177e4SLinus Torvalds void ip6_route_cleanup(void)
33361da177e4SLinus Torvalds {
33378ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3338d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3339101367c2SThomas Graf 	fib6_rules_cleanup();
33401da177e4SLinus Torvalds 	xfrm6_fini();
33411da177e4SLinus Torvalds 	fib6_gc_cleanup();
3342c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
33438ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
334441bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3345f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
33461da177e4SLinus Torvalds }
3347