xref: /openbmc/linux/net/ipv6/route.c (revision 6f3118b571b8a4c06c7985dc3172c3526cb86253)
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>
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #include <asm/uaccess.h>
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
641da177e4SLinus Torvalds #include <linux/sysctl.h>
651da177e4SLinus Torvalds #endif
661da177e4SLinus Torvalds 
671716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
6821efcfa0SEric Dumazet 				    const struct in6_addr *dest);
691da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
700dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
71ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
721da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
731da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
741da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
751da177e4SLinus Torvalds 				       struct net_device *dev, int how);
76569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
791da177e4SLinus Torvalds static int		ip6_pkt_discard_out(struct sk_buff *skb);
801da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
816700c270SDavid S. Miller static void		ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
826700c270SDavid S. Miller 					   struct sk_buff *skb, u32 mtu);
836700c270SDavid S. Miller static void		rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
846700c270SDavid S. Miller 					struct sk_buff *skb);
851da177e4SLinus Torvalds 
8670ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
87efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
88b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
89b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
9095c96174SEric Dumazet 					   unsigned int pref);
91efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
92b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
93b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
9470ceb4f5SYOSHIFUJI Hideaki #endif
9570ceb4f5SYOSHIFUJI Hideaki 
9606582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
9706582540SDavid S. Miller {
9806582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
9906582540SDavid S. Miller 	struct inet_peer *peer;
10006582540SDavid S. Miller 	u32 *p = NULL;
10106582540SDavid S. Miller 
1028e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST))
1038e2ec639SYan, Zheng 		return NULL;
1048e2ec639SYan, Zheng 
105fbfe95a4SDavid S. Miller 	peer = rt6_get_peer_create(rt);
10606582540SDavid S. Miller 	if (peer) {
10706582540SDavid S. Miller 		u32 *old_p = __DST_METRICS_PTR(old);
10806582540SDavid S. Miller 		unsigned long prev, new;
10906582540SDavid S. Miller 
11006582540SDavid S. Miller 		p = peer->metrics;
11106582540SDavid S. Miller 		if (inet_metrics_new(peer))
11206582540SDavid S. Miller 			memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
11306582540SDavid S. Miller 
11406582540SDavid S. Miller 		new = (unsigned long) p;
11506582540SDavid S. Miller 		prev = cmpxchg(&dst->_metrics, old, new);
11606582540SDavid S. Miller 
11706582540SDavid S. Miller 		if (prev != old) {
11806582540SDavid S. Miller 			p = __DST_METRICS_PTR(prev);
11906582540SDavid S. Miller 			if (prev & DST_METRICS_READ_ONLY)
12006582540SDavid S. Miller 				p = NULL;
12106582540SDavid S. Miller 		}
12206582540SDavid S. Miller 	}
12306582540SDavid S. Miller 	return p;
12406582540SDavid S. Miller }
12506582540SDavid S. Miller 
126f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt,
127f894cbf8SDavid S. Miller 					     struct sk_buff *skb,
128f894cbf8SDavid S. Miller 					     const void *daddr)
12939232973SDavid S. Miller {
13039232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
13139232973SDavid S. Miller 
132a7563f34SDavid S. Miller 	if (!ipv6_addr_any(p))
13339232973SDavid S. Miller 		return (const void *) p;
134f894cbf8SDavid S. Miller 	else if (skb)
135f894cbf8SDavid S. Miller 		return &ipv6_hdr(skb)->daddr;
13639232973SDavid S. Miller 	return daddr;
13739232973SDavid S. Miller }
13839232973SDavid S. Miller 
139f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
140f894cbf8SDavid S. Miller 					  struct sk_buff *skb,
141f894cbf8SDavid S. Miller 					  const void *daddr)
142d3aaeb38SDavid S. Miller {
14339232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
14439232973SDavid S. Miller 	struct neighbour *n;
14539232973SDavid S. Miller 
146f894cbf8SDavid S. Miller 	daddr = choose_neigh_daddr(rt, skb, daddr);
14739232973SDavid S. Miller 	n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
148f83c7790SDavid S. Miller 	if (n)
149f83c7790SDavid S. Miller 		return n;
150f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
151f83c7790SDavid S. Miller }
152f83c7790SDavid S. Miller 
1538ade06c6SDavid S. Miller static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
154f83c7790SDavid S. Miller {
1558ade06c6SDavid S. Miller 	struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
1568ade06c6SDavid S. Miller 	if (!n) {
1578ade06c6SDavid S. Miller 		n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
158f83c7790SDavid S. Miller 		if (IS_ERR(n))
159f83c7790SDavid S. Miller 			return PTR_ERR(n);
1608ade06c6SDavid S. Miller 	}
16197cac082SDavid S. Miller 	rt->n = n;
162f83c7790SDavid S. Miller 
163f83c7790SDavid S. Miller 	return 0;
164d3aaeb38SDavid S. Miller }
165d3aaeb38SDavid S. Miller 
1669a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
1671da177e4SLinus Torvalds 	.family			=	AF_INET6,
16809640e63SHarvey Harrison 	.protocol		=	cpu_to_be16(ETH_P_IPV6),
1691da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
1701da177e4SLinus Torvalds 	.gc_thresh		=	1024,
1711da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
1720dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
173ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
17406582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
1751da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
1761da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
1771da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
1781da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
1791da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
1806e157b6aSDavid S. Miller 	.redirect		=	rt6_do_redirect,
1811ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
182d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
1831da177e4SLinus Torvalds };
1841da177e4SLinus Torvalds 
185ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
186ec831ea7SRoland Dreier {
187618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
188618f9bc7SSteffen Klassert 
189618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
190ec831ea7SRoland Dreier }
191ec831ea7SRoland Dreier 
1926700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
1936700c270SDavid S. Miller 					 struct sk_buff *skb, u32 mtu)
19414e50e57SDavid S. Miller {
19514e50e57SDavid S. Miller }
19614e50e57SDavid S. Miller 
1976700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
1986700c270SDavid S. Miller 				      struct sk_buff *skb)
199b587ee3bSDavid S. Miller {
200b587ee3bSDavid S. Miller }
201b587ee3bSDavid S. Miller 
2020972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
2030972ddb2SHeld Bernhard 					 unsigned long old)
2040972ddb2SHeld Bernhard {
2050972ddb2SHeld Bernhard 	return NULL;
2060972ddb2SHeld Bernhard }
2070972ddb2SHeld Bernhard 
20814e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
20914e50e57SDavid S. Miller 	.family			=	AF_INET6,
21009640e63SHarvey Harrison 	.protocol		=	cpu_to_be16(ETH_P_IPV6),
21114e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
21214e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
213ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
214214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
21514e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
216b587ee3bSDavid S. Miller 	.redirect		=	ip6_rt_blackhole_redirect,
2170972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
218d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
21914e50e57SDavid S. Miller };
22014e50e57SDavid S. Miller 
22162fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
22262fa8a84SDavid S. Miller 	[RTAX_HOPLIMIT - 1] = 255,
22362fa8a84SDavid S. Miller };
22462fa8a84SDavid S. Miller 
225bdb3289fSDaniel Lezcano static struct rt6_info ip6_null_entry_template = {
2261da177e4SLinus Torvalds 	.dst = {
2271da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2281da177e4SLinus Torvalds 		.__use		= 1,
2291da177e4SLinus Torvalds 		.obsolete	= -1,
2301da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2311da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2321da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2331da177e4SLinus Torvalds 	},
2341da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2354f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2361da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2371da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2381da177e4SLinus Torvalds };
2391da177e4SLinus Torvalds 
240101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
241101367c2SThomas Graf 
2426723ab54SDavid S. Miller static int ip6_pkt_prohibit(struct sk_buff *skb);
2436723ab54SDavid S. Miller static int ip6_pkt_prohibit_out(struct sk_buff *skb);
2446723ab54SDavid S. Miller 
245280a34c8SAdrian Bunk static struct rt6_info ip6_prohibit_entry_template = {
246101367c2SThomas Graf 	.dst = {
247101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
248101367c2SThomas Graf 		.__use		= 1,
249101367c2SThomas Graf 		.obsolete	= -1,
250101367c2SThomas Graf 		.error		= -EACCES,
2519ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2529ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
253101367c2SThomas Graf 	},
254101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2554f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
256101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
257101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
258101367c2SThomas Graf };
259101367c2SThomas Graf 
260bdb3289fSDaniel Lezcano static struct rt6_info ip6_blk_hole_entry_template = {
261101367c2SThomas Graf 	.dst = {
262101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
263101367c2SThomas Graf 		.__use		= 1,
264101367c2SThomas Graf 		.obsolete	= -1,
265101367c2SThomas Graf 		.error		= -EINVAL,
266352e512cSHerbert Xu 		.input		= dst_discard,
267352e512cSHerbert Xu 		.output		= dst_discard,
268101367c2SThomas Graf 	},
269101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2704f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
271101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
272101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
273101367c2SThomas Graf };
274101367c2SThomas Graf 
275101367c2SThomas Graf #endif
276101367c2SThomas Graf 
2771da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
27897bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net,
279957c665fSDavid S. Miller 					     struct net_device *dev,
2808b96d22dSDavid S. Miller 					     int flags,
2818b96d22dSDavid S. Miller 					     struct fib6_table *table)
2821da177e4SLinus Torvalds {
28397bab73fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
284*6f3118b5SNicolas Dichtel 					0, DST_OBSOLETE_FORCE_CHK, flags);
285cf911662SDavid S. Miller 
28697bab73fSDavid S. Miller 	if (rt) {
2878104891bSSteffen Klassert 		struct dst_entry *dst = &rt->dst;
2888104891bSSteffen Klassert 
2898104891bSSteffen Klassert 		memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
2908b96d22dSDavid S. Miller 		rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
291*6f3118b5SNicolas Dichtel 		rt->rt6i_genid = rt_genid(net);
29297bab73fSDavid S. Miller 	}
293cf911662SDavid S. Miller 	return rt;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
2991da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3001da177e4SLinus Torvalds 
30197cac082SDavid S. Miller 	if (rt->n)
30297cac082SDavid S. Miller 		neigh_release(rt->n);
30397cac082SDavid S. Miller 
3048e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST))
3058e2ec639SYan, Zheng 		dst_destroy_metrics_generic(dst);
3068e2ec639SYan, Zheng 
30738308473SDavid S. Miller 	if (idev) {
3081da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3091da177e4SLinus Torvalds 		in6_dev_put(idev);
3101da177e4SLinus Torvalds 	}
3111716a961SGao feng 
3121716a961SGao feng 	if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
3131716a961SGao feng 		dst_release(dst->from);
3141716a961SGao feng 
31597bab73fSDavid S. Miller 	if (rt6_has_peer(rt)) {
31697bab73fSDavid S. Miller 		struct inet_peer *peer = rt6_peer_ptr(rt);
317b3419363SDavid S. Miller 		inet_putpeer(peer);
318b3419363SDavid S. Miller 	}
319b3419363SDavid S. Miller }
320b3419363SDavid S. Miller 
3216431cbc2SDavid S. Miller static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
3226431cbc2SDavid S. Miller 
3236431cbc2SDavid S. Miller static u32 rt6_peer_genid(void)
3246431cbc2SDavid S. Miller {
3256431cbc2SDavid S. Miller 	return atomic_read(&__rt6_peer_genid);
3266431cbc2SDavid S. Miller }
3276431cbc2SDavid S. Miller 
328b3419363SDavid S. Miller void rt6_bind_peer(struct rt6_info *rt, int create)
329b3419363SDavid S. Miller {
33097bab73fSDavid S. Miller 	struct inet_peer_base *base;
331b3419363SDavid S. Miller 	struct inet_peer *peer;
332b3419363SDavid S. Miller 
33397bab73fSDavid S. Miller 	base = inetpeer_base_ptr(rt->_rt6i_peer);
33497bab73fSDavid S. Miller 	if (!base)
33597bab73fSDavid S. Miller 		return;
33697bab73fSDavid S. Miller 
33797bab73fSDavid S. Miller 	peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
3387b34ca2aSDavid S. Miller 	if (peer) {
33997bab73fSDavid S. Miller 		if (!rt6_set_peer(rt, peer))
340b3419363SDavid S. Miller 			inet_putpeer(peer);
3416431cbc2SDavid S. Miller 		else
3426431cbc2SDavid S. Miller 			rt->rt6i_peer_genid = rt6_peer_genid();
3431da177e4SLinus Torvalds 	}
3447b34ca2aSDavid S. Miller }
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3471da177e4SLinus Torvalds 			   int how)
3481da177e4SLinus Torvalds {
3491da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3501da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3515a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
352c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3531da177e4SLinus Torvalds 
35497cac082SDavid S. Miller 	if (dev != loopback_dev) {
35597cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3565a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3575a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
35838308473SDavid S. Miller 			if (loopback_idev) {
3591da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
3601da177e4SLinus Torvalds 				in6_dev_put(idev);
3611da177e4SLinus Torvalds 			}
3621da177e4SLinus Torvalds 		}
36397cac082SDavid S. Miller 		if (rt->n && rt->n->dev == dev) {
36497cac082SDavid S. Miller 			rt->n->dev = loopback_dev;
36597cac082SDavid S. Miller 			dev_hold(loopback_dev);
36697cac082SDavid S. Miller 			dev_put(dev);
36797cac082SDavid S. Miller 		}
36897cac082SDavid S. Miller 	}
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds 
371a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
3721da177e4SLinus Torvalds {
3731716a961SGao feng 	struct rt6_info *ort = NULL;
3741716a961SGao feng 
3751716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
3761716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
377a50feda5SEric Dumazet 			return true;
3781716a961SGao feng 	} else if (rt->dst.from) {
3791716a961SGao feng 		ort = (struct rt6_info *) rt->dst.from;
3801716a961SGao feng 		return (ort->rt6i_flags & RTF_EXPIRES) &&
3811716a961SGao feng 			time_after(jiffies, ort->dst.expires);
3821716a961SGao feng 	}
383a50feda5SEric Dumazet 	return false;
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds 
386a50feda5SEric Dumazet static bool rt6_need_strict(const struct in6_addr *daddr)
387c71099acSThomas Graf {
388a02cec21SEric Dumazet 	return ipv6_addr_type(daddr) &
389a02cec21SEric Dumazet 		(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
390c71099acSThomas Graf }
391c71099acSThomas Graf 
3921da177e4SLinus Torvalds /*
393c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
3941da177e4SLinus Torvalds  */
3951da177e4SLinus Torvalds 
3968ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
3978ed67789SDaniel Lezcano 						    struct rt6_info *rt,
398b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
3991da177e4SLinus Torvalds 						    int oif,
400d420895eSYOSHIFUJI Hideaki 						    int flags)
4011da177e4SLinus Torvalds {
4021da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4031da177e4SLinus Torvalds 	struct rt6_info *sprt;
4041da177e4SLinus Torvalds 
405dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
406dd3abc4eSYOSHIFUJI Hideaki 		goto out;
407dd3abc4eSYOSHIFUJI Hideaki 
408d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
409d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
410dd3abc4eSYOSHIFUJI Hideaki 
411dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4121da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4131da177e4SLinus Torvalds 				return sprt;
4141da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
41538308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4161da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
417d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
4181da177e4SLinus Torvalds 						continue;
4191da177e4SLinus Torvalds 					if (local && (!oif ||
4201da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
4211da177e4SLinus Torvalds 						continue;
4221da177e4SLinus Torvalds 				}
4231da177e4SLinus Torvalds 				local = sprt;
4241da177e4SLinus Torvalds 			}
425dd3abc4eSYOSHIFUJI Hideaki 		} else {
426dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
427dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
428dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
429dd3abc4eSYOSHIFUJI Hideaki 		}
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 
432dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
4331da177e4SLinus Torvalds 		if (local)
4341da177e4SLinus Torvalds 			return local;
4351da177e4SLinus Torvalds 
436d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
4378ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
4381da177e4SLinus Torvalds 	}
439dd3abc4eSYOSHIFUJI Hideaki out:
4401da177e4SLinus Torvalds 	return rt;
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds 
44327097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
44427097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
44527097255SYOSHIFUJI Hideaki {
446f2c31e32SEric Dumazet 	struct neighbour *neigh;
44727097255SYOSHIFUJI Hideaki 	/*
44827097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
44927097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
45027097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
45127097255SYOSHIFUJI Hideaki 	 *
45227097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
45327097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
45427097255SYOSHIFUJI Hideaki 	 */
455f2c31e32SEric Dumazet 	rcu_read_lock();
45697cac082SDavid S. Miller 	neigh = rt ? rt->n : NULL;
45727097255SYOSHIFUJI Hideaki 	if (!neigh || (neigh->nud_state & NUD_VALID))
458f2c31e32SEric Dumazet 		goto out;
45927097255SYOSHIFUJI Hideaki 	read_lock_bh(&neigh->lock);
46027097255SYOSHIFUJI Hideaki 	if (!(neigh->nud_state & NUD_VALID) &&
46152e16356SYOSHIFUJI Hideaki 	    time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
46227097255SYOSHIFUJI Hideaki 		struct in6_addr mcaddr;
46327097255SYOSHIFUJI Hideaki 		struct in6_addr *target;
46427097255SYOSHIFUJI Hideaki 
46527097255SYOSHIFUJI Hideaki 		neigh->updated = jiffies;
46627097255SYOSHIFUJI Hideaki 		read_unlock_bh(&neigh->lock);
46727097255SYOSHIFUJI Hideaki 
46827097255SYOSHIFUJI Hideaki 		target = (struct in6_addr *)&neigh->primary_key;
46927097255SYOSHIFUJI Hideaki 		addrconf_addr_solict_mult(target, &mcaddr);
470d1918542SDavid S. Miller 		ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
471f2c31e32SEric Dumazet 	} else {
47227097255SYOSHIFUJI Hideaki 		read_unlock_bh(&neigh->lock);
47327097255SYOSHIFUJI Hideaki 	}
474f2c31e32SEric Dumazet out:
475f2c31e32SEric Dumazet 	rcu_read_unlock();
476f2c31e32SEric Dumazet }
47727097255SYOSHIFUJI Hideaki #else
47827097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
47927097255SYOSHIFUJI Hideaki {
48027097255SYOSHIFUJI Hideaki }
48127097255SYOSHIFUJI Hideaki #endif
48227097255SYOSHIFUJI Hideaki 
4831da177e4SLinus Torvalds /*
484554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
4851da177e4SLinus Torvalds  */
486b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
4871da177e4SLinus Torvalds {
488d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
489161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
490554cfb7eSYOSHIFUJI Hideaki 		return 2;
491161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
492161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
493161980f4SDavid S. Miller 		return 1;
494554cfb7eSYOSHIFUJI Hideaki 	return 0;
4951da177e4SLinus Torvalds }
4961da177e4SLinus Torvalds 
497b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt)
4981da177e4SLinus Torvalds {
499f2c31e32SEric Dumazet 	struct neighbour *neigh;
500398bcbebSYOSHIFUJI Hideaki 	int m;
501f2c31e32SEric Dumazet 
502f2c31e32SEric Dumazet 	rcu_read_lock();
50397cac082SDavid S. Miller 	neigh = rt->n;
5044d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
5054d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
5064d0c5911SYOSHIFUJI Hideaki 		m = 1;
5074d0c5911SYOSHIFUJI Hideaki 	else if (neigh) {
5081da177e4SLinus Torvalds 		read_lock_bh(&neigh->lock);
509554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
5104d0c5911SYOSHIFUJI Hideaki 			m = 2;
511398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
512398bcbebSYOSHIFUJI Hideaki 		else if (neigh->nud_state & NUD_FAILED)
513398bcbebSYOSHIFUJI Hideaki 			m = 0;
514398bcbebSYOSHIFUJI Hideaki #endif
515398bcbebSYOSHIFUJI Hideaki 		else
516ea73ee23SYOSHIFUJI Hideaki 			m = 1;
5171da177e4SLinus Torvalds 		read_unlock_bh(&neigh->lock);
518398bcbebSYOSHIFUJI Hideaki 	} else
519398bcbebSYOSHIFUJI Hideaki 		m = 0;
520f2c31e32SEric Dumazet 	rcu_read_unlock();
521554cfb7eSYOSHIFUJI Hideaki 	return m;
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds 
524554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
525554cfb7eSYOSHIFUJI Hideaki 			   int strict)
526554cfb7eSYOSHIFUJI Hideaki {
5274d0c5911SYOSHIFUJI Hideaki 	int m, n;
5284d0c5911SYOSHIFUJI Hideaki 
5294d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
53077d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
531554cfb7eSYOSHIFUJI Hideaki 		return -1;
532ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
533ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
534ebacaaa0SYOSHIFUJI Hideaki #endif
5354d0c5911SYOSHIFUJI Hideaki 	n = rt6_check_neigh(rt);
536557e92efSYOSHIFUJI Hideaki 	if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
537554cfb7eSYOSHIFUJI Hideaki 		return -1;
538554cfb7eSYOSHIFUJI Hideaki 	return m;
539554cfb7eSYOSHIFUJI Hideaki }
540554cfb7eSYOSHIFUJI Hideaki 
541f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
542f11e6659SDavid S. Miller 				   int *mpri, struct rt6_info *match)
543554cfb7eSYOSHIFUJI Hideaki {
544554cfb7eSYOSHIFUJI Hideaki 	int m;
545554cfb7eSYOSHIFUJI Hideaki 
546554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
547f11e6659SDavid S. Miller 		goto out;
548554cfb7eSYOSHIFUJI Hideaki 
549554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
550554cfb7eSYOSHIFUJI Hideaki 	if (m < 0)
551f11e6659SDavid S. Miller 		goto out;
552554cfb7eSYOSHIFUJI Hideaki 
553f11e6659SDavid S. Miller 	if (m > *mpri) {
554ea659e07SYOSHIFUJI Hideaki 		if (strict & RT6_LOOKUP_F_REACHABLE)
55527097255SYOSHIFUJI Hideaki 			rt6_probe(match);
556f11e6659SDavid S. Miller 		*mpri = m;
557554cfb7eSYOSHIFUJI Hideaki 		match = rt;
558ea659e07SYOSHIFUJI Hideaki 	} else if (strict & RT6_LOOKUP_F_REACHABLE) {
55927097255SYOSHIFUJI Hideaki 		rt6_probe(rt);
5601da177e4SLinus Torvalds 	}
561f11e6659SDavid S. Miller 
562f11e6659SDavid S. Miller out:
563f11e6659SDavid S. Miller 	return match;
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds 
566f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
567f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
568f11e6659SDavid S. Miller 				     u32 metric, int oif, int strict)
569f11e6659SDavid S. Miller {
570f11e6659SDavid S. Miller 	struct rt6_info *rt, *match;
571f11e6659SDavid S. Miller 	int mpri = -1;
572f11e6659SDavid S. Miller 
573f11e6659SDavid S. Miller 	match = NULL;
574f11e6659SDavid S. Miller 	for (rt = rr_head; rt && rt->rt6i_metric == metric;
575d8d1f30bSChangli Gao 	     rt = rt->dst.rt6_next)
576f11e6659SDavid S. Miller 		match = find_match(rt, oif, strict, &mpri, match);
577f11e6659SDavid S. Miller 	for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
578d8d1f30bSChangli Gao 	     rt = rt->dst.rt6_next)
579f11e6659SDavid S. Miller 		match = find_match(rt, oif, strict, &mpri, match);
580f11e6659SDavid S. Miller 
581f11e6659SDavid S. Miller 	return match;
582f11e6659SDavid S. Miller }
583f11e6659SDavid S. Miller 
584f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
585f11e6659SDavid S. Miller {
586f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
5878ed67789SDaniel Lezcano 	struct net *net;
588f11e6659SDavid S. Miller 
589f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
590f11e6659SDavid S. Miller 	if (!rt0)
591f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
592f11e6659SDavid S. Miller 
593f11e6659SDavid S. Miller 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
594f11e6659SDavid S. Miller 
595554cfb7eSYOSHIFUJI Hideaki 	if (!match &&
596f11e6659SDavid S. Miller 	    (strict & RT6_LOOKUP_F_REACHABLE)) {
597d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
598f11e6659SDavid S. Miller 
599554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
600f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
601f11e6659SDavid S. Miller 			next = fn->leaf;
602f11e6659SDavid S. Miller 
603f11e6659SDavid S. Miller 		if (next != rt0)
604f11e6659SDavid S. Miller 			fn->rr_ptr = next;
605554cfb7eSYOSHIFUJI Hideaki 	}
606554cfb7eSYOSHIFUJI Hideaki 
607d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
608a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
6091da177e4SLinus Torvalds }
6101da177e4SLinus Torvalds 
61170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
61270ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
613b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
61470ceb4f5SYOSHIFUJI Hideaki {
615c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
61670ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
61770ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
61870ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
6194bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
62070ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
62170ceb4f5SYOSHIFUJI Hideaki 
62270ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
62370ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
62470ceb4f5SYOSHIFUJI Hideaki 	}
62570ceb4f5SYOSHIFUJI Hideaki 
62670ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
62770ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
62870ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
62970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
63070ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
63170ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
63270ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
63370ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
63470ceb4f5SYOSHIFUJI Hideaki 		}
63570ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
63670ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
63770ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
63870ceb4f5SYOSHIFUJI Hideaki 		}
63970ceb4f5SYOSHIFUJI Hideaki 	}
64070ceb4f5SYOSHIFUJI Hideaki 
64170ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
64270ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
6433933fc95SJens Rosenboom 		return -EINVAL;
64470ceb4f5SYOSHIFUJI Hideaki 
6454bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
64670ceb4f5SYOSHIFUJI Hideaki 
64770ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
64870ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
64970ceb4f5SYOSHIFUJI Hideaki 	else {
65070ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
65170ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
65270ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
65370ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
65470ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
65570ceb4f5SYOSHIFUJI Hideaki 	}
65670ceb4f5SYOSHIFUJI Hideaki 
657efa2cea0SDaniel Lezcano 	rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
658efa2cea0SDaniel Lezcano 				dev->ifindex);
65970ceb4f5SYOSHIFUJI Hideaki 
66070ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
661e0a1ad73SThomas Graf 		ip6_del_rt(rt);
66270ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
66370ceb4f5SYOSHIFUJI Hideaki 	}
66470ceb4f5SYOSHIFUJI Hideaki 
66570ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
666efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
66770ceb4f5SYOSHIFUJI Hideaki 					pref);
66870ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
66970ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
67070ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
67170ceb4f5SYOSHIFUJI Hideaki 
67270ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
6731716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
6741716a961SGao feng 			rt6_clean_expires(rt);
6751716a961SGao feng 		else
6761716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
6771716a961SGao feng 
678d8d1f30bSChangli Gao 		dst_release(&rt->dst);
67970ceb4f5SYOSHIFUJI Hideaki 	}
68070ceb4f5SYOSHIFUJI Hideaki 	return 0;
68170ceb4f5SYOSHIFUJI Hideaki }
68270ceb4f5SYOSHIFUJI Hideaki #endif
68370ceb4f5SYOSHIFUJI Hideaki 
6848ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr)			\
685982f56f3SYOSHIFUJI Hideaki do { \
6868ed67789SDaniel Lezcano 	if (rt == __net->ipv6.ip6_null_entry) {	\
687982f56f3SYOSHIFUJI Hideaki 		struct fib6_node *pn; \
688e0eda7bbSVille Nuorvala 		while (1) { \
689982f56f3SYOSHIFUJI Hideaki 			if (fn->fn_flags & RTN_TL_ROOT) \
690c71099acSThomas Graf 				goto out; \
691982f56f3SYOSHIFUJI Hideaki 			pn = fn->parent; \
692982f56f3SYOSHIFUJI Hideaki 			if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
6938bce65b9SKim Nordlund 				fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
694982f56f3SYOSHIFUJI Hideaki 			else \
695982f56f3SYOSHIFUJI Hideaki 				fn = pn; \
696c71099acSThomas Graf 			if (fn->fn_flags & RTN_RTINFO) \
697c71099acSThomas Graf 				goto restart; \
698c71099acSThomas Graf 		} \
699982f56f3SYOSHIFUJI Hideaki 	} \
700982f56f3SYOSHIFUJI Hideaki } while (0)
701c71099acSThomas Graf 
7028ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
7038ed67789SDaniel Lezcano 					     struct fib6_table *table,
7044c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
7051da177e4SLinus Torvalds {
7061da177e4SLinus Torvalds 	struct fib6_node *fn;
7071da177e4SLinus Torvalds 	struct rt6_info *rt;
7081da177e4SLinus Torvalds 
709c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
7104c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
711c71099acSThomas Graf restart:
712c71099acSThomas Graf 	rt = fn->leaf;
7134c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
7144c9483b2SDavid S. Miller 	BACKTRACK(net, &fl6->saddr);
715c71099acSThomas Graf out:
716d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
717c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
7181da177e4SLinus Torvalds 	return rt;
719c71099acSThomas Graf 
720c71099acSThomas Graf }
721c71099acSThomas Graf 
722ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
723ea6e574eSFlorian Westphal 				    int flags)
724ea6e574eSFlorian Westphal {
725ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
726ea6e574eSFlorian Westphal }
727ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
728ea6e574eSFlorian Westphal 
7299acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
7309acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
731c71099acSThomas Graf {
7324c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
7334c9483b2SDavid S. Miller 		.flowi6_oif = oif,
7344c9483b2SDavid S. Miller 		.daddr = *daddr,
735c71099acSThomas Graf 	};
736c71099acSThomas Graf 	struct dst_entry *dst;
73777d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
738c71099acSThomas Graf 
739adaa70bbSThomas Graf 	if (saddr) {
7404c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
741adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
742adaa70bbSThomas Graf 	}
743adaa70bbSThomas Graf 
7444c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
745c71099acSThomas Graf 	if (dst->error == 0)
746c71099acSThomas Graf 		return (struct rt6_info *) dst;
747c71099acSThomas Graf 
748c71099acSThomas Graf 	dst_release(dst);
749c71099acSThomas Graf 
7501da177e4SLinus Torvalds 	return NULL;
7511da177e4SLinus Torvalds }
7521da177e4SLinus Torvalds 
7537159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
7547159039aSYOSHIFUJI Hideaki 
755c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
7561da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
7571da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
7581da177e4SLinus Torvalds    be destroyed.
7591da177e4SLinus Torvalds  */
7601da177e4SLinus Torvalds 
76186872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
7621da177e4SLinus Torvalds {
7631da177e4SLinus Torvalds 	int err;
764c71099acSThomas Graf 	struct fib6_table *table;
7651da177e4SLinus Torvalds 
766c71099acSThomas Graf 	table = rt->rt6i_table;
767c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
76886872cb5SThomas Graf 	err = fib6_add(&table->tb6_root, rt, info);
769c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds 	return err;
7721da177e4SLinus Torvalds }
7731da177e4SLinus Torvalds 
77440e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
77540e22e8fSThomas Graf {
7764d1169c1SDenis V. Lunev 	struct nl_info info = {
777d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
7784d1169c1SDenis V. Lunev 	};
779528c4cebSDenis V. Lunev 	return __ip6_ins_rt(rt, &info);
78040e22e8fSThomas Graf }
78140e22e8fSThomas Graf 
7821716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
78321efcfa0SEric Dumazet 				      const struct in6_addr *daddr,
784b71d1d42SEric Dumazet 				      const struct in6_addr *saddr)
7851da177e4SLinus Torvalds {
7861da177e4SLinus Torvalds 	struct rt6_info *rt;
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	/*
7891da177e4SLinus Torvalds 	 *	Clone the route.
7901da177e4SLinus Torvalds 	 */
7911da177e4SLinus Torvalds 
79221efcfa0SEric Dumazet 	rt = ip6_rt_copy(ort, daddr);
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 	if (rt) {
79514deae41SDavid S. Miller 		int attempts = !in_softirq();
79614deae41SDavid S. Miller 
79758c4fb86SYOSHIFUJI Hideaki 		if (!(rt->rt6i_flags & RTF_GATEWAY)) {
798bb3c3686SDavid S. Miller 			if (ort->rt6i_dst.plen != 128 &&
79921efcfa0SEric Dumazet 			    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
80058c4fb86SYOSHIFUJI Hideaki 				rt->rt6i_flags |= RTF_ANYCAST;
8014e3fd7a0SAlexey Dobriyan 			rt->rt6i_gateway = *daddr;
80258c4fb86SYOSHIFUJI Hideaki 		}
8031da177e4SLinus Torvalds 
8041da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_CACHE;
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
8071da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
8084e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
8091da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
8101da177e4SLinus Torvalds 		}
8111da177e4SLinus Torvalds #endif
8121da177e4SLinus Torvalds 
81314deae41SDavid S. Miller 	retry:
8148ade06c6SDavid S. Miller 		if (rt6_bind_neighbour(rt, rt->dst.dev)) {
815d1918542SDavid S. Miller 			struct net *net = dev_net(rt->dst.dev);
81614deae41SDavid S. Miller 			int saved_rt_min_interval =
81714deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval;
81814deae41SDavid S. Miller 			int saved_rt_elasticity =
81914deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity;
82014deae41SDavid S. Miller 
82114deae41SDavid S. Miller 			if (attempts-- > 0) {
82214deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
82314deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
82414deae41SDavid S. Miller 
82586393e52SAlexey Dobriyan 				ip6_dst_gc(&net->ipv6.ip6_dst_ops);
82614deae41SDavid S. Miller 
82714deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity =
82814deae41SDavid S. Miller 					saved_rt_elasticity;
82914deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval =
83014deae41SDavid S. Miller 					saved_rt_min_interval;
83114deae41SDavid S. Miller 				goto retry;
83214deae41SDavid S. Miller 			}
83314deae41SDavid S. Miller 
834f3213831SJoe Perches 			net_warn_ratelimited("Neighbour table overflow\n");
835d8d1f30bSChangli Gao 			dst_free(&rt->dst);
83614deae41SDavid S. Miller 			return NULL;
83714deae41SDavid S. Miller 		}
83895a9a5baSYOSHIFUJI Hideaki 	}
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds 	return rt;
8411da177e4SLinus Torvalds }
84295a9a5baSYOSHIFUJI Hideaki 
84321efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
84421efcfa0SEric Dumazet 					const struct in6_addr *daddr)
845299d9939SYOSHIFUJI Hideaki {
84621efcfa0SEric Dumazet 	struct rt6_info *rt = ip6_rt_copy(ort, daddr);
84721efcfa0SEric Dumazet 
848299d9939SYOSHIFUJI Hideaki 	if (rt) {
849299d9939SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_CACHE;
85097cac082SDavid S. Miller 		rt->n = neigh_clone(ort->n);
851299d9939SYOSHIFUJI Hideaki 	}
852299d9939SYOSHIFUJI Hideaki 	return rt;
853299d9939SYOSHIFUJI Hideaki }
854299d9939SYOSHIFUJI Hideaki 
8558ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
8564c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
8571da177e4SLinus Torvalds {
8581da177e4SLinus Torvalds 	struct fib6_node *fn;
859519fbd87SYOSHIFUJI Hideaki 	struct rt6_info *rt, *nrt;
860c71099acSThomas Graf 	int strict = 0;
8611da177e4SLinus Torvalds 	int attempts = 3;
862519fbd87SYOSHIFUJI Hideaki 	int err;
86353b7997fSYOSHIFUJI Hideaki 	int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
8641da177e4SLinus Torvalds 
86577d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
8661da177e4SLinus Torvalds 
8671da177e4SLinus Torvalds relookup:
868c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8691da177e4SLinus Torvalds 
8708238dd06SYOSHIFUJI Hideaki restart_2:
8714c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
8721da177e4SLinus Torvalds 
8731da177e4SLinus Torvalds restart:
8744acad72dSPavel Emelyanov 	rt = rt6_select(fn, oif, strict | reachable);
8758ed67789SDaniel Lezcano 
8764c9483b2SDavid S. Miller 	BACKTRACK(net, &fl6->saddr);
8778ed67789SDaniel Lezcano 	if (rt == net->ipv6.ip6_null_entry ||
8788238dd06SYOSHIFUJI Hideaki 	    rt->rt6i_flags & RTF_CACHE)
8791da177e4SLinus Torvalds 		goto out;
8801da177e4SLinus Torvalds 
881d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
882c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8831da177e4SLinus Torvalds 
88497cac082SDavid S. Miller 	if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
8854c9483b2SDavid S. Miller 		nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
8867343ff31SDavid S. Miller 	else if (!(rt->dst.flags & DST_HOST))
8874c9483b2SDavid S. Miller 		nrt = rt6_alloc_clone(rt, &fl6->daddr);
8887343ff31SDavid S. Miller 	else
8897343ff31SDavid S. Miller 		goto out2;
8901da177e4SLinus Torvalds 
891d8d1f30bSChangli Gao 	dst_release(&rt->dst);
8928ed67789SDaniel Lezcano 	rt = nrt ? : net->ipv6.ip6_null_entry;
8931da177e4SLinus Torvalds 
894d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
895e40cf353SYOSHIFUJI Hideaki 	if (nrt) {
89640e22e8fSThomas Graf 		err = ip6_ins_rt(nrt);
897e40cf353SYOSHIFUJI Hideaki 		if (!err)
898e40cf353SYOSHIFUJI Hideaki 			goto out2;
899e40cf353SYOSHIFUJI Hideaki 	}
900e40cf353SYOSHIFUJI Hideaki 
901e40cf353SYOSHIFUJI Hideaki 	if (--attempts <= 0)
9021da177e4SLinus Torvalds 		goto out2;
9031da177e4SLinus Torvalds 
904519fbd87SYOSHIFUJI Hideaki 	/*
905c71099acSThomas Graf 	 * Race condition! In the gap, when table->tb6_lock was
906519fbd87SYOSHIFUJI Hideaki 	 * released someone could insert this route.  Relookup.
9071da177e4SLinus Torvalds 	 */
908d8d1f30bSChangli Gao 	dst_release(&rt->dst);
9091da177e4SLinus Torvalds 	goto relookup;
910e40cf353SYOSHIFUJI Hideaki 
911519fbd87SYOSHIFUJI Hideaki out:
9128238dd06SYOSHIFUJI Hideaki 	if (reachable) {
9138238dd06SYOSHIFUJI Hideaki 		reachable = 0;
9148238dd06SYOSHIFUJI Hideaki 		goto restart_2;
9158238dd06SYOSHIFUJI Hideaki 	}
916d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
917c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
9181da177e4SLinus Torvalds out2:
919d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
920d8d1f30bSChangli Gao 	rt->dst.__use++;
921c71099acSThomas Graf 
922c71099acSThomas Graf 	return rt;
923c71099acSThomas Graf }
924c71099acSThomas Graf 
9258ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
9264c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
9274acad72dSPavel Emelyanov {
9284c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
9294acad72dSPavel Emelyanov }
9304acad72dSPavel Emelyanov 
93172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
93272331bc0SShmulik Ladkani 						struct net_device *dev,
93372331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
93472331bc0SShmulik Ladkani {
93572331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
93672331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
93772331bc0SShmulik Ladkani 
93872331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
93972331bc0SShmulik Ladkani }
94072331bc0SShmulik Ladkani 
941c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
942c71099acSThomas Graf {
943b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
944c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
945adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
9464c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
9474c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
9484c9483b2SDavid S. Miller 		.daddr = iph->daddr,
9494c9483b2SDavid S. Miller 		.saddr = iph->saddr,
9504c9483b2SDavid S. Miller 		.flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
9514c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
9524c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
953c71099acSThomas Graf 	};
954adaa70bbSThomas Graf 
95572331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
956c71099acSThomas Graf }
957c71099acSThomas Graf 
9588ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
9594c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
960c71099acSThomas Graf {
9614c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
962c71099acSThomas Graf }
963c71099acSThomas Graf 
9649c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
9654c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
966c71099acSThomas Graf {
967c71099acSThomas Graf 	int flags = 0;
968c71099acSThomas Graf 
9694dc27d1cSDavid McCullough 	fl6->flowi6_iif = net->loopback_dev->ifindex;
9704dc27d1cSDavid McCullough 
9714c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
97277d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
973c71099acSThomas Graf 
9744c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
975adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
9760c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
9770c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
978adaa70bbSThomas Graf 
9794c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
9801da177e4SLinus Torvalds }
9811da177e4SLinus Torvalds 
9827159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
9831da177e4SLinus Torvalds 
9842774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
98514e50e57SDavid S. Miller {
9865c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
98714e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
98814e50e57SDavid S. Miller 
989f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
99014e50e57SDavid S. Miller 	if (rt) {
991d8d1f30bSChangli Gao 		new = &rt->dst;
99214e50e57SDavid S. Miller 
9938104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
9948104891bSSteffen Klassert 		rt6_init_peer(rt, net->ipv6.peers);
9958104891bSSteffen Klassert 
99614e50e57SDavid S. Miller 		new->__use = 1;
997352e512cSHerbert Xu 		new->input = dst_discard;
998352e512cSHerbert Xu 		new->output = dst_discard;
99914e50e57SDavid S. Miller 
100021efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
100121efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
100221efcfa0SEric Dumazet 		else
1003defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
100414e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
100514e50e57SDavid S. Miller 		if (rt->rt6i_idev)
100614e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
100714e50e57SDavid S. Miller 
10084e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
10091716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
10101716a961SGao feng 		rt6_clean_expires(rt);
101114e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
101214e50e57SDavid S. Miller 
101314e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
101414e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
101514e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
101614e50e57SDavid S. Miller #endif
101714e50e57SDavid S. Miller 
101814e50e57SDavid S. Miller 		dst_free(new);
101914e50e57SDavid S. Miller 	}
102014e50e57SDavid S. Miller 
102169ead7afSDavid S. Miller 	dst_release(dst_orig);
102269ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
102314e50e57SDavid S. Miller }
102414e50e57SDavid S. Miller 
10251da177e4SLinus Torvalds /*
10261da177e4SLinus Torvalds  *	Destination cache support functions
10271da177e4SLinus Torvalds  */
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
10301da177e4SLinus Torvalds {
10311da177e4SLinus Torvalds 	struct rt6_info *rt;
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
10341da177e4SLinus Torvalds 
1035*6f3118b5SNicolas Dichtel 	/* All IPV6 dsts are created with ->obsolete set to the value
1036*6f3118b5SNicolas Dichtel 	 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
1037*6f3118b5SNicolas Dichtel 	 * into this function always.
1038*6f3118b5SNicolas Dichtel 	 */
1039*6f3118b5SNicolas Dichtel 	if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
1040*6f3118b5SNicolas Dichtel 		return NULL;
1041*6f3118b5SNicolas Dichtel 
10426431cbc2SDavid S. Miller 	if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
10436431cbc2SDavid S. Miller 		if (rt->rt6i_peer_genid != rt6_peer_genid()) {
104497bab73fSDavid S. Miller 			if (!rt6_has_peer(rt))
10456431cbc2SDavid S. Miller 				rt6_bind_peer(rt, 0);
10466431cbc2SDavid S. Miller 			rt->rt6i_peer_genid = rt6_peer_genid();
10476431cbc2SDavid S. Miller 		}
10481da177e4SLinus Torvalds 		return dst;
10496431cbc2SDavid S. Miller 	}
10501da177e4SLinus Torvalds 	return NULL;
10511da177e4SLinus Torvalds }
10521da177e4SLinus Torvalds 
10531da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
10541da177e4SLinus Torvalds {
10551da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	if (rt) {
105854c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
105954c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1060e0a1ad73SThomas Graf 				ip6_del_rt(rt);
106154c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
10621da177e4SLinus Torvalds 			}
106354c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
106454c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
106554c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
106654c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
106754c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
106854c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
10691da177e4SLinus Torvalds }
10701da177e4SLinus Torvalds 
10711da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
10721da177e4SLinus Torvalds {
10731da177e4SLinus Torvalds 	struct rt6_info *rt;
10741da177e4SLinus Torvalds 
10753ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
10761da177e4SLinus Torvalds 
1077adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
10781da177e4SLinus Torvalds 	if (rt) {
10791716a961SGao feng 		if (rt->rt6i_flags & RTF_CACHE)
10801716a961SGao feng 			rt6_update_expires(rt, 0);
10811716a961SGao feng 		else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
10821da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
10831da177e4SLinus Torvalds 	}
10841da177e4SLinus Torvalds }
10851da177e4SLinus Torvalds 
10866700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
10876700c270SDavid S. Miller 			       struct sk_buff *skb, u32 mtu)
10881da177e4SLinus Torvalds {
10891da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info*)dst;
10901da177e4SLinus Torvalds 
109181aded24SDavid S. Miller 	dst_confirm(dst);
10921da177e4SLinus Torvalds 	if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
109381aded24SDavid S. Miller 		struct net *net = dev_net(dst->dev);
109481aded24SDavid S. Miller 
10951da177e4SLinus Torvalds 		rt6->rt6i_flags |= RTF_MODIFIED;
10961da177e4SLinus Torvalds 		if (mtu < IPV6_MIN_MTU) {
1097defb3519SDavid S. Miller 			u32 features = dst_metric(dst, RTAX_FEATURES);
10981da177e4SLinus Torvalds 			mtu = IPV6_MIN_MTU;
1099defb3519SDavid S. Miller 			features |= RTAX_FEATURE_ALLFRAG;
1100defb3519SDavid S. Miller 			dst_metric_set(dst, RTAX_FEATURES, features);
11011da177e4SLinus Torvalds 		}
1102defb3519SDavid S. Miller 		dst_metric_set(dst, RTAX_MTU, mtu);
110381aded24SDavid S. Miller 		rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
11041da177e4SLinus Torvalds 	}
11051da177e4SLinus Torvalds }
11061da177e4SLinus Torvalds 
110742ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
110842ae66c8SDavid S. Miller 		     int oif, u32 mark)
110981aded24SDavid S. Miller {
111081aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
111181aded24SDavid S. Miller 	struct dst_entry *dst;
111281aded24SDavid S. Miller 	struct flowi6 fl6;
111381aded24SDavid S. Miller 
111481aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
111581aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
111681aded24SDavid S. Miller 	fl6.flowi6_mark = mark;
11173e12939aSDavid S. Miller 	fl6.flowi6_flags = 0;
111881aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
111981aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
112081aded24SDavid S. Miller 	fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
112181aded24SDavid S. Miller 
112281aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
112381aded24SDavid S. Miller 	if (!dst->error)
11246700c270SDavid S. Miller 		ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
112581aded24SDavid S. Miller 	dst_release(dst);
112681aded24SDavid S. Miller }
112781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
112881aded24SDavid S. Miller 
112981aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
113081aded24SDavid S. Miller {
113181aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
113281aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
113381aded24SDavid S. Miller }
113481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
113581aded24SDavid S. Miller 
11363a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
11373a5ad2eeSDavid S. Miller {
11383a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
11393a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
11403a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
11413a5ad2eeSDavid S. Miller 
11423a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
11433a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
11443a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
11453a5ad2eeSDavid S. Miller 	fl6.flowi6_flags = 0;
11463a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
11473a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
11483a5ad2eeSDavid S. Miller 	fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
11493a5ad2eeSDavid S. Miller 
11503a5ad2eeSDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
11513a5ad2eeSDavid S. Miller 	if (!dst->error)
11526700c270SDavid S. Miller 		rt6_do_redirect(dst, NULL, skb);
11533a5ad2eeSDavid S. Miller 	dst_release(dst);
11543a5ad2eeSDavid S. Miller }
11553a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
11563a5ad2eeSDavid S. Miller 
11573a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
11583a5ad2eeSDavid S. Miller {
11593a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
11603a5ad2eeSDavid S. Miller }
11613a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
11623a5ad2eeSDavid S. Miller 
11630dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
11641da177e4SLinus Torvalds {
11650dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
11660dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
11670dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
11680dbaee3bSDavid S. Miller 
11691da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
11701da177e4SLinus Torvalds 
11715578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
11725578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
11731da177e4SLinus Torvalds 
11741da177e4SLinus Torvalds 	/*
11751da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
11761da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
11771da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
11781da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
11791da177e4SLinus Torvalds 	 */
11801da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
11811da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
11821da177e4SLinus Torvalds 	return mtu;
11831da177e4SLinus Torvalds }
11841da177e4SLinus Torvalds 
1185ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1186d33e4553SDavid S. Miller {
1187d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1188618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1189618f9bc7SSteffen Klassert 
1190618f9bc7SSteffen Klassert 	if (mtu)
1191618f9bc7SSteffen Klassert 		return mtu;
1192618f9bc7SSteffen Klassert 
1193618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1194d33e4553SDavid S. Miller 
1195d33e4553SDavid S. Miller 	rcu_read_lock();
1196d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1197d33e4553SDavid S. Miller 	if (idev)
1198d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1199d33e4553SDavid S. Miller 	rcu_read_unlock();
1200d33e4553SDavid S. Miller 
1201d33e4553SDavid S. Miller 	return mtu;
1202d33e4553SDavid S. Miller }
1203d33e4553SDavid S. Miller 
12043b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
12053b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
12065d0bbeebSThomas Graf 
12073b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
12081da177e4SLinus Torvalds 				  struct neighbour *neigh,
120987a11578SDavid S. Miller 				  struct flowi6 *fl6)
12101da177e4SLinus Torvalds {
121187a11578SDavid S. Miller 	struct dst_entry *dst;
12121da177e4SLinus Torvalds 	struct rt6_info *rt;
12131da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1214c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12151da177e4SLinus Torvalds 
121638308473SDavid S. Miller 	if (unlikely(!idev))
1217122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
12181da177e4SLinus Torvalds 
12198b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, dev, 0, NULL);
122038308473SDavid S. Miller 	if (unlikely(!rt)) {
12211da177e4SLinus Torvalds 		in6_dev_put(idev);
122287a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
12231da177e4SLinus Torvalds 		goto out;
12241da177e4SLinus Torvalds 	}
12251da177e4SLinus Torvalds 
12261da177e4SLinus Torvalds 	if (neigh)
12271da177e4SLinus Torvalds 		neigh_hold(neigh);
122814deae41SDavid S. Miller 	else {
1229f894cbf8SDavid S. Miller 		neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
1230b43faac6SDavid S. Miller 		if (IS_ERR(neigh)) {
1231252c3d84SRongQing.Li 			in6_dev_put(idev);
1232b43faac6SDavid S. Miller 			dst_free(&rt->dst);
1233b43faac6SDavid S. Miller 			return ERR_CAST(neigh);
1234b43faac6SDavid S. Miller 		}
123514deae41SDavid S. Miller 	}
12361da177e4SLinus Torvalds 
12378e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
12388e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
123997cac082SDavid S. Miller 	rt->n = neigh;
1240d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
124187a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
12428e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
12438e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
12447011687fSGao feng 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
12451da177e4SLinus Torvalds 
12463b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1247d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1248d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
12493b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
12501da177e4SLinus Torvalds 
12515578689aSDaniel Lezcano 	fib6_force_start_gc(net);
12521da177e4SLinus Torvalds 
125387a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
125487a11578SDavid S. Miller 
12551da177e4SLinus Torvalds out:
125687a11578SDavid S. Miller 	return dst;
12571da177e4SLinus Torvalds }
12581da177e4SLinus Torvalds 
12593d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
12601da177e4SLinus Torvalds {
1261e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
12623d0f24a7SStephen Hemminger 	int more = 0;
12631da177e4SLinus Torvalds 
12643b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
12653b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
12665d0bbeebSThomas Graf 
12671da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
12681da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
12691da177e4SLinus Torvalds 			*pprev = dst->next;
12701da177e4SLinus Torvalds 			dst_free(dst);
12711da177e4SLinus Torvalds 		} else {
12721da177e4SLinus Torvalds 			pprev = &dst->next;
12733d0f24a7SStephen Hemminger 			++more;
12741da177e4SLinus Torvalds 		}
12751da177e4SLinus Torvalds 	}
12761da177e4SLinus Torvalds 
12773b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
12785d0bbeebSThomas Graf 
12793d0f24a7SStephen Hemminger 	return more;
12801da177e4SLinus Torvalds }
12811da177e4SLinus Torvalds 
12821e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
12831e493d19SDavid S. Miller 			    void *arg)
12841e493d19SDavid S. Miller {
12851e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
12861e493d19SDavid S. Miller 
12871e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
12881e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
12891e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
12901e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
12911e493d19SDavid S. Miller 		if (func(rt, arg)) {
12921e493d19SDavid S. Miller 			*pprev = dst->next;
12931e493d19SDavid S. Miller 			dst_free(dst);
12941e493d19SDavid S. Miller 		} else {
12951e493d19SDavid S. Miller 			pprev = &dst->next;
12961e493d19SDavid S. Miller 		}
12971e493d19SDavid S. Miller 	}
12981e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
12991e493d19SDavid S. Miller }
13001e493d19SDavid S. Miller 
1301569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
13021da177e4SLinus Torvalds {
13031da177e4SLinus Torvalds 	unsigned long now = jiffies;
130486393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
13057019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
13067019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
13077019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
13087019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
13097019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1310fc66f95cSEric Dumazet 	int entries;
13111da177e4SLinus Torvalds 
1312fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
13137019b78eSDaniel Lezcano 	if (time_after(rt_last_gc + rt_min_interval, now) &&
1314fc66f95cSEric Dumazet 	    entries <= rt_max_size)
13151da177e4SLinus Torvalds 		goto out;
13161da177e4SLinus Torvalds 
13176891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
13186891a346SBenjamin Thery 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
13196891a346SBenjamin Thery 	net->ipv6.ip6_rt_last_gc = now;
1320fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1321fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
13227019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
13231da177e4SLinus Torvalds out:
13247019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1325fc66f95cSEric Dumazet 	return entries > rt_max_size;
13261da177e4SLinus Torvalds }
13271da177e4SLinus Torvalds 
13281da177e4SLinus Torvalds /* Clean host part of a prefix. Not necessary in radix tree,
13291da177e4SLinus Torvalds    but results in cleaner routing tables.
13301da177e4SLinus Torvalds 
13311da177e4SLinus Torvalds    Remove it only when all the things will work!
13321da177e4SLinus Torvalds  */
13331da177e4SLinus Torvalds 
13346b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst)
13351da177e4SLinus Torvalds {
13365170ae82SDavid S. Miller 	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
1337a02e4b7dSDavid S. Miller 	if (hoplimit == 0) {
13386b75d090SYOSHIFUJI Hideaki 		struct net_device *dev = dst->dev;
1339c68f24ccSEric Dumazet 		struct inet6_dev *idev;
1340c68f24ccSEric Dumazet 
1341c68f24ccSEric Dumazet 		rcu_read_lock();
1342c68f24ccSEric Dumazet 		idev = __in6_dev_get(dev);
1343c68f24ccSEric Dumazet 		if (idev)
13441da177e4SLinus Torvalds 			hoplimit = idev->cnf.hop_limit;
1345c68f24ccSEric Dumazet 		else
134653b7997fSYOSHIFUJI Hideaki 			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
1347c68f24ccSEric Dumazet 		rcu_read_unlock();
13481da177e4SLinus Torvalds 	}
13491da177e4SLinus Torvalds 	return hoplimit;
13501da177e4SLinus Torvalds }
1351abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit);
13521da177e4SLinus Torvalds 
13531da177e4SLinus Torvalds /*
13541da177e4SLinus Torvalds  *
13551da177e4SLinus Torvalds  */
13561da177e4SLinus Torvalds 
135786872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
13581da177e4SLinus Torvalds {
13591da177e4SLinus Torvalds 	int err;
13605578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
13611da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
13621da177e4SLinus Torvalds 	struct net_device *dev = NULL;
13631da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1364c71099acSThomas Graf 	struct fib6_table *table;
13651da177e4SLinus Torvalds 	int addr_type;
13661da177e4SLinus Torvalds 
136786872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
13681da177e4SLinus Torvalds 		return -EINVAL;
13691da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
137086872cb5SThomas Graf 	if (cfg->fc_src_len)
13711da177e4SLinus Torvalds 		return -EINVAL;
13721da177e4SLinus Torvalds #endif
137386872cb5SThomas Graf 	if (cfg->fc_ifindex) {
13741da177e4SLinus Torvalds 		err = -ENODEV;
13755578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
13761da177e4SLinus Torvalds 		if (!dev)
13771da177e4SLinus Torvalds 			goto out;
13781da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
13791da177e4SLinus Torvalds 		if (!idev)
13801da177e4SLinus Torvalds 			goto out;
13811da177e4SLinus Torvalds 	}
13821da177e4SLinus Torvalds 
138386872cb5SThomas Graf 	if (cfg->fc_metric == 0)
138486872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
13851da177e4SLinus Torvalds 
1386c71099acSThomas Graf 	err = -ENOBUFS;
138738308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1388d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1389d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
139038308473SDavid S. Miller 		if (!table) {
1391f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1392d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1393d71314b4SMatti Vaittinen 		}
1394d71314b4SMatti Vaittinen 	} else {
1395d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1396d71314b4SMatti Vaittinen 	}
139738308473SDavid S. Miller 
139838308473SDavid S. Miller 	if (!table)
1399c71099acSThomas Graf 		goto out;
1400c71099acSThomas Graf 
14018b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
14021da177e4SLinus Torvalds 
140338308473SDavid S. Miller 	if (!rt) {
14041da177e4SLinus Torvalds 		err = -ENOMEM;
14051da177e4SLinus Torvalds 		goto out;
14061da177e4SLinus Torvalds 	}
14071da177e4SLinus Torvalds 
14081716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
14091716a961SGao feng 		rt6_set_expires(rt, jiffies +
14101716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
14111716a961SGao feng 	else
14121716a961SGao feng 		rt6_clean_expires(rt);
14131da177e4SLinus Torvalds 
141486872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
141586872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
141686872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
141786872cb5SThomas Graf 
141886872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
14191da177e4SLinus Torvalds 
14201da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1421d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1422ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1423ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
14241da177e4SLinus Torvalds 	else
1425d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
14261da177e4SLinus Torvalds 
1427d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
14281da177e4SLinus Torvalds 
142986872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
143086872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
14311da177e4SLinus Torvalds 	if (rt->rt6i_dst.plen == 128)
143211d53b49SDavid S. Miller 	       rt->dst.flags |= DST_HOST;
14331da177e4SLinus Torvalds 
14348e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
14358e2ec639SYan, Zheng 		u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
14368e2ec639SYan, Zheng 		if (!metrics) {
14378e2ec639SYan, Zheng 			err = -ENOMEM;
14388e2ec639SYan, Zheng 			goto out;
14398e2ec639SYan, Zheng 		}
14408e2ec639SYan, Zheng 		dst_init_metrics(&rt->dst, metrics, 0);
14418e2ec639SYan, Zheng 	}
14421da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
144386872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
144486872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
14451da177e4SLinus Torvalds #endif
14461da177e4SLinus Torvalds 
144786872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
14481da177e4SLinus Torvalds 
14491da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
14501da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
14511da177e4SLinus Torvalds 	 */
145286872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
145338308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
145438308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
145538308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
14561da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
14575578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
14581da177e4SLinus Torvalds 			if (dev) {
14591da177e4SLinus Torvalds 				dev_put(dev);
14601da177e4SLinus Torvalds 				in6_dev_put(idev);
14611da177e4SLinus Torvalds 			}
14625578689aSDaniel Lezcano 			dev = net->loopback_dev;
14631da177e4SLinus Torvalds 			dev_hold(dev);
14641da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
14651da177e4SLinus Torvalds 			if (!idev) {
14661da177e4SLinus Torvalds 				err = -ENODEV;
14671da177e4SLinus Torvalds 				goto out;
14681da177e4SLinus Torvalds 			}
14691da177e4SLinus Torvalds 		}
1470d8d1f30bSChangli Gao 		rt->dst.output = ip6_pkt_discard_out;
1471d8d1f30bSChangli Gao 		rt->dst.input = ip6_pkt_discard;
1472d8d1f30bSChangli Gao 		rt->dst.error = -ENETUNREACH;
14731da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
14741da177e4SLinus Torvalds 		goto install_route;
14751da177e4SLinus Torvalds 	}
14761da177e4SLinus Torvalds 
147786872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1478b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
14791da177e4SLinus Torvalds 		int gwa_type;
14801da177e4SLinus Torvalds 
148186872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
14824e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
14831da177e4SLinus Torvalds 		gwa_type = ipv6_addr_type(gw_addr);
14841da177e4SLinus Torvalds 
14851da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
14861da177e4SLinus Torvalds 			struct rt6_info *grt;
14871da177e4SLinus Torvalds 
14881da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
14891da177e4SLinus Torvalds 			   addresses as nexthop address.
14901da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
14911da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
14921da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
14931da177e4SLinus Torvalds 			   some exceptions. --ANK
14941da177e4SLinus Torvalds 			 */
14951da177e4SLinus Torvalds 			err = -EINVAL;
14961da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
14971da177e4SLinus Torvalds 				goto out;
14981da177e4SLinus Torvalds 
14995578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
15001da177e4SLinus Torvalds 
15011da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
150238308473SDavid S. Miller 			if (!grt)
15031da177e4SLinus Torvalds 				goto out;
15041da177e4SLinus Torvalds 			if (dev) {
1505d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
1506d8d1f30bSChangli Gao 					dst_release(&grt->dst);
15071da177e4SLinus Torvalds 					goto out;
15081da177e4SLinus Torvalds 				}
15091da177e4SLinus Torvalds 			} else {
1510d1918542SDavid S. Miller 				dev = grt->dst.dev;
15111da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
15121da177e4SLinus Torvalds 				dev_hold(dev);
15131da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
15141da177e4SLinus Torvalds 			}
15151da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
15161da177e4SLinus Torvalds 				err = 0;
1517d8d1f30bSChangli Gao 			dst_release(&grt->dst);
15181da177e4SLinus Torvalds 
15191da177e4SLinus Torvalds 			if (err)
15201da177e4SLinus Torvalds 				goto out;
15211da177e4SLinus Torvalds 		}
15221da177e4SLinus Torvalds 		err = -EINVAL;
152338308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
15241da177e4SLinus Torvalds 			goto out;
15251da177e4SLinus Torvalds 	}
15261da177e4SLinus Torvalds 
15271da177e4SLinus Torvalds 	err = -ENODEV;
152838308473SDavid S. Miller 	if (!dev)
15291da177e4SLinus Torvalds 		goto out;
15301da177e4SLinus Torvalds 
1531c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1532c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1533c3968a85SDaniel Walter 			err = -EINVAL;
1534c3968a85SDaniel Walter 			goto out;
1535c3968a85SDaniel Walter 		}
15364e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1537c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1538c3968a85SDaniel Walter 	} else
1539c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1540c3968a85SDaniel Walter 
154186872cb5SThomas Graf 	if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
15428ade06c6SDavid S. Miller 		err = rt6_bind_neighbour(rt, dev);
1543f83c7790SDavid S. Miller 		if (err)
15441da177e4SLinus Torvalds 			goto out;
15451da177e4SLinus Torvalds 	}
15461da177e4SLinus Torvalds 
154786872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
15481da177e4SLinus Torvalds 
15491da177e4SLinus Torvalds install_route:
155086872cb5SThomas Graf 	if (cfg->fc_mx) {
155186872cb5SThomas Graf 		struct nlattr *nla;
155286872cb5SThomas Graf 		int remaining;
15531da177e4SLinus Torvalds 
155486872cb5SThomas Graf 		nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
15558f4c1f9bSThomas Graf 			int type = nla_type(nla);
155686872cb5SThomas Graf 
155786872cb5SThomas Graf 			if (type) {
155886872cb5SThomas Graf 				if (type > RTAX_MAX) {
15591da177e4SLinus Torvalds 					err = -EINVAL;
15601da177e4SLinus Torvalds 					goto out;
15611da177e4SLinus Torvalds 				}
156286872cb5SThomas Graf 
1563defb3519SDavid S. Miller 				dst_metric_set(&rt->dst, type, nla_get_u32(nla));
15641da177e4SLinus Torvalds 			}
15651da177e4SLinus Torvalds 		}
15661da177e4SLinus Torvalds 	}
15671da177e4SLinus Torvalds 
1568d8d1f30bSChangli Gao 	rt->dst.dev = dev;
15691da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1570c71099acSThomas Graf 	rt->rt6i_table = table;
157163152fc0SDaniel Lezcano 
1572c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
157363152fc0SDaniel Lezcano 
157486872cb5SThomas Graf 	return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
15751da177e4SLinus Torvalds 
15761da177e4SLinus Torvalds out:
15771da177e4SLinus Torvalds 	if (dev)
15781da177e4SLinus Torvalds 		dev_put(dev);
15791da177e4SLinus Torvalds 	if (idev)
15801da177e4SLinus Torvalds 		in6_dev_put(idev);
15811da177e4SLinus Torvalds 	if (rt)
1582d8d1f30bSChangli Gao 		dst_free(&rt->dst);
15831da177e4SLinus Torvalds 	return err;
15841da177e4SLinus Torvalds }
15851da177e4SLinus Torvalds 
158686872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
15871da177e4SLinus Torvalds {
15881da177e4SLinus Torvalds 	int err;
1589c71099acSThomas Graf 	struct fib6_table *table;
1590d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
15911da177e4SLinus Torvalds 
15928ed67789SDaniel Lezcano 	if (rt == net->ipv6.ip6_null_entry)
15936c813a72SPatrick McHardy 		return -ENOENT;
15946c813a72SPatrick McHardy 
1595c71099acSThomas Graf 	table = rt->rt6i_table;
1596c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
15971da177e4SLinus Torvalds 
159886872cb5SThomas Graf 	err = fib6_del(rt, info);
1599d8d1f30bSChangli Gao 	dst_release(&rt->dst);
16001da177e4SLinus Torvalds 
1601c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
16021da177e4SLinus Torvalds 
16031da177e4SLinus Torvalds 	return err;
16041da177e4SLinus Torvalds }
16051da177e4SLinus Torvalds 
1606e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1607e0a1ad73SThomas Graf {
16084d1169c1SDenis V. Lunev 	struct nl_info info = {
1609d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
16104d1169c1SDenis V. Lunev 	};
1611528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1612e0a1ad73SThomas Graf }
1613e0a1ad73SThomas Graf 
161486872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
16151da177e4SLinus Torvalds {
1616c71099acSThomas Graf 	struct fib6_table *table;
16171da177e4SLinus Torvalds 	struct fib6_node *fn;
16181da177e4SLinus Torvalds 	struct rt6_info *rt;
16191da177e4SLinus Torvalds 	int err = -ESRCH;
16201da177e4SLinus Torvalds 
16215578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
162238308473SDavid S. Miller 	if (!table)
1623c71099acSThomas Graf 		return err;
16241da177e4SLinus Torvalds 
1625c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1626c71099acSThomas Graf 
1627c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
162886872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
162986872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
16301da177e4SLinus Torvalds 
16311da177e4SLinus Torvalds 	if (fn) {
1632d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
163386872cb5SThomas Graf 			if (cfg->fc_ifindex &&
1634d1918542SDavid S. Miller 			    (!rt->dst.dev ||
1635d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
16361da177e4SLinus Torvalds 				continue;
163786872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
163886872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
16391da177e4SLinus Torvalds 				continue;
164086872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
16411da177e4SLinus Torvalds 				continue;
1642d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1643c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
16441da177e4SLinus Torvalds 
164586872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
16461da177e4SLinus Torvalds 		}
16471da177e4SLinus Torvalds 	}
1648c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
16491da177e4SLinus Torvalds 
16501da177e4SLinus Torvalds 	return err;
16511da177e4SLinus Torvalds }
16521da177e4SLinus Torvalds 
16536700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
1654a6279458SYOSHIFUJI Hideaki {
1655e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
1656a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
1657e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
1658e8599ff4SDavid S. Miller 	const struct in6_addr *target;
1659e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
16606e157b6aSDavid S. Miller 	const struct in6_addr *dest;
16616e157b6aSDavid S. Miller 	struct neighbour *old_neigh;
1662e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
1663e8599ff4SDavid S. Miller 	struct neighbour *neigh;
1664e8599ff4SDavid S. Miller 	struct icmp6hdr *icmph;
16656e157b6aSDavid S. Miller 	int optlen, on_link;
16666e157b6aSDavid S. Miller 	u8 *lladdr;
1667e8599ff4SDavid S. Miller 
1668e8599ff4SDavid S. Miller 	optlen = skb->tail - skb->transport_header;
1669e8599ff4SDavid S. Miller 	optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1670e8599ff4SDavid S. Miller 
1671e8599ff4SDavid S. Miller 	if (optlen < 0) {
16726e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
1673e8599ff4SDavid S. Miller 		return;
1674e8599ff4SDavid S. Miller 	}
1675e8599ff4SDavid S. Miller 
1676e8599ff4SDavid S. Miller 	icmph = icmp6_hdr(skb);
1677e8599ff4SDavid S. Miller 	target = (const struct in6_addr *) (icmph + 1);
1678e8599ff4SDavid S. Miller 	dest = target + 1;
1679e8599ff4SDavid S. Miller 
1680e8599ff4SDavid S. Miller 	if (ipv6_addr_is_multicast(dest)) {
16816e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
1682e8599ff4SDavid S. Miller 		return;
1683e8599ff4SDavid S. Miller 	}
1684e8599ff4SDavid S. Miller 
16856e157b6aSDavid S. Miller 	on_link = 0;
1686e8599ff4SDavid S. Miller 	if (ipv6_addr_equal(dest, target)) {
1687e8599ff4SDavid S. Miller 		on_link = 1;
1688e8599ff4SDavid S. Miller 	} else if (ipv6_addr_type(target) !=
1689e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
16906e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
1691e8599ff4SDavid S. Miller 		return;
1692e8599ff4SDavid S. Miller 	}
1693e8599ff4SDavid S. Miller 
1694e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
1695e8599ff4SDavid S. Miller 	if (!in6_dev)
1696e8599ff4SDavid S. Miller 		return;
1697e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1698e8599ff4SDavid S. Miller 		return;
1699e8599ff4SDavid S. Miller 
1700e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
1701e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
1702e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
1703e8599ff4SDavid S. Miller 	 */
1704e8599ff4SDavid S. Miller 
1705e8599ff4SDavid S. Miller 	if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1706e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1707e8599ff4SDavid S. Miller 		return;
1708e8599ff4SDavid S. Miller 	}
17096e157b6aSDavid S. Miller 
17106e157b6aSDavid S. Miller 	lladdr = NULL;
1711e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
1712e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1713e8599ff4SDavid S. Miller 					     skb->dev);
1714e8599ff4SDavid S. Miller 		if (!lladdr) {
1715e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1716e8599ff4SDavid S. Miller 			return;
1717e8599ff4SDavid S. Miller 		}
1718e8599ff4SDavid S. Miller 	}
1719e8599ff4SDavid S. Miller 
17206e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
17216e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
17226e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
17236e157b6aSDavid S. Miller 		return;
17246e157b6aSDavid S. Miller 	}
17256e157b6aSDavid S. Miller 
17266e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
17276e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
17286e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
17296e157b6aSDavid S. Miller 	 */
17306e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
17316e157b6aSDavid S. Miller 
1732e8599ff4SDavid S. Miller 	neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1733e8599ff4SDavid S. Miller 	if (!neigh)
1734e8599ff4SDavid S. Miller 		return;
1735e8599ff4SDavid S. Miller 
17366e157b6aSDavid S. Miller 	/* Duplicate redirect: silently ignore. */
17376e157b6aSDavid S. Miller 	old_neigh = rt->n;
17386e157b6aSDavid S. Miller 	if (neigh == old_neigh)
1739a6279458SYOSHIFUJI Hideaki 		goto out;
17401da177e4SLinus Torvalds 
17411da177e4SLinus Torvalds 	/*
17421da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
17431da177e4SLinus Torvalds 	 */
17441da177e4SLinus Torvalds 
17451da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
17461da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
17471da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
17481da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
17491da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
17501da177e4SLinus Torvalds 		     );
17511da177e4SLinus Torvalds 
175221efcfa0SEric Dumazet 	nrt = ip6_rt_copy(rt, dest);
175338308473SDavid S. Miller 	if (!nrt)
17541da177e4SLinus Torvalds 		goto out;
17551da177e4SLinus Torvalds 
17561da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
17571da177e4SLinus Torvalds 	if (on_link)
17581da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
17591da177e4SLinus Torvalds 
17604e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
176197cac082SDavid S. Miller 	nrt->n = neigh_clone(neigh);
17621da177e4SLinus Torvalds 
176340e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
17641da177e4SLinus Torvalds 		goto out;
17651da177e4SLinus Torvalds 
1766d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
17671d248b1cSDavid S. Miller 	netevent.old_neigh = old_neigh;
1768d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
17691d248b1cSDavid S. Miller 	netevent.new_neigh = neigh;
17701d248b1cSDavid S. Miller 	netevent.daddr = dest;
17718d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
17728d71740cSTom Tucker 
17731da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
17746e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
1775e0a1ad73SThomas Graf 		ip6_del_rt(rt);
17761da177e4SLinus Torvalds 	}
17771da177e4SLinus Torvalds 
17781da177e4SLinus Torvalds out:
1779e8599ff4SDavid S. Miller 	neigh_release(neigh);
17806e157b6aSDavid S. Miller }
17816e157b6aSDavid S. Miller 
17821da177e4SLinus Torvalds /*
17831da177e4SLinus Torvalds  *	Misc support functions
17841da177e4SLinus Torvalds  */
17851da177e4SLinus Torvalds 
17861716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
178721efcfa0SEric Dumazet 				    const struct in6_addr *dest)
17881da177e4SLinus Torvalds {
1789d1918542SDavid S. Miller 	struct net *net = dev_net(ort->dst.dev);
17908b96d22dSDavid S. Miller 	struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
17918b96d22dSDavid S. Miller 					    ort->rt6i_table);
17921da177e4SLinus Torvalds 
17931da177e4SLinus Torvalds 	if (rt) {
1794d8d1f30bSChangli Gao 		rt->dst.input = ort->dst.input;
1795d8d1f30bSChangli Gao 		rt->dst.output = ort->dst.output;
17968e2ec639SYan, Zheng 		rt->dst.flags |= DST_HOST;
17971da177e4SLinus Torvalds 
17984e3fd7a0SAlexey Dobriyan 		rt->rt6i_dst.addr = *dest;
17998e2ec639SYan, Zheng 		rt->rt6i_dst.plen = 128;
1800defb3519SDavid S. Miller 		dst_copy_metrics(&rt->dst, &ort->dst);
1801d8d1f30bSChangli Gao 		rt->dst.error = ort->dst.error;
18021da177e4SLinus Torvalds 		rt->rt6i_idev = ort->rt6i_idev;
18031da177e4SLinus Torvalds 		if (rt->rt6i_idev)
18041da177e4SLinus Torvalds 			in6_dev_hold(rt->rt6i_idev);
1805d8d1f30bSChangli Gao 		rt->dst.lastuse = jiffies;
18061da177e4SLinus Torvalds 
18074e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
18081716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
18091716a961SGao feng 		if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
18101716a961SGao feng 		    (RTF_DEFAULT | RTF_ADDRCONF))
18111716a961SGao feng 			rt6_set_from(rt, ort);
18121716a961SGao feng 		else
18131716a961SGao feng 			rt6_clean_expires(rt);
18141da177e4SLinus Torvalds 		rt->rt6i_metric = 0;
18151da177e4SLinus Torvalds 
18161da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
18171da177e4SLinus Torvalds 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
18181da177e4SLinus Torvalds #endif
18190f6c6392SFlorian Westphal 		memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
1820c71099acSThomas Graf 		rt->rt6i_table = ort->rt6i_table;
18211da177e4SLinus Torvalds 	}
18221da177e4SLinus Torvalds 	return rt;
18231da177e4SLinus Torvalds }
18241da177e4SLinus Torvalds 
182570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
1826efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
1827b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1828b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
182970ceb4f5SYOSHIFUJI Hideaki {
183070ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
183170ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
1832c71099acSThomas Graf 	struct fib6_table *table;
183370ceb4f5SYOSHIFUJI Hideaki 
1834efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
183538308473SDavid S. Miller 	if (!table)
1836c71099acSThomas Graf 		return NULL;
1837c71099acSThomas Graf 
1838c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
1839c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
184070ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
184170ceb4f5SYOSHIFUJI Hideaki 		goto out;
184270ceb4f5SYOSHIFUJI Hideaki 
1843d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1844d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
184570ceb4f5SYOSHIFUJI Hideaki 			continue;
184670ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
184770ceb4f5SYOSHIFUJI Hideaki 			continue;
184870ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
184970ceb4f5SYOSHIFUJI Hideaki 			continue;
1850d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
185170ceb4f5SYOSHIFUJI Hideaki 		break;
185270ceb4f5SYOSHIFUJI Hideaki 	}
185370ceb4f5SYOSHIFUJI Hideaki out:
1854c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
185570ceb4f5SYOSHIFUJI Hideaki 	return rt;
185670ceb4f5SYOSHIFUJI Hideaki }
185770ceb4f5SYOSHIFUJI Hideaki 
1858efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
1859b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1860b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
186195c96174SEric Dumazet 					   unsigned int pref)
186270ceb4f5SYOSHIFUJI Hideaki {
186386872cb5SThomas Graf 	struct fib6_config cfg = {
186486872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
1865238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
186686872cb5SThomas Graf 		.fc_ifindex	= ifindex,
186786872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
186886872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
186986872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
1870efa2cea0SDaniel Lezcano 		.fc_nlinfo.pid = 0,
1871efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
1872efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
187386872cb5SThomas Graf 	};
187470ceb4f5SYOSHIFUJI Hideaki 
18754e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
18764e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
187786872cb5SThomas Graf 
1878e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
1879e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
188086872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
188170ceb4f5SYOSHIFUJI Hideaki 
188286872cb5SThomas Graf 	ip6_route_add(&cfg);
188370ceb4f5SYOSHIFUJI Hideaki 
1884efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
188570ceb4f5SYOSHIFUJI Hideaki }
188670ceb4f5SYOSHIFUJI Hideaki #endif
188770ceb4f5SYOSHIFUJI Hideaki 
1888b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
18891da177e4SLinus Torvalds {
18901da177e4SLinus Torvalds 	struct rt6_info *rt;
1891c71099acSThomas Graf 	struct fib6_table *table;
18921da177e4SLinus Torvalds 
1893c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
189438308473SDavid S. Miller 	if (!table)
1895c71099acSThomas Graf 		return NULL;
18961da177e4SLinus Torvalds 
1897c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
1898d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
1899d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
1900045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
19011da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
19021da177e4SLinus Torvalds 			break;
19031da177e4SLinus Torvalds 	}
19041da177e4SLinus Torvalds 	if (rt)
1905d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
1906c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
19071da177e4SLinus Torvalds 	return rt;
19081da177e4SLinus Torvalds }
19091da177e4SLinus Torvalds 
1910b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
1911ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
1912ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
19131da177e4SLinus Torvalds {
191486872cb5SThomas Graf 	struct fib6_config cfg = {
191586872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
1916238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
191786872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
191886872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
191986872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
19205578689aSDaniel Lezcano 		.fc_nlinfo.pid = 0,
19215578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
1922c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
192386872cb5SThomas Graf 	};
19241da177e4SLinus Torvalds 
19254e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
19261da177e4SLinus Torvalds 
192786872cb5SThomas Graf 	ip6_route_add(&cfg);
19281da177e4SLinus Torvalds 
19291da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
19301da177e4SLinus Torvalds }
19311da177e4SLinus Torvalds 
19327b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
19331da177e4SLinus Torvalds {
19341da177e4SLinus Torvalds 	struct rt6_info *rt;
1935c71099acSThomas Graf 	struct fib6_table *table;
1936c71099acSThomas Graf 
1937c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
19387b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
193938308473SDavid S. Miller 	if (!table)
1940c71099acSThomas Graf 		return;
19411da177e4SLinus Torvalds 
19421da177e4SLinus Torvalds restart:
1943c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1944d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
19451da177e4SLinus Torvalds 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
1946d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1947c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
1948e0a1ad73SThomas Graf 			ip6_del_rt(rt);
19491da177e4SLinus Torvalds 			goto restart;
19501da177e4SLinus Torvalds 		}
19511da177e4SLinus Torvalds 	}
1952c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
19531da177e4SLinus Torvalds }
19541da177e4SLinus Torvalds 
19555578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
19565578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
195786872cb5SThomas Graf 				 struct fib6_config *cfg)
195886872cb5SThomas Graf {
195986872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
196086872cb5SThomas Graf 
196186872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
196286872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
196386872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
196486872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
196586872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
196686872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
196786872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
196886872cb5SThomas Graf 
19695578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
1970f1243c2dSBenjamin Thery 
19714e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
19724e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
19734e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
197486872cb5SThomas Graf }
197586872cb5SThomas Graf 
19765578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
19771da177e4SLinus Torvalds {
197886872cb5SThomas Graf 	struct fib6_config cfg;
19791da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
19801da177e4SLinus Torvalds 	int err;
19811da177e4SLinus Torvalds 
19821da177e4SLinus Torvalds 	switch(cmd) {
19831da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
19841da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
19851da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
19861da177e4SLinus Torvalds 			return -EPERM;
19871da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
19881da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
19891da177e4SLinus Torvalds 		if (err)
19901da177e4SLinus Torvalds 			return -EFAULT;
19911da177e4SLinus Torvalds 
19925578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
199386872cb5SThomas Graf 
19941da177e4SLinus Torvalds 		rtnl_lock();
19951da177e4SLinus Torvalds 		switch (cmd) {
19961da177e4SLinus Torvalds 		case SIOCADDRT:
199786872cb5SThomas Graf 			err = ip6_route_add(&cfg);
19981da177e4SLinus Torvalds 			break;
19991da177e4SLinus Torvalds 		case SIOCDELRT:
200086872cb5SThomas Graf 			err = ip6_route_del(&cfg);
20011da177e4SLinus Torvalds 			break;
20021da177e4SLinus Torvalds 		default:
20031da177e4SLinus Torvalds 			err = -EINVAL;
20041da177e4SLinus Torvalds 		}
20051da177e4SLinus Torvalds 		rtnl_unlock();
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds 		return err;
20083ff50b79SStephen Hemminger 	}
20091da177e4SLinus Torvalds 
20101da177e4SLinus Torvalds 	return -EINVAL;
20111da177e4SLinus Torvalds }
20121da177e4SLinus Torvalds 
20131da177e4SLinus Torvalds /*
20141da177e4SLinus Torvalds  *	Drop the packet on the floor
20151da177e4SLinus Torvalds  */
20161da177e4SLinus Torvalds 
2017d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
20181da177e4SLinus Torvalds {
2019612f09e8SYOSHIFUJI Hideaki 	int type;
2020adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2021612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2022612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
20230660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
202445bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
20253bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
20263bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2027612f09e8SYOSHIFUJI Hideaki 			break;
2028612f09e8SYOSHIFUJI Hideaki 		}
2029612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2030612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
20313bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
20323bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2033612f09e8SYOSHIFUJI Hideaki 		break;
2034612f09e8SYOSHIFUJI Hideaki 	}
20353ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
20361da177e4SLinus Torvalds 	kfree_skb(skb);
20371da177e4SLinus Torvalds 	return 0;
20381da177e4SLinus Torvalds }
20391da177e4SLinus Torvalds 
20409ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
20419ce8ade0SThomas Graf {
2042612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
20439ce8ade0SThomas Graf }
20449ce8ade0SThomas Graf 
204520380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb)
20461da177e4SLinus Torvalds {
2047adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2048612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
20491da177e4SLinus Torvalds }
20501da177e4SLinus Torvalds 
20516723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES
20526723ab54SDavid S. Miller 
20539ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
20549ce8ade0SThomas Graf {
2055612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
20569ce8ade0SThomas Graf }
20579ce8ade0SThomas Graf 
20589ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb)
20599ce8ade0SThomas Graf {
2060adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2061612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
20629ce8ade0SThomas Graf }
20639ce8ade0SThomas Graf 
20646723ab54SDavid S. Miller #endif
20656723ab54SDavid S. Miller 
20661da177e4SLinus Torvalds /*
20671da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
20681da177e4SLinus Torvalds  */
20691da177e4SLinus Torvalds 
20701da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
20711da177e4SLinus Torvalds 				    const struct in6_addr *addr,
20728f031519SDavid S. Miller 				    bool anycast)
20731da177e4SLinus Torvalds {
2074c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
20758b96d22dSDavid S. Miller 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
2076f83c7790SDavid S. Miller 	int err;
20771da177e4SLinus Torvalds 
207838308473SDavid S. Miller 	if (!rt) {
2079f3213831SJoe Perches 		net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
20801da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
208140385653SBen Greear 	}
20821da177e4SLinus Torvalds 
20831da177e4SLinus Torvalds 	in6_dev_hold(idev);
20841da177e4SLinus Torvalds 
208511d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2086d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2087d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
20881da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
20891da177e4SLinus Torvalds 
20901da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
209158c4fb86SYOSHIFUJI Hideaki 	if (anycast)
209258c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
209358c4fb86SYOSHIFUJI Hideaki 	else
20941da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
20958ade06c6SDavid S. Miller 	err = rt6_bind_neighbour(rt, rt->dst.dev);
2096f83c7790SDavid S. Miller 	if (err) {
2097d8d1f30bSChangli Gao 		dst_free(&rt->dst);
2098f83c7790SDavid S. Miller 		return ERR_PTR(err);
20991da177e4SLinus Torvalds 	}
21001da177e4SLinus Torvalds 
21014e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
21021da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
21035578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
21041da177e4SLinus Torvalds 
2105d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
21061da177e4SLinus Torvalds 
21071da177e4SLinus Torvalds 	return rt;
21081da177e4SLinus Torvalds }
21091da177e4SLinus Torvalds 
2110c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2111c3968a85SDaniel Walter 			struct rt6_info *rt,
2112b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2113c3968a85SDaniel Walter 			unsigned int prefs,
2114c3968a85SDaniel Walter 			struct in6_addr *saddr)
2115c3968a85SDaniel Walter {
2116c3968a85SDaniel Walter 	struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2117c3968a85SDaniel Walter 	int err = 0;
2118c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen)
21194e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2120c3968a85SDaniel Walter 	else
2121c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2122c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2123c3968a85SDaniel Walter 	return err;
2124c3968a85SDaniel Walter }
2125c3968a85SDaniel Walter 
2126c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2127c3968a85SDaniel Walter struct arg_dev_net_ip {
2128c3968a85SDaniel Walter 	struct net_device *dev;
2129c3968a85SDaniel Walter 	struct net *net;
2130c3968a85SDaniel Walter 	struct in6_addr *addr;
2131c3968a85SDaniel Walter };
2132c3968a85SDaniel Walter 
2133c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2134c3968a85SDaniel Walter {
2135c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2136c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2137c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2138c3968a85SDaniel Walter 
2139d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2140c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2141c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2142c3968a85SDaniel Walter 		/* remove prefsrc entry */
2143c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2144c3968a85SDaniel Walter 	}
2145c3968a85SDaniel Walter 	return 0;
2146c3968a85SDaniel Walter }
2147c3968a85SDaniel Walter 
2148c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2149c3968a85SDaniel Walter {
2150c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2151c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2152c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2153c3968a85SDaniel Walter 		.net = net,
2154c3968a85SDaniel Walter 		.addr = &ifp->addr,
2155c3968a85SDaniel Walter 	};
2156c3968a85SDaniel Walter 	fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2157c3968a85SDaniel Walter }
2158c3968a85SDaniel Walter 
21598ed67789SDaniel Lezcano struct arg_dev_net {
21608ed67789SDaniel Lezcano 	struct net_device *dev;
21618ed67789SDaniel Lezcano 	struct net *net;
21628ed67789SDaniel Lezcano };
21638ed67789SDaniel Lezcano 
21641da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
21651da177e4SLinus Torvalds {
2166bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2167bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
21688ed67789SDaniel Lezcano 
2169d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2170c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
21711da177e4SLinus Torvalds 		return -1;
2172c159d30cSDavid S. Miller 
21731da177e4SLinus Torvalds 	return 0;
21741da177e4SLinus Torvalds }
21751da177e4SLinus Torvalds 
2176f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
21771da177e4SLinus Torvalds {
21788ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
21798ed67789SDaniel Lezcano 		.dev = dev,
21808ed67789SDaniel Lezcano 		.net = net,
21818ed67789SDaniel Lezcano 	};
21828ed67789SDaniel Lezcano 
21838ed67789SDaniel Lezcano 	fib6_clean_all(net, fib6_ifdown, 0, &adn);
21841e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
21851da177e4SLinus Torvalds }
21861da177e4SLinus Torvalds 
218795c96174SEric Dumazet struct rt6_mtu_change_arg {
21881da177e4SLinus Torvalds 	struct net_device *dev;
218995c96174SEric Dumazet 	unsigned int mtu;
21901da177e4SLinus Torvalds };
21911da177e4SLinus Torvalds 
21921da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
21931da177e4SLinus Torvalds {
21941da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
21951da177e4SLinus Torvalds 	struct inet6_dev *idev;
21961da177e4SLinus Torvalds 
21971da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
21981da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
21991da177e4SLinus Torvalds 	   We still use this lock to block changes
22001da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
22011da177e4SLinus Torvalds 	*/
22021da177e4SLinus Torvalds 
22031da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
220438308473SDavid S. Miller 	if (!idev)
22051da177e4SLinus Torvalds 		return 0;
22061da177e4SLinus Torvalds 
22071da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
22081da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
22091da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
22101da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
22111da177e4SLinus Torvalds 	 */
22121da177e4SLinus Torvalds 	/*
22131da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
22141da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
22151da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
22161da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
22171da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
22181da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
22191da177e4SLinus Torvalds 	   PMTU discouvery.
22201da177e4SLinus Torvalds 	 */
2221d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
2222d8d1f30bSChangli Gao 	    !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2223d8d1f30bSChangli Gao 	    (dst_mtu(&rt->dst) >= arg->mtu ||
2224d8d1f30bSChangli Gao 	     (dst_mtu(&rt->dst) < arg->mtu &&
2225d8d1f30bSChangli Gao 	      dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
2226defb3519SDavid S. Miller 		dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2227566cfd8fSSimon Arlott 	}
22281da177e4SLinus Torvalds 	return 0;
22291da177e4SLinus Torvalds }
22301da177e4SLinus Torvalds 
223195c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
22321da177e4SLinus Torvalds {
2233c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2234c71099acSThomas Graf 		.dev = dev,
2235c71099acSThomas Graf 		.mtu = mtu,
2236c71099acSThomas Graf 	};
22371da177e4SLinus Torvalds 
2238c346dca1SYOSHIFUJI Hideaki 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
22391da177e4SLinus Torvalds }
22401da177e4SLinus Torvalds 
2241ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
22425176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
224386872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2244ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
224586872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
224686872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
224786872cb5SThomas Graf };
224886872cb5SThomas Graf 
224986872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
225086872cb5SThomas Graf 			      struct fib6_config *cfg)
22511da177e4SLinus Torvalds {
225286872cb5SThomas Graf 	struct rtmsg *rtm;
225386872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
225486872cb5SThomas Graf 	int err;
22551da177e4SLinus Torvalds 
225686872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
225786872cb5SThomas Graf 	if (err < 0)
225886872cb5SThomas Graf 		goto errout;
22591da177e4SLinus Torvalds 
226086872cb5SThomas Graf 	err = -EINVAL;
226186872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
226286872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
226386872cb5SThomas Graf 
226486872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
226586872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
226686872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
226786872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
226886872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
226986872cb5SThomas Graf 
227086872cb5SThomas Graf 	if (rtm->rtm_type == RTN_UNREACHABLE)
227186872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
227286872cb5SThomas Graf 
2273ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2274ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2275ab79ad14SMaciej Żenczykowski 
227686872cb5SThomas Graf 	cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
227786872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
22783b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
227986872cb5SThomas Graf 
228086872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
228186872cb5SThomas Graf 		nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
228286872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
22831da177e4SLinus Torvalds 	}
228486872cb5SThomas Graf 
228586872cb5SThomas Graf 	if (tb[RTA_DST]) {
228686872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
228786872cb5SThomas Graf 
228886872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
228986872cb5SThomas Graf 			goto errout;
229086872cb5SThomas Graf 
229186872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
22921da177e4SLinus Torvalds 	}
229386872cb5SThomas Graf 
229486872cb5SThomas Graf 	if (tb[RTA_SRC]) {
229586872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
229686872cb5SThomas Graf 
229786872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
229886872cb5SThomas Graf 			goto errout;
229986872cb5SThomas Graf 
230086872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
23011da177e4SLinus Torvalds 	}
230286872cb5SThomas Graf 
2303c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
2304c3968a85SDaniel Walter 		nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2305c3968a85SDaniel Walter 
230686872cb5SThomas Graf 	if (tb[RTA_OIF])
230786872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
230886872cb5SThomas Graf 
230986872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
231086872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
231186872cb5SThomas Graf 
231286872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
231386872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
231486872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
23151da177e4SLinus Torvalds 	}
231686872cb5SThomas Graf 
231786872cb5SThomas Graf 	if (tb[RTA_TABLE])
231886872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
231986872cb5SThomas Graf 
232086872cb5SThomas Graf 	err = 0;
232186872cb5SThomas Graf errout:
232286872cb5SThomas Graf 	return err;
23231da177e4SLinus Torvalds }
23241da177e4SLinus Torvalds 
2325c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
23261da177e4SLinus Torvalds {
232786872cb5SThomas Graf 	struct fib6_config cfg;
232886872cb5SThomas Graf 	int err;
23291da177e4SLinus Torvalds 
233086872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
233186872cb5SThomas Graf 	if (err < 0)
233286872cb5SThomas Graf 		return err;
233386872cb5SThomas Graf 
233486872cb5SThomas Graf 	return ip6_route_del(&cfg);
23351da177e4SLinus Torvalds }
23361da177e4SLinus Torvalds 
2337c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
23381da177e4SLinus Torvalds {
233986872cb5SThomas Graf 	struct fib6_config cfg;
234086872cb5SThomas Graf 	int err;
23411da177e4SLinus Torvalds 
234286872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
234386872cb5SThomas Graf 	if (err < 0)
234486872cb5SThomas Graf 		return err;
234586872cb5SThomas Graf 
234686872cb5SThomas Graf 	return ip6_route_add(&cfg);
23471da177e4SLinus Torvalds }
23481da177e4SLinus Torvalds 
2349339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void)
2350339bf98fSThomas Graf {
2351339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2352339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2353339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2354339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2355339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2356339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2357339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2358339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2359339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
23606a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2361339bf98fSThomas Graf 	       + nla_total_size(sizeof(struct rta_cacheinfo));
2362339bf98fSThomas Graf }
2363339bf98fSThomas Graf 
2364191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2365191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
23660d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
23670d51aa80SJamal Hadi Salim 			 int iif, int type, u32 pid, u32 seq,
23687bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
23691da177e4SLinus Torvalds {
23701da177e4SLinus Torvalds 	struct rtmsg *rtm;
23711da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2372e3703b3dSThomas Graf 	long expires;
23739e762a4aSPatrick McHardy 	u32 table;
2374f2c31e32SEric Dumazet 	struct neighbour *n;
23751da177e4SLinus Torvalds 
23761da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
23771da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
23781da177e4SLinus Torvalds 			/* success since this is not a prefix route */
23791da177e4SLinus Torvalds 			return 1;
23801da177e4SLinus Torvalds 		}
23811da177e4SLinus Torvalds 	}
23821da177e4SLinus Torvalds 
23832d7202bfSThomas Graf 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
238438308473SDavid S. Miller 	if (!nlh)
238526932566SPatrick McHardy 		return -EMSGSIZE;
23862d7202bfSThomas Graf 
23872d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
23881da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
23891da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
23901da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
23911da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2392c71099acSThomas Graf 	if (rt->rt6i_table)
23939e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2394c71099acSThomas Graf 	else
23959e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
23969e762a4aSPatrick McHardy 	rtm->rtm_table = table;
2397c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
2398c78679e8SDavid S. Miller 		goto nla_put_failure;
23991da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_REJECT)
24001da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNREACHABLE;
2401ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2402ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2403d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
24041da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
24051da177e4SLinus Torvalds 	else
24061da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
24071da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
24081da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
24091da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
24101da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
24111da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
2412f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
2413f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
24141da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
2415f0396f60SDenis Ovsienko 		else
2416f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
2417f0396f60SDenis Ovsienko 	}
24181da177e4SLinus Torvalds 
24191da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
24201da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
24211da177e4SLinus Torvalds 
24221da177e4SLinus Torvalds 	if (dst) {
2423c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_DST, 16, dst))
2424c78679e8SDavid S. Miller 			goto nla_put_failure;
24251da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
24261da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
2427c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2428c78679e8SDavid S. Miller 			goto nla_put_failure;
24291da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
24301da177e4SLinus Torvalds 	if (src) {
2431c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_SRC, 16, src))
2432c78679e8SDavid S. Miller 			goto nla_put_failure;
24331da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
2434c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
2435c78679e8SDavid S. Miller 		   nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2436c78679e8SDavid S. Miller 		goto nla_put_failure;
24371da177e4SLinus Torvalds #endif
24387bc570c8SYOSHIFUJI Hideaki 	if (iif) {
24397bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
24407bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
24418229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
24427bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
24437bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
24447bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
24457bc570c8SYOSHIFUJI Hideaki 						return 0;
24467bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
24477bc570c8SYOSHIFUJI Hideaki 				} else {
24487bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
24497bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
24507bc570c8SYOSHIFUJI Hideaki 				}
24517bc570c8SYOSHIFUJI Hideaki 			}
24527bc570c8SYOSHIFUJI Hideaki 		} else
24537bc570c8SYOSHIFUJI Hideaki #endif
2454c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
2455c78679e8SDavid S. Miller 				goto nla_put_failure;
24567bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
24571da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2458c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2459c78679e8SDavid S. Miller 		    nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2460c78679e8SDavid S. Miller 			goto nla_put_failure;
2461c3968a85SDaniel Walter 	}
2462c3968a85SDaniel Walter 
2463c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2464c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
24654e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
2466c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2467c78679e8SDavid S. Miller 			goto nla_put_failure;
24681da177e4SLinus Torvalds 	}
24692d7202bfSThomas Graf 
2470defb3519SDavid S. Miller 	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
24712d7202bfSThomas Graf 		goto nla_put_failure;
24722d7202bfSThomas Graf 
2473f2c31e32SEric Dumazet 	rcu_read_lock();
247497cac082SDavid S. Miller 	n = rt->n;
247594f826b8SEric Dumazet 	if (n) {
247694f826b8SEric Dumazet 		if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
247794f826b8SEric Dumazet 			rcu_read_unlock();
247894f826b8SEric Dumazet 			goto nla_put_failure;
247994f826b8SEric Dumazet 		}
248094f826b8SEric Dumazet 	}
2481f2c31e32SEric Dumazet 	rcu_read_unlock();
24822d7202bfSThomas Graf 
2483c78679e8SDavid S. Miller 	if (rt->dst.dev &&
2484c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2485c78679e8SDavid S. Miller 		goto nla_put_failure;
2486c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2487c78679e8SDavid S. Miller 		goto nla_put_failure;
24888253947eSLi Wei 
24898253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
249069cdf8f9SYOSHIFUJI Hideaki 
249187a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
2492e3703b3dSThomas Graf 		goto nla_put_failure;
24931da177e4SLinus Torvalds 
24942d7202bfSThomas Graf 	return nlmsg_end(skb, nlh);
24952d7202bfSThomas Graf 
24962d7202bfSThomas Graf nla_put_failure:
249726932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
249826932566SPatrick McHardy 	return -EMSGSIZE;
24991da177e4SLinus Torvalds }
25001da177e4SLinus Torvalds 
25011b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
25021da177e4SLinus Torvalds {
25031da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
25041da177e4SLinus Torvalds 	int prefix;
25051da177e4SLinus Torvalds 
25062d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
25072d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
25081da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
25091da177e4SLinus Torvalds 	} else
25101da177e4SLinus Torvalds 		prefix = 0;
25111da177e4SLinus Torvalds 
2512191cd582SBrian Haley 	return rt6_fill_node(arg->net,
2513191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
25141da177e4SLinus Torvalds 		     NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
25157bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
25161da177e4SLinus Torvalds }
25171da177e4SLinus Torvalds 
2518c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
25191da177e4SLinus Torvalds {
25203b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
2521ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
25221da177e4SLinus Torvalds 	struct rt6_info *rt;
2523ab364a6fSThomas Graf 	struct sk_buff *skb;
2524ab364a6fSThomas Graf 	struct rtmsg *rtm;
25254c9483b2SDavid S. Miller 	struct flowi6 fl6;
252672331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
2527ab364a6fSThomas Graf 
2528ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2529ab364a6fSThomas Graf 	if (err < 0)
2530ab364a6fSThomas Graf 		goto errout;
2531ab364a6fSThomas Graf 
2532ab364a6fSThomas Graf 	err = -EINVAL;
25334c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2534ab364a6fSThomas Graf 
2535ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
2536ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2537ab364a6fSThomas Graf 			goto errout;
2538ab364a6fSThomas Graf 
25394e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
2540ab364a6fSThomas Graf 	}
2541ab364a6fSThomas Graf 
2542ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
2543ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2544ab364a6fSThomas Graf 			goto errout;
2545ab364a6fSThomas Graf 
25464e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
2547ab364a6fSThomas Graf 	}
2548ab364a6fSThomas Graf 
2549ab364a6fSThomas Graf 	if (tb[RTA_IIF])
2550ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
2551ab364a6fSThomas Graf 
2552ab364a6fSThomas Graf 	if (tb[RTA_OIF])
255372331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
2554ab364a6fSThomas Graf 
2555ab364a6fSThomas Graf 	if (iif) {
2556ab364a6fSThomas Graf 		struct net_device *dev;
255772331bc0SShmulik Ladkani 		int flags = 0;
255872331bc0SShmulik Ladkani 
25595578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
2560ab364a6fSThomas Graf 		if (!dev) {
2561ab364a6fSThomas Graf 			err = -ENODEV;
2562ab364a6fSThomas Graf 			goto errout;
2563ab364a6fSThomas Graf 		}
256472331bc0SShmulik Ladkani 
256572331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
256672331bc0SShmulik Ladkani 
256772331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
256872331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
256972331bc0SShmulik Ladkani 
257072331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
257172331bc0SShmulik Ladkani 							       flags);
257272331bc0SShmulik Ladkani 	} else {
257372331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
257472331bc0SShmulik Ladkani 
257572331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
2576ab364a6fSThomas Graf 	}
25771da177e4SLinus Torvalds 
25781da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
257938308473SDavid S. Miller 	if (!skb) {
25802173bff5SShmulik Ladkani 		dst_release(&rt->dst);
2581ab364a6fSThomas Graf 		err = -ENOBUFS;
2582ab364a6fSThomas Graf 		goto errout;
2583ab364a6fSThomas Graf 	}
25841da177e4SLinus Torvalds 
25851da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
25861da177e4SLinus Torvalds 	   through good chunk of routing engine.
25871da177e4SLinus Torvalds 	 */
2588459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
25891da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
25901da177e4SLinus Torvalds 
2591d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
25921da177e4SLinus Torvalds 
25934c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
25941da177e4SLinus Torvalds 			    RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
25957bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
25961da177e4SLinus Torvalds 	if (err < 0) {
2597ab364a6fSThomas Graf 		kfree_skb(skb);
2598ab364a6fSThomas Graf 		goto errout;
25991da177e4SLinus Torvalds 	}
26001da177e4SLinus Torvalds 
26015578689aSDaniel Lezcano 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
2602ab364a6fSThomas Graf errout:
26031da177e4SLinus Torvalds 	return err;
26041da177e4SLinus Torvalds }
26051da177e4SLinus Torvalds 
260686872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
26071da177e4SLinus Torvalds {
26081da177e4SLinus Torvalds 	struct sk_buff *skb;
26095578689aSDaniel Lezcano 	struct net *net = info->nl_net;
2610528c4cebSDenis V. Lunev 	u32 seq;
2611528c4cebSDenis V. Lunev 	int err;
26120d51aa80SJamal Hadi Salim 
2613528c4cebSDenis V. Lunev 	err = -ENOBUFS;
261438308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
261586872cb5SThomas Graf 
2616339bf98fSThomas Graf 	skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
261738308473SDavid S. Miller 	if (!skb)
261821713ebcSThomas Graf 		goto errout;
26191da177e4SLinus Torvalds 
2620191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
26217bc570c8SYOSHIFUJI Hideaki 				event, info->pid, seq, 0, 0, 0);
262226932566SPatrick McHardy 	if (err < 0) {
262326932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
262426932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
262526932566SPatrick McHardy 		kfree_skb(skb);
262626932566SPatrick McHardy 		goto errout;
262726932566SPatrick McHardy 	}
26281ce85fe4SPablo Neira Ayuso 	rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
26295578689aSDaniel Lezcano 		    info->nlh, gfp_any());
26301ce85fe4SPablo Neira Ayuso 	return;
263121713ebcSThomas Graf errout:
263221713ebcSThomas Graf 	if (err < 0)
26335578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
26341da177e4SLinus Torvalds }
26351da177e4SLinus Torvalds 
26368ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
26378ed67789SDaniel Lezcano 				unsigned long event, void *data)
26388ed67789SDaniel Lezcano {
26398ed67789SDaniel Lezcano 	struct net_device *dev = (struct net_device *)data;
2640c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
26418ed67789SDaniel Lezcano 
26428ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
2643d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
26448ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
26458ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2646d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
26478ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
2648d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
26498ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
26508ed67789SDaniel Lezcano #endif
26518ed67789SDaniel Lezcano 	}
26528ed67789SDaniel Lezcano 
26538ed67789SDaniel Lezcano 	return NOTIFY_OK;
26548ed67789SDaniel Lezcano }
26558ed67789SDaniel Lezcano 
26561da177e4SLinus Torvalds /*
26571da177e4SLinus Torvalds  *	/proc
26581da177e4SLinus Torvalds  */
26591da177e4SLinus Torvalds 
26601da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
26611da177e4SLinus Torvalds 
26621da177e4SLinus Torvalds struct rt6_proc_arg
26631da177e4SLinus Torvalds {
26641da177e4SLinus Torvalds 	char *buffer;
26651da177e4SLinus Torvalds 	int offset;
26661da177e4SLinus Torvalds 	int length;
26671da177e4SLinus Torvalds 	int skip;
26681da177e4SLinus Torvalds 	int len;
26691da177e4SLinus Torvalds };
26701da177e4SLinus Torvalds 
26711da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg)
26721da177e4SLinus Torvalds {
267333120b30SAlexey Dobriyan 	struct seq_file *m = p_arg;
267469cce1d1SDavid S. Miller 	struct neighbour *n;
26751da177e4SLinus Torvalds 
26764b7a4274SHarvey Harrison 	seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
26771da177e4SLinus Torvalds 
26781da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
26794b7a4274SHarvey Harrison 	seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
26801da177e4SLinus Torvalds #else
268133120b30SAlexey Dobriyan 	seq_puts(m, "00000000000000000000000000000000 00 ");
26821da177e4SLinus Torvalds #endif
2683f2c31e32SEric Dumazet 	rcu_read_lock();
268497cac082SDavid S. Miller 	n = rt->n;
268569cce1d1SDavid S. Miller 	if (n) {
268669cce1d1SDavid S. Miller 		seq_printf(m, "%pi6", n->primary_key);
26871da177e4SLinus Torvalds 	} else {
268833120b30SAlexey Dobriyan 		seq_puts(m, "00000000000000000000000000000000");
26891da177e4SLinus Torvalds 	}
2690f2c31e32SEric Dumazet 	rcu_read_unlock();
269133120b30SAlexey Dobriyan 	seq_printf(m, " %08x %08x %08x %08x %8s\n",
2692d8d1f30bSChangli Gao 		   rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2693d8d1f30bSChangli Gao 		   rt->dst.__use, rt->rt6i_flags,
2694d1918542SDavid S. Miller 		   rt->dst.dev ? rt->dst.dev->name : "");
26951da177e4SLinus Torvalds 	return 0;
26961da177e4SLinus Torvalds }
26971da177e4SLinus Torvalds 
269833120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v)
26991da177e4SLinus Torvalds {
2700f3db4851SDaniel Lezcano 	struct net *net = (struct net *)m->private;
270132b293a5SJosh Hunt 	fib6_clean_all_ro(net, rt6_info_route, 0, m);
270233120b30SAlexey Dobriyan 	return 0;
27031da177e4SLinus Torvalds }
27041da177e4SLinus Torvalds 
270533120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file)
270633120b30SAlexey Dobriyan {
2707de05c557SPavel Emelyanov 	return single_open_net(inode, file, ipv6_route_show);
2708f3db4851SDaniel Lezcano }
2709f3db4851SDaniel Lezcano 
271033120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
271133120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
271233120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
271333120b30SAlexey Dobriyan 	.read		= seq_read,
271433120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
2715b6fcbdb4SPavel Emelyanov 	.release	= single_release_net,
271633120b30SAlexey Dobriyan };
271733120b30SAlexey Dobriyan 
27181da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
27191da177e4SLinus Torvalds {
272069ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
27211da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
272269ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
272369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
272469ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
272569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
272669ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
2727fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
272869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
27291da177e4SLinus Torvalds 
27301da177e4SLinus Torvalds 	return 0;
27311da177e4SLinus Torvalds }
27321da177e4SLinus Torvalds 
27331da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
27341da177e4SLinus Torvalds {
2735de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
273669ddb805SDaniel Lezcano }
273769ddb805SDaniel Lezcano 
27389a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
27391da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
27401da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
27411da177e4SLinus Torvalds 	.read	 = seq_read,
27421da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
2743b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
27441da177e4SLinus Torvalds };
27451da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
27461da177e4SLinus Torvalds 
27471da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
27481da177e4SLinus Torvalds 
27491da177e4SLinus Torvalds static
27508d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
27511da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
27521da177e4SLinus Torvalds {
2753c486da34SLucian Adrian Grijincu 	struct net *net;
2754c486da34SLucian Adrian Grijincu 	int delay;
2755c486da34SLucian Adrian Grijincu 	if (!write)
2756c486da34SLucian Adrian Grijincu 		return -EINVAL;
2757c486da34SLucian Adrian Grijincu 
2758c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
2759c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
27608d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
27615b7c931dSDaniel Lezcano 	fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
27621da177e4SLinus Torvalds 	return 0;
27631da177e4SLinus Torvalds }
27641da177e4SLinus Torvalds 
2765760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = {
27661da177e4SLinus Torvalds 	{
27671da177e4SLinus Torvalds 		.procname	=	"flush",
27684990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
27691da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
277089c8b3a1SDave Jones 		.mode		=	0200,
27716d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
27721da177e4SLinus Torvalds 	},
27731da177e4SLinus Torvalds 	{
27741da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
27759a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
27761da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27771da177e4SLinus Torvalds 		.mode		=	0644,
27786d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
27791da177e4SLinus Torvalds 	},
27801da177e4SLinus Torvalds 	{
27811da177e4SLinus Torvalds 		.procname	=	"max_size",
27824990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
27831da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27841da177e4SLinus Torvalds 		.mode		=	0644,
27856d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
27861da177e4SLinus Torvalds 	},
27871da177e4SLinus Torvalds 	{
27881da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
27894990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
27901da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27911da177e4SLinus Torvalds 		.mode		=	0644,
27926d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
27931da177e4SLinus Torvalds 	},
27941da177e4SLinus Torvalds 	{
27951da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
27964990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
27971da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27981da177e4SLinus Torvalds 		.mode		=	0644,
27996d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28001da177e4SLinus Torvalds 	},
28011da177e4SLinus Torvalds 	{
28021da177e4SLinus Torvalds 		.procname	=	"gc_interval",
28034990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
28041da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28051da177e4SLinus Torvalds 		.mode		=	0644,
28066d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28071da177e4SLinus Torvalds 	},
28081da177e4SLinus Torvalds 	{
28091da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
28104990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
28111da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28121da177e4SLinus Torvalds 		.mode		=	0644,
2813f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
28141da177e4SLinus Torvalds 	},
28151da177e4SLinus Torvalds 	{
28161da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
28174990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
28181da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28191da177e4SLinus Torvalds 		.mode		=	0644,
28206d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28211da177e4SLinus Torvalds 	},
28221da177e4SLinus Torvalds 	{
28231da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
28244990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
28251da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28261da177e4SLinus Torvalds 		.mode		=	0644,
2827f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
28281da177e4SLinus Torvalds 	},
28291da177e4SLinus Torvalds 	{
28301da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
28314990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
28321da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28331da177e4SLinus Torvalds 		.mode		=	0644,
28346d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
28351da177e4SLinus Torvalds 	},
2836f8572d8fSEric W. Biederman 	{ }
28371da177e4SLinus Torvalds };
28381da177e4SLinus Torvalds 
28392c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
2840760f2d01SDaniel Lezcano {
2841760f2d01SDaniel Lezcano 	struct ctl_table *table;
2842760f2d01SDaniel Lezcano 
2843760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
2844760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
2845760f2d01SDaniel Lezcano 			GFP_KERNEL);
28465ee09105SYOSHIFUJI Hideaki 
28475ee09105SYOSHIFUJI Hideaki 	if (table) {
28485ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
2849c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
285086393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
28515ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
28525ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
28535ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
28545ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
28555ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
28565ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
28575ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
28589c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
28595ee09105SYOSHIFUJI Hideaki 	}
28605ee09105SYOSHIFUJI Hideaki 
2861760f2d01SDaniel Lezcano 	return table;
2862760f2d01SDaniel Lezcano }
28631da177e4SLinus Torvalds #endif
28641da177e4SLinus Torvalds 
28652c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
2866cdb18761SDaniel Lezcano {
2867633d424bSPavel Emelyanov 	int ret = -ENOMEM;
28688ed67789SDaniel Lezcano 
286986393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
287086393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
2871f2fc6a54SBenjamin Thery 
2872fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2873fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
2874fc66f95cSEric Dumazet 
28758ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
28768ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
28778ed67789SDaniel Lezcano 					   GFP_KERNEL);
28788ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
2879fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
2880d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
28818ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
2882d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
288362fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
288462fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
28858ed67789SDaniel Lezcano 
28868ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
28878ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
28888ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
28898ed67789SDaniel Lezcano 					       GFP_KERNEL);
289068fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
289168fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
2892d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
28938ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
2894d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
289562fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
289662fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
28978ed67789SDaniel Lezcano 
28988ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
28998ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
29008ed67789SDaniel Lezcano 					       GFP_KERNEL);
290168fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
290268fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
2903d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
29048ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
2905d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
290662fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
290762fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
29088ed67789SDaniel Lezcano #endif
29098ed67789SDaniel Lezcano 
2910b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
2911b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
2912b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2913b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2914b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2915b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2916b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2917b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2918b339a47cSPeter Zijlstra 
29196891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
29206891a346SBenjamin Thery 
29218ed67789SDaniel Lezcano 	ret = 0;
29228ed67789SDaniel Lezcano out:
29238ed67789SDaniel Lezcano 	return ret;
2924f2fc6a54SBenjamin Thery 
292568fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
292668fffc67SPeter Zijlstra out_ip6_prohibit_entry:
292768fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
292868fffc67SPeter Zijlstra out_ip6_null_entry:
292968fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
293068fffc67SPeter Zijlstra #endif
2931fc66f95cSEric Dumazet out_ip6_dst_entries:
2932fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
2933f2fc6a54SBenjamin Thery out_ip6_dst_ops:
2934f2fc6a54SBenjamin Thery 	goto out;
2935cdb18761SDaniel Lezcano }
2936cdb18761SDaniel Lezcano 
29372c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
2938cdb18761SDaniel Lezcano {
29398ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
29408ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
29418ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
29428ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
29438ed67789SDaniel Lezcano #endif
294441bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
2945cdb18761SDaniel Lezcano }
2946cdb18761SDaniel Lezcano 
2947d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
2948d189634eSThomas Graf {
2949d189634eSThomas Graf #ifdef CONFIG_PROC_FS
2950d189634eSThomas Graf 	proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2951d189634eSThomas Graf 	proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2952d189634eSThomas Graf #endif
2953d189634eSThomas Graf 	return 0;
2954d189634eSThomas Graf }
2955d189634eSThomas Graf 
2956d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
2957d189634eSThomas Graf {
2958d189634eSThomas Graf #ifdef CONFIG_PROC_FS
2959d189634eSThomas Graf 	proc_net_remove(net, "ipv6_route");
2960d189634eSThomas Graf 	proc_net_remove(net, "rt6_stats");
2961d189634eSThomas Graf #endif
2962d189634eSThomas Graf }
2963d189634eSThomas Graf 
2964cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
2965cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
2966cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
2967cdb18761SDaniel Lezcano };
2968cdb18761SDaniel Lezcano 
2969c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
2970c3426b47SDavid S. Miller {
2971c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2972c3426b47SDavid S. Miller 
2973c3426b47SDavid S. Miller 	if (!bp)
2974c3426b47SDavid S. Miller 		return -ENOMEM;
2975c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
2976c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
2977c3426b47SDavid S. Miller 	return 0;
2978c3426b47SDavid S. Miller }
2979c3426b47SDavid S. Miller 
2980c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
2981c3426b47SDavid S. Miller {
2982c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
2983c3426b47SDavid S. Miller 
2984c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
298556a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
2986c3426b47SDavid S. Miller 	kfree(bp);
2987c3426b47SDavid S. Miller }
2988c3426b47SDavid S. Miller 
29892b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
2990c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
2991c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
2992c3426b47SDavid S. Miller };
2993c3426b47SDavid S. Miller 
2994d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
2995d189634eSThomas Graf 	.init = ip6_route_net_init_late,
2996d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
2997d189634eSThomas Graf };
2998d189634eSThomas Graf 
29998ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
30008ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
30018ed67789SDaniel Lezcano 	.priority = 0,
30028ed67789SDaniel Lezcano };
30038ed67789SDaniel Lezcano 
3004433d49c3SDaniel Lezcano int __init ip6_route_init(void)
30051da177e4SLinus Torvalds {
3006433d49c3SDaniel Lezcano 	int ret;
3007433d49c3SDaniel Lezcano 
30089a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
30099a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
30109a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
30119a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
30129a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3013c19a28e1SFernando Carrijo 		goto out;
301414e50e57SDavid S. Miller 
3015fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
30168ed67789SDaniel Lezcano 	if (ret)
3017bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3018bdb3289fSDaniel Lezcano 
3019c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3020c3426b47SDavid S. Miller 	if (ret)
3021e8803b6cSDavid S. Miller 		goto out_dst_entries;
30222a0c451aSThomas Graf 
30237e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
30247e52b33bSDavid S. Miller 	if (ret)
30257e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3026c3426b47SDavid S. Miller 
30275dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
30285dc121e9SArnaud Ebalard 
30298ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
30308ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
30318ed67789SDaniel Lezcano 	 * manually for init_net */
3032d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
30338ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3034bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3035d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
30368ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3037d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
30388ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3039bdb3289fSDaniel Lezcano   #endif
3040e8803b6cSDavid S. Miller 	ret = fib6_init();
3041433d49c3SDaniel Lezcano 	if (ret)
30428ed67789SDaniel Lezcano 		goto out_register_subsys;
3043433d49c3SDaniel Lezcano 
3044433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3045433d49c3SDaniel Lezcano 	if (ret)
3046e8803b6cSDavid S. Miller 		goto out_fib6_init;
3047c35b7e72SDaniel Lezcano 
3048433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3049433d49c3SDaniel Lezcano 	if (ret)
3050433d49c3SDaniel Lezcano 		goto xfrm6_init;
30517e5449c2SDaniel Lezcano 
3052d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3053d189634eSThomas Graf 	if (ret)
3054d189634eSThomas Graf 		goto fib6_rules_init;
3055d189634eSThomas Graf 
3056433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3057c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3058c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3059c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3060d189634eSThomas Graf 		goto out_register_late_subsys;
3061433d49c3SDaniel Lezcano 
30628ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3063cdb18761SDaniel Lezcano 	if (ret)
3064d189634eSThomas Graf 		goto out_register_late_subsys;
30658ed67789SDaniel Lezcano 
3066433d49c3SDaniel Lezcano out:
3067433d49c3SDaniel Lezcano 	return ret;
3068433d49c3SDaniel Lezcano 
3069d189634eSThomas Graf out_register_late_subsys:
3070d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3071433d49c3SDaniel Lezcano fib6_rules_init:
3072433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3073433d49c3SDaniel Lezcano xfrm6_init:
3074433d49c3SDaniel Lezcano 	xfrm6_fini();
30752a0c451aSThomas Graf out_fib6_init:
30762a0c451aSThomas Graf 	fib6_gc_cleanup();
30778ed67789SDaniel Lezcano out_register_subsys:
30788ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
30797e52b33bSDavid S. Miller out_register_inetpeer:
30807e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3081fc66f95cSEric Dumazet out_dst_entries:
3082fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3083433d49c3SDaniel Lezcano out_kmem_cache:
3084f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3085433d49c3SDaniel Lezcano 	goto out;
30861da177e4SLinus Torvalds }
30871da177e4SLinus Torvalds 
30881da177e4SLinus Torvalds void ip6_route_cleanup(void)
30891da177e4SLinus Torvalds {
30908ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3091d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3092101367c2SThomas Graf 	fib6_rules_cleanup();
30931da177e4SLinus Torvalds 	xfrm6_fini();
30941da177e4SLinus Torvalds 	fib6_gc_cleanup();
3095c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
30968ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
309741bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3098f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
30991da177e4SLinus Torvalds }
3100