xref: /openbmc/linux/net/ipv6/route.c (revision 5744dd9b71c6b9df944c6a32a000d4a564a2abd7)
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 
225fb0af4c7SEric Dumazet static const 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 
245fb0af4c7SEric Dumazet static const 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 
260fb0af4c7SEric Dumazet static const 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,
284f5b0a874SDavid S. Miller 					0, DST_OBSOLETE_NONE, 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);
29197bab73fSDavid S. Miller 	}
292cf911662SDavid S. Miller 	return rt;
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
2961da177e4SLinus Torvalds {
2971da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
2981da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
2991da177e4SLinus Torvalds 
30097cac082SDavid S. Miller 	if (rt->n)
30197cac082SDavid S. Miller 		neigh_release(rt->n);
30297cac082SDavid S. Miller 
3038e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST))
3048e2ec639SYan, Zheng 		dst_destroy_metrics_generic(dst);
3058e2ec639SYan, Zheng 
30638308473SDavid S. Miller 	if (idev) {
3071da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
3081da177e4SLinus Torvalds 		in6_dev_put(idev);
3091da177e4SLinus Torvalds 	}
3101716a961SGao feng 
3111716a961SGao feng 	if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
3121716a961SGao feng 		dst_release(dst->from);
3131716a961SGao feng 
31497bab73fSDavid S. Miller 	if (rt6_has_peer(rt)) {
31597bab73fSDavid S. Miller 		struct inet_peer *peer = rt6_peer_ptr(rt);
316b3419363SDavid S. Miller 		inet_putpeer(peer);
317b3419363SDavid S. Miller 	}
318b3419363SDavid S. Miller }
319b3419363SDavid S. Miller 
3206431cbc2SDavid S. Miller static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
3216431cbc2SDavid S. Miller 
3226431cbc2SDavid S. Miller static u32 rt6_peer_genid(void)
3236431cbc2SDavid S. Miller {
3246431cbc2SDavid S. Miller 	return atomic_read(&__rt6_peer_genid);
3256431cbc2SDavid S. Miller }
3266431cbc2SDavid S. Miller 
327b3419363SDavid S. Miller void rt6_bind_peer(struct rt6_info *rt, int create)
328b3419363SDavid S. Miller {
32997bab73fSDavid S. Miller 	struct inet_peer_base *base;
330b3419363SDavid S. Miller 	struct inet_peer *peer;
331b3419363SDavid S. Miller 
33297bab73fSDavid S. Miller 	base = inetpeer_base_ptr(rt->_rt6i_peer);
33397bab73fSDavid S. Miller 	if (!base)
33497bab73fSDavid S. Miller 		return;
33597bab73fSDavid S. Miller 
33697bab73fSDavid S. Miller 	peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
3377b34ca2aSDavid S. Miller 	if (peer) {
33897bab73fSDavid S. Miller 		if (!rt6_set_peer(rt, peer))
339b3419363SDavid S. Miller 			inet_putpeer(peer);
3406431cbc2SDavid S. Miller 		else
3416431cbc2SDavid S. Miller 			rt->rt6i_peer_genid = rt6_peer_genid();
3421da177e4SLinus Torvalds 	}
3437b34ca2aSDavid S. Miller }
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3461da177e4SLinus Torvalds 			   int how)
3471da177e4SLinus Torvalds {
3481da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3491da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3505a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
351c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3521da177e4SLinus Torvalds 
35397cac082SDavid S. Miller 	if (dev != loopback_dev) {
35497cac082SDavid S. Miller 		if (idev && idev->dev == dev) {
3555a3e55d6SDenis V. Lunev 			struct inet6_dev *loopback_idev =
3565a3e55d6SDenis V. Lunev 				in6_dev_get(loopback_dev);
35738308473SDavid S. Miller 			if (loopback_idev) {
3581da177e4SLinus Torvalds 				rt->rt6i_idev = loopback_idev;
3591da177e4SLinus Torvalds 				in6_dev_put(idev);
3601da177e4SLinus Torvalds 			}
3611da177e4SLinus Torvalds 		}
36297cac082SDavid S. Miller 		if (rt->n && rt->n->dev == dev) {
36397cac082SDavid S. Miller 			rt->n->dev = loopback_dev;
36497cac082SDavid S. Miller 			dev_hold(loopback_dev);
36597cac082SDavid S. Miller 			dev_put(dev);
36697cac082SDavid S. Miller 		}
36797cac082SDavid S. Miller 	}
3681da177e4SLinus Torvalds }
3691da177e4SLinus Torvalds 
370a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt)
3711da177e4SLinus Torvalds {
3721716a961SGao feng 	struct rt6_info *ort = NULL;
3731716a961SGao feng 
3741716a961SGao feng 	if (rt->rt6i_flags & RTF_EXPIRES) {
3751716a961SGao feng 		if (time_after(jiffies, rt->dst.expires))
376a50feda5SEric Dumazet 			return true;
3771716a961SGao feng 	} else if (rt->dst.from) {
3781716a961SGao feng 		ort = (struct rt6_info *) rt->dst.from;
3791716a961SGao feng 		return (ort->rt6i_flags & RTF_EXPIRES) &&
3801716a961SGao feng 			time_after(jiffies, ort->dst.expires);
3811716a961SGao feng 	}
382a50feda5SEric Dumazet 	return false;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds 
385a50feda5SEric Dumazet static bool rt6_need_strict(const struct in6_addr *daddr)
386c71099acSThomas Graf {
387a02cec21SEric Dumazet 	return ipv6_addr_type(daddr) &
388a02cec21SEric Dumazet 		(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
389c71099acSThomas Graf }
390c71099acSThomas Graf 
3911da177e4SLinus Torvalds /*
392c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
3931da177e4SLinus Torvalds  */
3941da177e4SLinus Torvalds 
3958ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
3968ed67789SDaniel Lezcano 						    struct rt6_info *rt,
397b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
3981da177e4SLinus Torvalds 						    int oif,
399d420895eSYOSHIFUJI Hideaki 						    int flags)
4001da177e4SLinus Torvalds {
4011da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
4021da177e4SLinus Torvalds 	struct rt6_info *sprt;
4031da177e4SLinus Torvalds 
404dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
405dd3abc4eSYOSHIFUJI Hideaki 		goto out;
406dd3abc4eSYOSHIFUJI Hideaki 
407d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
408d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
409dd3abc4eSYOSHIFUJI Hideaki 
410dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
4111da177e4SLinus Torvalds 			if (dev->ifindex == oif)
4121da177e4SLinus Torvalds 				return sprt;
4131da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
41438308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
4151da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
416d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
4171da177e4SLinus Torvalds 						continue;
4181da177e4SLinus Torvalds 					if (local && (!oif ||
4191da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
4201da177e4SLinus Torvalds 						continue;
4211da177e4SLinus Torvalds 				}
4221da177e4SLinus Torvalds 				local = sprt;
4231da177e4SLinus Torvalds 			}
424dd3abc4eSYOSHIFUJI Hideaki 		} else {
425dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
426dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
427dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
428dd3abc4eSYOSHIFUJI Hideaki 		}
4291da177e4SLinus Torvalds 	}
4301da177e4SLinus Torvalds 
431dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
4321da177e4SLinus Torvalds 		if (local)
4331da177e4SLinus Torvalds 			return local;
4341da177e4SLinus Torvalds 
435d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
4368ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
4371da177e4SLinus Torvalds 	}
438dd3abc4eSYOSHIFUJI Hideaki out:
4391da177e4SLinus Torvalds 	return rt;
4401da177e4SLinus Torvalds }
4411da177e4SLinus Torvalds 
44227097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
44327097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
44427097255SYOSHIFUJI Hideaki {
445f2c31e32SEric Dumazet 	struct neighbour *neigh;
44627097255SYOSHIFUJI Hideaki 	/*
44727097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
44827097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
44927097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
45027097255SYOSHIFUJI Hideaki 	 *
45127097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
45227097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
45327097255SYOSHIFUJI Hideaki 	 */
45497cac082SDavid S. Miller 	neigh = rt ? rt->n : NULL;
45527097255SYOSHIFUJI Hideaki 	if (!neigh || (neigh->nud_state & NUD_VALID))
456fdd6681dSAmerigo Wang 		return;
45727097255SYOSHIFUJI Hideaki 	read_lock_bh(&neigh->lock);
45827097255SYOSHIFUJI Hideaki 	if (!(neigh->nud_state & NUD_VALID) &&
45952e16356SYOSHIFUJI Hideaki 	    time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
46027097255SYOSHIFUJI Hideaki 		struct in6_addr mcaddr;
46127097255SYOSHIFUJI Hideaki 		struct in6_addr *target;
46227097255SYOSHIFUJI Hideaki 
46327097255SYOSHIFUJI Hideaki 		neigh->updated = jiffies;
46427097255SYOSHIFUJI Hideaki 		read_unlock_bh(&neigh->lock);
46527097255SYOSHIFUJI Hideaki 
46627097255SYOSHIFUJI Hideaki 		target = (struct in6_addr *)&neigh->primary_key;
46727097255SYOSHIFUJI Hideaki 		addrconf_addr_solict_mult(target, &mcaddr);
468d1918542SDavid S. Miller 		ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
469f2c31e32SEric Dumazet 	} else {
47027097255SYOSHIFUJI Hideaki 		read_unlock_bh(&neigh->lock);
47127097255SYOSHIFUJI Hideaki 	}
472f2c31e32SEric Dumazet }
47327097255SYOSHIFUJI Hideaki #else
47427097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
47527097255SYOSHIFUJI Hideaki {
47627097255SYOSHIFUJI Hideaki }
47727097255SYOSHIFUJI Hideaki #endif
47827097255SYOSHIFUJI Hideaki 
4791da177e4SLinus Torvalds /*
480554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
4811da177e4SLinus Torvalds  */
482b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
4831da177e4SLinus Torvalds {
484d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
485161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
486554cfb7eSYOSHIFUJI Hideaki 		return 2;
487161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
488161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
489161980f4SDavid S. Miller 		return 1;
490554cfb7eSYOSHIFUJI Hideaki 	return 0;
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds 
493b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt)
4941da177e4SLinus Torvalds {
495f2c31e32SEric Dumazet 	struct neighbour *neigh;
496398bcbebSYOSHIFUJI Hideaki 	int m;
497f2c31e32SEric Dumazet 
49897cac082SDavid S. Miller 	neigh = rt->n;
4994d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
5004d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
5014d0c5911SYOSHIFUJI Hideaki 		m = 1;
5024d0c5911SYOSHIFUJI Hideaki 	else if (neigh) {
5031da177e4SLinus Torvalds 		read_lock_bh(&neigh->lock);
504554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
5054d0c5911SYOSHIFUJI Hideaki 			m = 2;
506398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
507398bcbebSYOSHIFUJI Hideaki 		else if (neigh->nud_state & NUD_FAILED)
508398bcbebSYOSHIFUJI Hideaki 			m = 0;
509398bcbebSYOSHIFUJI Hideaki #endif
510398bcbebSYOSHIFUJI Hideaki 		else
511ea73ee23SYOSHIFUJI Hideaki 			m = 1;
5121da177e4SLinus Torvalds 		read_unlock_bh(&neigh->lock);
513398bcbebSYOSHIFUJI Hideaki 	} else
514398bcbebSYOSHIFUJI Hideaki 		m = 0;
515554cfb7eSYOSHIFUJI Hideaki 	return m;
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds 
518554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
519554cfb7eSYOSHIFUJI Hideaki 			   int strict)
520554cfb7eSYOSHIFUJI Hideaki {
5214d0c5911SYOSHIFUJI Hideaki 	int m, n;
5224d0c5911SYOSHIFUJI Hideaki 
5234d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
52477d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
525554cfb7eSYOSHIFUJI Hideaki 		return -1;
526ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
527ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
528ebacaaa0SYOSHIFUJI Hideaki #endif
5294d0c5911SYOSHIFUJI Hideaki 	n = rt6_check_neigh(rt);
530557e92efSYOSHIFUJI Hideaki 	if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
531554cfb7eSYOSHIFUJI Hideaki 		return -1;
532554cfb7eSYOSHIFUJI Hideaki 	return m;
533554cfb7eSYOSHIFUJI Hideaki }
534554cfb7eSYOSHIFUJI Hideaki 
535f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
536f11e6659SDavid S. Miller 				   int *mpri, struct rt6_info *match)
537554cfb7eSYOSHIFUJI Hideaki {
538554cfb7eSYOSHIFUJI Hideaki 	int m;
539554cfb7eSYOSHIFUJI Hideaki 
540554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
541f11e6659SDavid S. Miller 		goto out;
542554cfb7eSYOSHIFUJI Hideaki 
543554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
544554cfb7eSYOSHIFUJI Hideaki 	if (m < 0)
545f11e6659SDavid S. Miller 		goto out;
546554cfb7eSYOSHIFUJI Hideaki 
547f11e6659SDavid S. Miller 	if (m > *mpri) {
548ea659e07SYOSHIFUJI Hideaki 		if (strict & RT6_LOOKUP_F_REACHABLE)
54927097255SYOSHIFUJI Hideaki 			rt6_probe(match);
550f11e6659SDavid S. Miller 		*mpri = m;
551554cfb7eSYOSHIFUJI Hideaki 		match = rt;
552ea659e07SYOSHIFUJI Hideaki 	} else if (strict & RT6_LOOKUP_F_REACHABLE) {
55327097255SYOSHIFUJI Hideaki 		rt6_probe(rt);
5541da177e4SLinus Torvalds 	}
555f11e6659SDavid S. Miller 
556f11e6659SDavid S. Miller out:
557f11e6659SDavid S. Miller 	return match;
5581da177e4SLinus Torvalds }
5591da177e4SLinus Torvalds 
560f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
561f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
562f11e6659SDavid S. Miller 				     u32 metric, int oif, int strict)
563f11e6659SDavid S. Miller {
564f11e6659SDavid S. Miller 	struct rt6_info *rt, *match;
565f11e6659SDavid S. Miller 	int mpri = -1;
566f11e6659SDavid S. Miller 
567f11e6659SDavid S. Miller 	match = NULL;
568f11e6659SDavid S. Miller 	for (rt = rr_head; rt && rt->rt6i_metric == metric;
569d8d1f30bSChangli Gao 	     rt = rt->dst.rt6_next)
570f11e6659SDavid S. Miller 		match = find_match(rt, oif, strict, &mpri, match);
571f11e6659SDavid S. Miller 	for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
572d8d1f30bSChangli Gao 	     rt = rt->dst.rt6_next)
573f11e6659SDavid S. Miller 		match = find_match(rt, oif, strict, &mpri, match);
574f11e6659SDavid S. Miller 
575f11e6659SDavid S. Miller 	return match;
576f11e6659SDavid S. Miller }
577f11e6659SDavid S. Miller 
578f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
579f11e6659SDavid S. Miller {
580f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
5818ed67789SDaniel Lezcano 	struct net *net;
582f11e6659SDavid S. Miller 
583f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
584f11e6659SDavid S. Miller 	if (!rt0)
585f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
586f11e6659SDavid S. Miller 
587f11e6659SDavid S. Miller 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
588f11e6659SDavid S. Miller 
589554cfb7eSYOSHIFUJI Hideaki 	if (!match &&
590f11e6659SDavid S. Miller 	    (strict & RT6_LOOKUP_F_REACHABLE)) {
591d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
592f11e6659SDavid S. Miller 
593554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
594f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
595f11e6659SDavid S. Miller 			next = fn->leaf;
596f11e6659SDavid S. Miller 
597f11e6659SDavid S. Miller 		if (next != rt0)
598f11e6659SDavid S. Miller 			fn->rr_ptr = next;
599554cfb7eSYOSHIFUJI Hideaki 	}
600554cfb7eSYOSHIFUJI Hideaki 
601d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
602a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
6031da177e4SLinus Torvalds }
6041da177e4SLinus Torvalds 
60570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
60670ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
607b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
60870ceb4f5SYOSHIFUJI Hideaki {
609c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
61070ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
61170ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
61270ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
6134bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
61470ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
61570ceb4f5SYOSHIFUJI Hideaki 
61670ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
61770ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
61870ceb4f5SYOSHIFUJI Hideaki 	}
61970ceb4f5SYOSHIFUJI Hideaki 
62070ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
62170ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
62270ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
62370ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
62470ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
62570ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
62670ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
62770ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
62870ceb4f5SYOSHIFUJI Hideaki 		}
62970ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
63070ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
63170ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
63270ceb4f5SYOSHIFUJI Hideaki 		}
63370ceb4f5SYOSHIFUJI Hideaki 	}
63470ceb4f5SYOSHIFUJI Hideaki 
63570ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
63670ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
6373933fc95SJens Rosenboom 		return -EINVAL;
63870ceb4f5SYOSHIFUJI Hideaki 
6394bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
64070ceb4f5SYOSHIFUJI Hideaki 
64170ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
64270ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
64370ceb4f5SYOSHIFUJI Hideaki 	else {
64470ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
64570ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
64670ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
64770ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
64870ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
64970ceb4f5SYOSHIFUJI Hideaki 	}
65070ceb4f5SYOSHIFUJI Hideaki 
651efa2cea0SDaniel Lezcano 	rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
652efa2cea0SDaniel Lezcano 				dev->ifindex);
65370ceb4f5SYOSHIFUJI Hideaki 
65470ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
655e0a1ad73SThomas Graf 		ip6_del_rt(rt);
65670ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
65770ceb4f5SYOSHIFUJI Hideaki 	}
65870ceb4f5SYOSHIFUJI Hideaki 
65970ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
660efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
66170ceb4f5SYOSHIFUJI Hideaki 					pref);
66270ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
66370ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
66470ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
66570ceb4f5SYOSHIFUJI Hideaki 
66670ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
6671716a961SGao feng 		if (!addrconf_finite_timeout(lifetime))
6681716a961SGao feng 			rt6_clean_expires(rt);
6691716a961SGao feng 		else
6701716a961SGao feng 			rt6_set_expires(rt, jiffies + HZ * lifetime);
6711716a961SGao feng 
672d8d1f30bSChangli Gao 		dst_release(&rt->dst);
67370ceb4f5SYOSHIFUJI Hideaki 	}
67470ceb4f5SYOSHIFUJI Hideaki 	return 0;
67570ceb4f5SYOSHIFUJI Hideaki }
67670ceb4f5SYOSHIFUJI Hideaki #endif
67770ceb4f5SYOSHIFUJI Hideaki 
6788ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr)			\
679982f56f3SYOSHIFUJI Hideaki do { \
6808ed67789SDaniel Lezcano 	if (rt == __net->ipv6.ip6_null_entry) {	\
681982f56f3SYOSHIFUJI Hideaki 		struct fib6_node *pn; \
682e0eda7bbSVille Nuorvala 		while (1) { \
683982f56f3SYOSHIFUJI Hideaki 			if (fn->fn_flags & RTN_TL_ROOT) \
684c71099acSThomas Graf 				goto out; \
685982f56f3SYOSHIFUJI Hideaki 			pn = fn->parent; \
686982f56f3SYOSHIFUJI Hideaki 			if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
6878bce65b9SKim Nordlund 				fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
688982f56f3SYOSHIFUJI Hideaki 			else \
689982f56f3SYOSHIFUJI Hideaki 				fn = pn; \
690c71099acSThomas Graf 			if (fn->fn_flags & RTN_RTINFO) \
691c71099acSThomas Graf 				goto restart; \
692c71099acSThomas Graf 		} \
693982f56f3SYOSHIFUJI Hideaki 	} \
694982f56f3SYOSHIFUJI Hideaki } while (0)
695c71099acSThomas Graf 
6968ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
6978ed67789SDaniel Lezcano 					     struct fib6_table *table,
6984c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
6991da177e4SLinus Torvalds {
7001da177e4SLinus Torvalds 	struct fib6_node *fn;
7011da177e4SLinus Torvalds 	struct rt6_info *rt;
7021da177e4SLinus Torvalds 
703c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
7044c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
705c71099acSThomas Graf restart:
706c71099acSThomas Graf 	rt = fn->leaf;
7074c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
7084c9483b2SDavid S. Miller 	BACKTRACK(net, &fl6->saddr);
709c71099acSThomas Graf out:
710d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
711c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
7121da177e4SLinus Torvalds 	return rt;
713c71099acSThomas Graf 
714c71099acSThomas Graf }
715c71099acSThomas Graf 
716ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
717ea6e574eSFlorian Westphal 				    int flags)
718ea6e574eSFlorian Westphal {
719ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
720ea6e574eSFlorian Westphal }
721ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
722ea6e574eSFlorian Westphal 
7239acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
7249acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
725c71099acSThomas Graf {
7264c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
7274c9483b2SDavid S. Miller 		.flowi6_oif = oif,
7284c9483b2SDavid S. Miller 		.daddr = *daddr,
729c71099acSThomas Graf 	};
730c71099acSThomas Graf 	struct dst_entry *dst;
73177d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
732c71099acSThomas Graf 
733adaa70bbSThomas Graf 	if (saddr) {
7344c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
735adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
736adaa70bbSThomas Graf 	}
737adaa70bbSThomas Graf 
7384c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
739c71099acSThomas Graf 	if (dst->error == 0)
740c71099acSThomas Graf 		return (struct rt6_info *) dst;
741c71099acSThomas Graf 
742c71099acSThomas Graf 	dst_release(dst);
743c71099acSThomas Graf 
7441da177e4SLinus Torvalds 	return NULL;
7451da177e4SLinus Torvalds }
7461da177e4SLinus Torvalds 
7477159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
7487159039aSYOSHIFUJI Hideaki 
749c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
7501da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
7511da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
7521da177e4SLinus Torvalds    be destroyed.
7531da177e4SLinus Torvalds  */
7541da177e4SLinus Torvalds 
75586872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
7561da177e4SLinus Torvalds {
7571da177e4SLinus Torvalds 	int err;
758c71099acSThomas Graf 	struct fib6_table *table;
7591da177e4SLinus Torvalds 
760c71099acSThomas Graf 	table = rt->rt6i_table;
761c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
76286872cb5SThomas Graf 	err = fib6_add(&table->tb6_root, rt, info);
763c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	return err;
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds 
76840e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
76940e22e8fSThomas Graf {
7704d1169c1SDenis V. Lunev 	struct nl_info info = {
771d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
7724d1169c1SDenis V. Lunev 	};
773528c4cebSDenis V. Lunev 	return __ip6_ins_rt(rt, &info);
77440e22e8fSThomas Graf }
77540e22e8fSThomas Graf 
7761716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
77721efcfa0SEric Dumazet 				      const struct in6_addr *daddr,
778b71d1d42SEric Dumazet 				      const struct in6_addr *saddr)
7791da177e4SLinus Torvalds {
7801da177e4SLinus Torvalds 	struct rt6_info *rt;
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds 	/*
7831da177e4SLinus Torvalds 	 *	Clone the route.
7841da177e4SLinus Torvalds 	 */
7851da177e4SLinus Torvalds 
78621efcfa0SEric Dumazet 	rt = ip6_rt_copy(ort, daddr);
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	if (rt) {
78914deae41SDavid S. Miller 		int attempts = !in_softirq();
79014deae41SDavid S. Miller 
79158c4fb86SYOSHIFUJI Hideaki 		if (!(rt->rt6i_flags & RTF_GATEWAY)) {
792bb3c3686SDavid S. Miller 			if (ort->rt6i_dst.plen != 128 &&
79321efcfa0SEric Dumazet 			    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
79458c4fb86SYOSHIFUJI Hideaki 				rt->rt6i_flags |= RTF_ANYCAST;
7954e3fd7a0SAlexey Dobriyan 			rt->rt6i_gateway = *daddr;
79658c4fb86SYOSHIFUJI Hideaki 		}
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_CACHE;
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
8011da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
8024e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
8031da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
8041da177e4SLinus Torvalds 		}
8051da177e4SLinus Torvalds #endif
8061da177e4SLinus Torvalds 
80714deae41SDavid S. Miller 	retry:
8088ade06c6SDavid S. Miller 		if (rt6_bind_neighbour(rt, rt->dst.dev)) {
809d1918542SDavid S. Miller 			struct net *net = dev_net(rt->dst.dev);
81014deae41SDavid S. Miller 			int saved_rt_min_interval =
81114deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval;
81214deae41SDavid S. Miller 			int saved_rt_elasticity =
81314deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity;
81414deae41SDavid S. Miller 
81514deae41SDavid S. Miller 			if (attempts-- > 0) {
81614deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
81714deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
81814deae41SDavid S. Miller 
81986393e52SAlexey Dobriyan 				ip6_dst_gc(&net->ipv6.ip6_dst_ops);
82014deae41SDavid S. Miller 
82114deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity =
82214deae41SDavid S. Miller 					saved_rt_elasticity;
82314deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval =
82414deae41SDavid S. Miller 					saved_rt_min_interval;
82514deae41SDavid S. Miller 				goto retry;
82614deae41SDavid S. Miller 			}
82714deae41SDavid S. Miller 
828f3213831SJoe Perches 			net_warn_ratelimited("Neighbour table overflow\n");
829d8d1f30bSChangli Gao 			dst_free(&rt->dst);
83014deae41SDavid S. Miller 			return NULL;
83114deae41SDavid S. Miller 		}
83295a9a5baSYOSHIFUJI Hideaki 	}
8331da177e4SLinus Torvalds 
8341da177e4SLinus Torvalds 	return rt;
8351da177e4SLinus Torvalds }
83695a9a5baSYOSHIFUJI Hideaki 
83721efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
83821efcfa0SEric Dumazet 					const struct in6_addr *daddr)
839299d9939SYOSHIFUJI Hideaki {
84021efcfa0SEric Dumazet 	struct rt6_info *rt = ip6_rt_copy(ort, daddr);
84121efcfa0SEric Dumazet 
842299d9939SYOSHIFUJI Hideaki 	if (rt) {
843299d9939SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_CACHE;
84497cac082SDavid S. Miller 		rt->n = neigh_clone(ort->n);
845299d9939SYOSHIFUJI Hideaki 	}
846299d9939SYOSHIFUJI Hideaki 	return rt;
847299d9939SYOSHIFUJI Hideaki }
848299d9939SYOSHIFUJI Hideaki 
8498ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
8504c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
8511da177e4SLinus Torvalds {
8521da177e4SLinus Torvalds 	struct fib6_node *fn;
853519fbd87SYOSHIFUJI Hideaki 	struct rt6_info *rt, *nrt;
854c71099acSThomas Graf 	int strict = 0;
8551da177e4SLinus Torvalds 	int attempts = 3;
856519fbd87SYOSHIFUJI Hideaki 	int err;
85753b7997fSYOSHIFUJI Hideaki 	int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
8581da177e4SLinus Torvalds 
85977d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
8601da177e4SLinus Torvalds 
8611da177e4SLinus Torvalds relookup:
862c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8631da177e4SLinus Torvalds 
8648238dd06SYOSHIFUJI Hideaki restart_2:
8654c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
8661da177e4SLinus Torvalds 
8671da177e4SLinus Torvalds restart:
8684acad72dSPavel Emelyanov 	rt = rt6_select(fn, oif, strict | reachable);
8698ed67789SDaniel Lezcano 
8704c9483b2SDavid S. Miller 	BACKTRACK(net, &fl6->saddr);
8718ed67789SDaniel Lezcano 	if (rt == net->ipv6.ip6_null_entry ||
8728238dd06SYOSHIFUJI Hideaki 	    rt->rt6i_flags & RTF_CACHE)
8731da177e4SLinus Torvalds 		goto out;
8741da177e4SLinus Torvalds 
875d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
876c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8771da177e4SLinus Torvalds 
87897cac082SDavid S. Miller 	if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
8794c9483b2SDavid S. Miller 		nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
8807343ff31SDavid S. Miller 	else if (!(rt->dst.flags & DST_HOST))
8814c9483b2SDavid S. Miller 		nrt = rt6_alloc_clone(rt, &fl6->daddr);
8827343ff31SDavid S. Miller 	else
8837343ff31SDavid S. Miller 		goto out2;
8841da177e4SLinus Torvalds 
885d8d1f30bSChangli Gao 	dst_release(&rt->dst);
8868ed67789SDaniel Lezcano 	rt = nrt ? : net->ipv6.ip6_null_entry;
8871da177e4SLinus Torvalds 
888d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
889e40cf353SYOSHIFUJI Hideaki 	if (nrt) {
89040e22e8fSThomas Graf 		err = ip6_ins_rt(nrt);
891e40cf353SYOSHIFUJI Hideaki 		if (!err)
892e40cf353SYOSHIFUJI Hideaki 			goto out2;
893e40cf353SYOSHIFUJI Hideaki 	}
894e40cf353SYOSHIFUJI Hideaki 
895e40cf353SYOSHIFUJI Hideaki 	if (--attempts <= 0)
8961da177e4SLinus Torvalds 		goto out2;
8971da177e4SLinus Torvalds 
898519fbd87SYOSHIFUJI Hideaki 	/*
899c71099acSThomas Graf 	 * Race condition! In the gap, when table->tb6_lock was
900519fbd87SYOSHIFUJI Hideaki 	 * released someone could insert this route.  Relookup.
9011da177e4SLinus Torvalds 	 */
902d8d1f30bSChangli Gao 	dst_release(&rt->dst);
9031da177e4SLinus Torvalds 	goto relookup;
904e40cf353SYOSHIFUJI Hideaki 
905519fbd87SYOSHIFUJI Hideaki out:
9068238dd06SYOSHIFUJI Hideaki 	if (reachable) {
9078238dd06SYOSHIFUJI Hideaki 		reachable = 0;
9088238dd06SYOSHIFUJI Hideaki 		goto restart_2;
9098238dd06SYOSHIFUJI Hideaki 	}
910d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
911c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
9121da177e4SLinus Torvalds out2:
913d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
914d8d1f30bSChangli Gao 	rt->dst.__use++;
915c71099acSThomas Graf 
916c71099acSThomas Graf 	return rt;
917c71099acSThomas Graf }
918c71099acSThomas Graf 
9198ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
9204c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
9214acad72dSPavel Emelyanov {
9224c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
9234acad72dSPavel Emelyanov }
9244acad72dSPavel Emelyanov 
92572331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net,
92672331bc0SShmulik Ladkani 						struct net_device *dev,
92772331bc0SShmulik Ladkani 						struct flowi6 *fl6, int flags)
92872331bc0SShmulik Ladkani {
92972331bc0SShmulik Ladkani 	if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
93072331bc0SShmulik Ladkani 		flags |= RT6_LOOKUP_F_IFACE;
93172331bc0SShmulik Ladkani 
93272331bc0SShmulik Ladkani 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
93372331bc0SShmulik Ladkani }
93472331bc0SShmulik Ladkani 
935c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
936c71099acSThomas Graf {
937b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
938c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
939adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
9404c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
9414c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
9424c9483b2SDavid S. Miller 		.daddr = iph->daddr,
9434c9483b2SDavid S. Miller 		.saddr = iph->saddr,
9444c9483b2SDavid S. Miller 		.flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
9454c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
9464c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
947c71099acSThomas Graf 	};
948adaa70bbSThomas Graf 
94972331bc0SShmulik Ladkani 	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
950c71099acSThomas Graf }
951c71099acSThomas Graf 
9528ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
9534c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
954c71099acSThomas Graf {
9554c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
956c71099acSThomas Graf }
957c71099acSThomas Graf 
9589c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
9594c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
960c71099acSThomas Graf {
961c71099acSThomas Graf 	int flags = 0;
962c71099acSThomas Graf 
9631fb9489bSPavel Emelyanov 	fl6->flowi6_iif = LOOPBACK_IFINDEX;
9644dc27d1cSDavid McCullough 
9654c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
96677d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
967c71099acSThomas Graf 
9684c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
969adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
9700c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
9710c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
972adaa70bbSThomas Graf 
9734c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
9741da177e4SLinus Torvalds }
9751da177e4SLinus Torvalds 
9767159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
9771da177e4SLinus Torvalds 
9782774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
97914e50e57SDavid S. Miller {
9805c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
98114e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
98214e50e57SDavid S. Miller 
983f5b0a874SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
98414e50e57SDavid S. Miller 	if (rt) {
985d8d1f30bSChangli Gao 		new = &rt->dst;
98614e50e57SDavid S. Miller 
9878104891bSSteffen Klassert 		memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
9888104891bSSteffen Klassert 		rt6_init_peer(rt, net->ipv6.peers);
9898104891bSSteffen Klassert 
99014e50e57SDavid S. Miller 		new->__use = 1;
991352e512cSHerbert Xu 		new->input = dst_discard;
992352e512cSHerbert Xu 		new->output = dst_discard;
99314e50e57SDavid S. Miller 
99421efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
99521efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
99621efcfa0SEric Dumazet 		else
997defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
99814e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
99914e50e57SDavid S. Miller 		if (rt->rt6i_idev)
100014e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
100114e50e57SDavid S. Miller 
10024e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
10031716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
10041716a961SGao feng 		rt6_clean_expires(rt);
100514e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
100614e50e57SDavid S. Miller 
100714e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
100814e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
100914e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
101014e50e57SDavid S. Miller #endif
101114e50e57SDavid S. Miller 
101214e50e57SDavid S. Miller 		dst_free(new);
101314e50e57SDavid S. Miller 	}
101414e50e57SDavid S. Miller 
101569ead7afSDavid S. Miller 	dst_release(dst_orig);
101669ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
101714e50e57SDavid S. Miller }
101814e50e57SDavid S. Miller 
10191da177e4SLinus Torvalds /*
10201da177e4SLinus Torvalds  *	Destination cache support functions
10211da177e4SLinus Torvalds  */
10221da177e4SLinus Torvalds 
10231da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
10241da177e4SLinus Torvalds {
10251da177e4SLinus Torvalds 	struct rt6_info *rt;
10261da177e4SLinus Torvalds 
10271da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
10281da177e4SLinus Torvalds 
10296431cbc2SDavid S. Miller 	if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
10306431cbc2SDavid S. Miller 		if (rt->rt6i_peer_genid != rt6_peer_genid()) {
103197bab73fSDavid S. Miller 			if (!rt6_has_peer(rt))
10326431cbc2SDavid S. Miller 				rt6_bind_peer(rt, 0);
10336431cbc2SDavid S. Miller 			rt->rt6i_peer_genid = rt6_peer_genid();
10346431cbc2SDavid S. Miller 		}
10351da177e4SLinus Torvalds 		return dst;
10366431cbc2SDavid S. Miller 	}
10371da177e4SLinus Torvalds 	return NULL;
10381da177e4SLinus Torvalds }
10391da177e4SLinus Torvalds 
10401da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
10411da177e4SLinus Torvalds {
10421da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
10431da177e4SLinus Torvalds 
10441da177e4SLinus Torvalds 	if (rt) {
104554c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
104654c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
1047e0a1ad73SThomas Graf 				ip6_del_rt(rt);
104854c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
10491da177e4SLinus Torvalds 			}
105054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
105154c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
105254c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
105354c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
105454c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
105554c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
10561da177e4SLinus Torvalds }
10571da177e4SLinus Torvalds 
10581da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
10591da177e4SLinus Torvalds {
10601da177e4SLinus Torvalds 	struct rt6_info *rt;
10611da177e4SLinus Torvalds 
10623ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
10631da177e4SLinus Torvalds 
1064adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
10651da177e4SLinus Torvalds 	if (rt) {
10661716a961SGao feng 		if (rt->rt6i_flags & RTF_CACHE)
10671716a961SGao feng 			rt6_update_expires(rt, 0);
10681716a961SGao feng 		else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
10691da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
10701da177e4SLinus Torvalds 	}
10711da177e4SLinus Torvalds }
10721da177e4SLinus Torvalds 
10736700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
10746700c270SDavid S. Miller 			       struct sk_buff *skb, u32 mtu)
10751da177e4SLinus Torvalds {
10761da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info*)dst;
10771da177e4SLinus Torvalds 
107881aded24SDavid S. Miller 	dst_confirm(dst);
10791da177e4SLinus Torvalds 	if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
108081aded24SDavid S. Miller 		struct net *net = dev_net(dst->dev);
108181aded24SDavid S. Miller 
10821da177e4SLinus Torvalds 		rt6->rt6i_flags |= RTF_MODIFIED;
10831da177e4SLinus Torvalds 		if (mtu < IPV6_MIN_MTU) {
1084defb3519SDavid S. Miller 			u32 features = dst_metric(dst, RTAX_FEATURES);
10851da177e4SLinus Torvalds 			mtu = IPV6_MIN_MTU;
1086defb3519SDavid S. Miller 			features |= RTAX_FEATURE_ALLFRAG;
1087defb3519SDavid S. Miller 			dst_metric_set(dst, RTAX_FEATURES, features);
10881da177e4SLinus Torvalds 		}
1089defb3519SDavid S. Miller 		dst_metric_set(dst, RTAX_MTU, mtu);
109081aded24SDavid S. Miller 		rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
10911da177e4SLinus Torvalds 	}
10921da177e4SLinus Torvalds }
10931da177e4SLinus Torvalds 
109442ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
109542ae66c8SDavid S. Miller 		     int oif, u32 mark)
109681aded24SDavid S. Miller {
109781aded24SDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
109881aded24SDavid S. Miller 	struct dst_entry *dst;
109981aded24SDavid S. Miller 	struct flowi6 fl6;
110081aded24SDavid S. Miller 
110181aded24SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
110281aded24SDavid S. Miller 	fl6.flowi6_oif = oif;
110381aded24SDavid S. Miller 	fl6.flowi6_mark = mark;
11043e12939aSDavid S. Miller 	fl6.flowi6_flags = 0;
110581aded24SDavid S. Miller 	fl6.daddr = iph->daddr;
110681aded24SDavid S. Miller 	fl6.saddr = iph->saddr;
110781aded24SDavid S. Miller 	fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
110881aded24SDavid S. Miller 
110981aded24SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
111081aded24SDavid S. Miller 	if (!dst->error)
11116700c270SDavid S. Miller 		ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
111281aded24SDavid S. Miller 	dst_release(dst);
111381aded24SDavid S. Miller }
111481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu);
111581aded24SDavid S. Miller 
111681aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
111781aded24SDavid S. Miller {
111881aded24SDavid S. Miller 	ip6_update_pmtu(skb, sock_net(sk), mtu,
111981aded24SDavid S. Miller 			sk->sk_bound_dev_if, sk->sk_mark);
112081aded24SDavid S. Miller }
112181aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
112281aded24SDavid S. Miller 
11233a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
11243a5ad2eeSDavid S. Miller {
11253a5ad2eeSDavid S. Miller 	const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
11263a5ad2eeSDavid S. Miller 	struct dst_entry *dst;
11273a5ad2eeSDavid S. Miller 	struct flowi6 fl6;
11283a5ad2eeSDavid S. Miller 
11293a5ad2eeSDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
11303a5ad2eeSDavid S. Miller 	fl6.flowi6_oif = oif;
11313a5ad2eeSDavid S. Miller 	fl6.flowi6_mark = mark;
11323a5ad2eeSDavid S. Miller 	fl6.flowi6_flags = 0;
11333a5ad2eeSDavid S. Miller 	fl6.daddr = iph->daddr;
11343a5ad2eeSDavid S. Miller 	fl6.saddr = iph->saddr;
11353a5ad2eeSDavid S. Miller 	fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
11363a5ad2eeSDavid S. Miller 
11373a5ad2eeSDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
11383a5ad2eeSDavid S. Miller 	if (!dst->error)
11396700c270SDavid S. Miller 		rt6_do_redirect(dst, NULL, skb);
11403a5ad2eeSDavid S. Miller 	dst_release(dst);
11413a5ad2eeSDavid S. Miller }
11423a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect);
11433a5ad2eeSDavid S. Miller 
11443a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
11453a5ad2eeSDavid S. Miller {
11463a5ad2eeSDavid S. Miller 	ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
11473a5ad2eeSDavid S. Miller }
11483a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect);
11493a5ad2eeSDavid S. Miller 
11500dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
11511da177e4SLinus Torvalds {
11520dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
11530dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
11540dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
11550dbaee3bSDavid S. Miller 
11561da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
11571da177e4SLinus Torvalds 
11585578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
11595578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds 	/*
11621da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
11631da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
11641da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
11651da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
11661da177e4SLinus Torvalds 	 */
11671da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
11681da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
11691da177e4SLinus Torvalds 	return mtu;
11701da177e4SLinus Torvalds }
11711da177e4SLinus Torvalds 
1172ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1173d33e4553SDavid S. Miller {
1174d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1175618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1176618f9bc7SSteffen Klassert 
1177618f9bc7SSteffen Klassert 	if (mtu)
1178618f9bc7SSteffen Klassert 		return mtu;
1179618f9bc7SSteffen Klassert 
1180618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1181d33e4553SDavid S. Miller 
1182d33e4553SDavid S. Miller 	rcu_read_lock();
1183d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1184d33e4553SDavid S. Miller 	if (idev)
1185d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1186d33e4553SDavid S. Miller 	rcu_read_unlock();
1187d33e4553SDavid S. Miller 
1188d33e4553SDavid S. Miller 	return mtu;
1189d33e4553SDavid S. Miller }
1190d33e4553SDavid S. Miller 
11913b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
11923b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
11935d0bbeebSThomas Graf 
11943b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
11951da177e4SLinus Torvalds 				  struct neighbour *neigh,
119687a11578SDavid S. Miller 				  struct flowi6 *fl6)
11971da177e4SLinus Torvalds {
119887a11578SDavid S. Miller 	struct dst_entry *dst;
11991da177e4SLinus Torvalds 	struct rt6_info *rt;
12001da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1201c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12021da177e4SLinus Torvalds 
120338308473SDavid S. Miller 	if (unlikely(!idev))
1204122bdf67SEric Dumazet 		return ERR_PTR(-ENODEV);
12051da177e4SLinus Torvalds 
12068b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, dev, 0, NULL);
120738308473SDavid S. Miller 	if (unlikely(!rt)) {
12081da177e4SLinus Torvalds 		in6_dev_put(idev);
120987a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
12101da177e4SLinus Torvalds 		goto out;
12111da177e4SLinus Torvalds 	}
12121da177e4SLinus Torvalds 
12131da177e4SLinus Torvalds 	if (neigh)
12141da177e4SLinus Torvalds 		neigh_hold(neigh);
121514deae41SDavid S. Miller 	else {
1216f894cbf8SDavid S. Miller 		neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
1217b43faac6SDavid S. Miller 		if (IS_ERR(neigh)) {
1218252c3d84SRongQing.Li 			in6_dev_put(idev);
1219b43faac6SDavid S. Miller 			dst_free(&rt->dst);
1220b43faac6SDavid S. Miller 			return ERR_CAST(neigh);
1221b43faac6SDavid S. Miller 		}
122214deae41SDavid S. Miller 	}
12231da177e4SLinus Torvalds 
12248e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
12258e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
122697cac082SDavid S. Miller 	rt->n = neigh;
1227d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
122887a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
12298e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
12308e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
12317011687fSGao feng 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
12321da177e4SLinus Torvalds 
12333b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1234d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1235d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
12363b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
12371da177e4SLinus Torvalds 
12385578689aSDaniel Lezcano 	fib6_force_start_gc(net);
12391da177e4SLinus Torvalds 
124087a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
124187a11578SDavid S. Miller 
12421da177e4SLinus Torvalds out:
124387a11578SDavid S. Miller 	return dst;
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds 
12463d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
12471da177e4SLinus Torvalds {
1248e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
12493d0f24a7SStephen Hemminger 	int more = 0;
12501da177e4SLinus Torvalds 
12513b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
12523b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
12535d0bbeebSThomas Graf 
12541da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
12551da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
12561da177e4SLinus Torvalds 			*pprev = dst->next;
12571da177e4SLinus Torvalds 			dst_free(dst);
12581da177e4SLinus Torvalds 		} else {
12591da177e4SLinus Torvalds 			pprev = &dst->next;
12603d0f24a7SStephen Hemminger 			++more;
12611da177e4SLinus Torvalds 		}
12621da177e4SLinus Torvalds 	}
12631da177e4SLinus Torvalds 
12643b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
12655d0bbeebSThomas Graf 
12663d0f24a7SStephen Hemminger 	return more;
12671da177e4SLinus Torvalds }
12681da177e4SLinus Torvalds 
12691e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
12701e493d19SDavid S. Miller 			    void *arg)
12711e493d19SDavid S. Miller {
12721e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
12731e493d19SDavid S. Miller 
12741e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
12751e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
12761e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
12771e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
12781e493d19SDavid S. Miller 		if (func(rt, arg)) {
12791e493d19SDavid S. Miller 			*pprev = dst->next;
12801e493d19SDavid S. Miller 			dst_free(dst);
12811e493d19SDavid S. Miller 		} else {
12821e493d19SDavid S. Miller 			pprev = &dst->next;
12831e493d19SDavid S. Miller 		}
12841e493d19SDavid S. Miller 	}
12851e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
12861e493d19SDavid S. Miller }
12871e493d19SDavid S. Miller 
1288569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
12891da177e4SLinus Torvalds {
12901da177e4SLinus Torvalds 	unsigned long now = jiffies;
129186393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
12927019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
12937019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
12947019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
12957019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
12967019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1297fc66f95cSEric Dumazet 	int entries;
12981da177e4SLinus Torvalds 
1299fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
13007019b78eSDaniel Lezcano 	if (time_after(rt_last_gc + rt_min_interval, now) &&
1301fc66f95cSEric Dumazet 	    entries <= rt_max_size)
13021da177e4SLinus Torvalds 		goto out;
13031da177e4SLinus Torvalds 
13046891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
13056891a346SBenjamin Thery 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
13066891a346SBenjamin Thery 	net->ipv6.ip6_rt_last_gc = now;
1307fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1308fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
13097019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
13101da177e4SLinus Torvalds out:
13117019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1312fc66f95cSEric Dumazet 	return entries > rt_max_size;
13131da177e4SLinus Torvalds }
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds /* Clean host part of a prefix. Not necessary in radix tree,
13161da177e4SLinus Torvalds    but results in cleaner routing tables.
13171da177e4SLinus Torvalds 
13181da177e4SLinus Torvalds    Remove it only when all the things will work!
13191da177e4SLinus Torvalds  */
13201da177e4SLinus Torvalds 
13216b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst)
13221da177e4SLinus Torvalds {
13235170ae82SDavid S. Miller 	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
1324a02e4b7dSDavid S. Miller 	if (hoplimit == 0) {
13256b75d090SYOSHIFUJI Hideaki 		struct net_device *dev = dst->dev;
1326c68f24ccSEric Dumazet 		struct inet6_dev *idev;
1327c68f24ccSEric Dumazet 
1328c68f24ccSEric Dumazet 		rcu_read_lock();
1329c68f24ccSEric Dumazet 		idev = __in6_dev_get(dev);
1330c68f24ccSEric Dumazet 		if (idev)
13311da177e4SLinus Torvalds 			hoplimit = idev->cnf.hop_limit;
1332c68f24ccSEric Dumazet 		else
133353b7997fSYOSHIFUJI Hideaki 			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
1334c68f24ccSEric Dumazet 		rcu_read_unlock();
13351da177e4SLinus Torvalds 	}
13361da177e4SLinus Torvalds 	return hoplimit;
13371da177e4SLinus Torvalds }
1338abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit);
13391da177e4SLinus Torvalds 
13401da177e4SLinus Torvalds /*
13411da177e4SLinus Torvalds  *
13421da177e4SLinus Torvalds  */
13431da177e4SLinus Torvalds 
134486872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
13451da177e4SLinus Torvalds {
13461da177e4SLinus Torvalds 	int err;
13475578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
13481da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
13491da177e4SLinus Torvalds 	struct net_device *dev = NULL;
13501da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1351c71099acSThomas Graf 	struct fib6_table *table;
13521da177e4SLinus Torvalds 	int addr_type;
13531da177e4SLinus Torvalds 
135486872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
13551da177e4SLinus Torvalds 		return -EINVAL;
13561da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
135786872cb5SThomas Graf 	if (cfg->fc_src_len)
13581da177e4SLinus Torvalds 		return -EINVAL;
13591da177e4SLinus Torvalds #endif
136086872cb5SThomas Graf 	if (cfg->fc_ifindex) {
13611da177e4SLinus Torvalds 		err = -ENODEV;
13625578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
13631da177e4SLinus Torvalds 		if (!dev)
13641da177e4SLinus Torvalds 			goto out;
13651da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
13661da177e4SLinus Torvalds 		if (!idev)
13671da177e4SLinus Torvalds 			goto out;
13681da177e4SLinus Torvalds 	}
13691da177e4SLinus Torvalds 
137086872cb5SThomas Graf 	if (cfg->fc_metric == 0)
137186872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
13721da177e4SLinus Torvalds 
1373c71099acSThomas Graf 	err = -ENOBUFS;
137438308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1375d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1376d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
137738308473SDavid S. Miller 		if (!table) {
1378f3213831SJoe Perches 			pr_warn("NLM_F_CREATE should be specified when creating new route\n");
1379d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1380d71314b4SMatti Vaittinen 		}
1381d71314b4SMatti Vaittinen 	} else {
1382d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1383d71314b4SMatti Vaittinen 	}
138438308473SDavid S. Miller 
138538308473SDavid S. Miller 	if (!table)
1386c71099acSThomas Graf 		goto out;
1387c71099acSThomas Graf 
13888b96d22dSDavid S. Miller 	rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
13891da177e4SLinus Torvalds 
139038308473SDavid S. Miller 	if (!rt) {
13911da177e4SLinus Torvalds 		err = -ENOMEM;
13921da177e4SLinus Torvalds 		goto out;
13931da177e4SLinus Torvalds 	}
13941da177e4SLinus Torvalds 
1395d8d1f30bSChangli Gao 	rt->dst.obsolete = -1;
13961716a961SGao feng 
13971716a961SGao feng 	if (cfg->fc_flags & RTF_EXPIRES)
13981716a961SGao feng 		rt6_set_expires(rt, jiffies +
13991716a961SGao feng 				clock_t_to_jiffies(cfg->fc_expires));
14001716a961SGao feng 	else
14011716a961SGao feng 		rt6_clean_expires(rt);
14021da177e4SLinus Torvalds 
140386872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
140486872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
140586872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
140686872cb5SThomas Graf 
140786872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
14081da177e4SLinus Torvalds 
14091da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1410d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1411ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1412ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
14131da177e4SLinus Torvalds 	else
1414d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
14151da177e4SLinus Torvalds 
1416d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
14171da177e4SLinus Torvalds 
141886872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
141986872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
14201da177e4SLinus Torvalds 	if (rt->rt6i_dst.plen == 128)
142111d53b49SDavid S. Miller 	       rt->dst.flags |= DST_HOST;
14221da177e4SLinus Torvalds 
14238e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
14248e2ec639SYan, Zheng 		u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
14258e2ec639SYan, Zheng 		if (!metrics) {
14268e2ec639SYan, Zheng 			err = -ENOMEM;
14278e2ec639SYan, Zheng 			goto out;
14288e2ec639SYan, Zheng 		}
14298e2ec639SYan, Zheng 		dst_init_metrics(&rt->dst, metrics, 0);
14308e2ec639SYan, Zheng 	}
14311da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
143286872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
143386872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
14341da177e4SLinus Torvalds #endif
14351da177e4SLinus Torvalds 
143686872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
14371da177e4SLinus Torvalds 
14381da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
14391da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
14401da177e4SLinus Torvalds 	 */
144186872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
144238308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
144338308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
144438308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
14451da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
14465578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
14471da177e4SLinus Torvalds 			if (dev) {
14481da177e4SLinus Torvalds 				dev_put(dev);
14491da177e4SLinus Torvalds 				in6_dev_put(idev);
14501da177e4SLinus Torvalds 			}
14515578689aSDaniel Lezcano 			dev = net->loopback_dev;
14521da177e4SLinus Torvalds 			dev_hold(dev);
14531da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
14541da177e4SLinus Torvalds 			if (!idev) {
14551da177e4SLinus Torvalds 				err = -ENODEV;
14561da177e4SLinus Torvalds 				goto out;
14571da177e4SLinus Torvalds 			}
14581da177e4SLinus Torvalds 		}
1459d8d1f30bSChangli Gao 		rt->dst.output = ip6_pkt_discard_out;
1460d8d1f30bSChangli Gao 		rt->dst.input = ip6_pkt_discard;
14611da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1462ef2c7d7bSNicolas Dichtel 		switch (cfg->fc_type) {
1463ef2c7d7bSNicolas Dichtel 		case RTN_BLACKHOLE:
1464ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EINVAL;
1465ef2c7d7bSNicolas Dichtel 			break;
1466ef2c7d7bSNicolas Dichtel 		case RTN_PROHIBIT:
1467ef2c7d7bSNicolas Dichtel 			rt->dst.error = -EACCES;
1468ef2c7d7bSNicolas Dichtel 			break;
1469b4949ab2SNicolas Dichtel 		case RTN_THROW:
1470b4949ab2SNicolas Dichtel 			rt->dst.error = -EAGAIN;
1471b4949ab2SNicolas Dichtel 			break;
1472ef2c7d7bSNicolas Dichtel 		default:
1473ef2c7d7bSNicolas Dichtel 			rt->dst.error = -ENETUNREACH;
1474ef2c7d7bSNicolas Dichtel 			break;
1475ef2c7d7bSNicolas Dichtel 		}
14761da177e4SLinus Torvalds 		goto install_route;
14771da177e4SLinus Torvalds 	}
14781da177e4SLinus Torvalds 
147986872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1480b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
14811da177e4SLinus Torvalds 		int gwa_type;
14821da177e4SLinus Torvalds 
148386872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
14844e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
14851da177e4SLinus Torvalds 		gwa_type = ipv6_addr_type(gw_addr);
14861da177e4SLinus Torvalds 
14871da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
14881da177e4SLinus Torvalds 			struct rt6_info *grt;
14891da177e4SLinus Torvalds 
14901da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
14911da177e4SLinus Torvalds 			   addresses as nexthop address.
14921da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
14931da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
14941da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
14951da177e4SLinus Torvalds 			   some exceptions. --ANK
14961da177e4SLinus Torvalds 			 */
14971da177e4SLinus Torvalds 			err = -EINVAL;
14981da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
14991da177e4SLinus Torvalds 				goto out;
15001da177e4SLinus Torvalds 
15015578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
15021da177e4SLinus Torvalds 
15031da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
150438308473SDavid S. Miller 			if (!grt)
15051da177e4SLinus Torvalds 				goto out;
15061da177e4SLinus Torvalds 			if (dev) {
1507d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
1508d8d1f30bSChangli Gao 					dst_release(&grt->dst);
15091da177e4SLinus Torvalds 					goto out;
15101da177e4SLinus Torvalds 				}
15111da177e4SLinus Torvalds 			} else {
1512d1918542SDavid S. Miller 				dev = grt->dst.dev;
15131da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
15141da177e4SLinus Torvalds 				dev_hold(dev);
15151da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
15161da177e4SLinus Torvalds 			}
15171da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
15181da177e4SLinus Torvalds 				err = 0;
1519d8d1f30bSChangli Gao 			dst_release(&grt->dst);
15201da177e4SLinus Torvalds 
15211da177e4SLinus Torvalds 			if (err)
15221da177e4SLinus Torvalds 				goto out;
15231da177e4SLinus Torvalds 		}
15241da177e4SLinus Torvalds 		err = -EINVAL;
152538308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
15261da177e4SLinus Torvalds 			goto out;
15271da177e4SLinus Torvalds 	}
15281da177e4SLinus Torvalds 
15291da177e4SLinus Torvalds 	err = -ENODEV;
153038308473SDavid S. Miller 	if (!dev)
15311da177e4SLinus Torvalds 		goto out;
15321da177e4SLinus Torvalds 
1533c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1534c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1535c3968a85SDaniel Walter 			err = -EINVAL;
1536c3968a85SDaniel Walter 			goto out;
1537c3968a85SDaniel Walter 		}
15384e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1539c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1540c3968a85SDaniel Walter 	} else
1541c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1542c3968a85SDaniel Walter 
154386872cb5SThomas Graf 	if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
15448ade06c6SDavid S. Miller 		err = rt6_bind_neighbour(rt, dev);
1545f83c7790SDavid S. Miller 		if (err)
15461da177e4SLinus Torvalds 			goto out;
15471da177e4SLinus Torvalds 	}
15481da177e4SLinus Torvalds 
154986872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
15501da177e4SLinus Torvalds 
15511da177e4SLinus Torvalds install_route:
155286872cb5SThomas Graf 	if (cfg->fc_mx) {
155386872cb5SThomas Graf 		struct nlattr *nla;
155486872cb5SThomas Graf 		int remaining;
15551da177e4SLinus Torvalds 
155686872cb5SThomas Graf 		nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
15578f4c1f9bSThomas Graf 			int type = nla_type(nla);
155886872cb5SThomas Graf 
155986872cb5SThomas Graf 			if (type) {
156086872cb5SThomas Graf 				if (type > RTAX_MAX) {
15611da177e4SLinus Torvalds 					err = -EINVAL;
15621da177e4SLinus Torvalds 					goto out;
15631da177e4SLinus Torvalds 				}
156486872cb5SThomas Graf 
1565defb3519SDavid S. Miller 				dst_metric_set(&rt->dst, type, nla_get_u32(nla));
15661da177e4SLinus Torvalds 			}
15671da177e4SLinus Torvalds 		}
15681da177e4SLinus Torvalds 	}
15691da177e4SLinus Torvalds 
1570d8d1f30bSChangli Gao 	rt->dst.dev = dev;
15711da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1572c71099acSThomas Graf 	rt->rt6i_table = table;
157363152fc0SDaniel Lezcano 
1574c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
157563152fc0SDaniel Lezcano 
157686872cb5SThomas Graf 	return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
15771da177e4SLinus Torvalds 
15781da177e4SLinus Torvalds out:
15791da177e4SLinus Torvalds 	if (dev)
15801da177e4SLinus Torvalds 		dev_put(dev);
15811da177e4SLinus Torvalds 	if (idev)
15821da177e4SLinus Torvalds 		in6_dev_put(idev);
15831da177e4SLinus Torvalds 	if (rt)
1584d8d1f30bSChangli Gao 		dst_free(&rt->dst);
15851da177e4SLinus Torvalds 	return err;
15861da177e4SLinus Torvalds }
15871da177e4SLinus Torvalds 
158886872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
15891da177e4SLinus Torvalds {
15901da177e4SLinus Torvalds 	int err;
1591c71099acSThomas Graf 	struct fib6_table *table;
1592d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
15931da177e4SLinus Torvalds 
15948ed67789SDaniel Lezcano 	if (rt == net->ipv6.ip6_null_entry)
15956c813a72SPatrick McHardy 		return -ENOENT;
15966c813a72SPatrick McHardy 
1597c71099acSThomas Graf 	table = rt->rt6i_table;
1598c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
15991da177e4SLinus Torvalds 
160086872cb5SThomas Graf 	err = fib6_del(rt, info);
1601d8d1f30bSChangli Gao 	dst_release(&rt->dst);
16021da177e4SLinus Torvalds 
1603c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
16041da177e4SLinus Torvalds 
16051da177e4SLinus Torvalds 	return err;
16061da177e4SLinus Torvalds }
16071da177e4SLinus Torvalds 
1608e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1609e0a1ad73SThomas Graf {
16104d1169c1SDenis V. Lunev 	struct nl_info info = {
1611d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
16124d1169c1SDenis V. Lunev 	};
1613528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1614e0a1ad73SThomas Graf }
1615e0a1ad73SThomas Graf 
161686872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
16171da177e4SLinus Torvalds {
1618c71099acSThomas Graf 	struct fib6_table *table;
16191da177e4SLinus Torvalds 	struct fib6_node *fn;
16201da177e4SLinus Torvalds 	struct rt6_info *rt;
16211da177e4SLinus Torvalds 	int err = -ESRCH;
16221da177e4SLinus Torvalds 
16235578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
162438308473SDavid S. Miller 	if (!table)
1625c71099acSThomas Graf 		return err;
16261da177e4SLinus Torvalds 
1627c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1628c71099acSThomas Graf 
1629c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
163086872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
163186872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
16321da177e4SLinus Torvalds 
16331da177e4SLinus Torvalds 	if (fn) {
1634d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
163586872cb5SThomas Graf 			if (cfg->fc_ifindex &&
1636d1918542SDavid S. Miller 			    (!rt->dst.dev ||
1637d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
16381da177e4SLinus Torvalds 				continue;
163986872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
164086872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
16411da177e4SLinus Torvalds 				continue;
164286872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
16431da177e4SLinus Torvalds 				continue;
1644d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1645c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
16461da177e4SLinus Torvalds 
164786872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
16481da177e4SLinus Torvalds 		}
16491da177e4SLinus Torvalds 	}
1650c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
16511da177e4SLinus Torvalds 
16521da177e4SLinus Torvalds 	return err;
16531da177e4SLinus Torvalds }
16541da177e4SLinus Torvalds 
16556700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
1656a6279458SYOSHIFUJI Hideaki {
1657e8599ff4SDavid S. Miller 	struct net *net = dev_net(skb->dev);
1658a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
1659e8599ff4SDavid S. Miller 	struct rt6_info *rt, *nrt = NULL;
1660e8599ff4SDavid S. Miller 	const struct in6_addr *target;
1661e8599ff4SDavid S. Miller 	struct ndisc_options ndopts;
16626e157b6aSDavid S. Miller 	const struct in6_addr *dest;
16636e157b6aSDavid S. Miller 	struct neighbour *old_neigh;
1664e8599ff4SDavid S. Miller 	struct inet6_dev *in6_dev;
1665e8599ff4SDavid S. Miller 	struct neighbour *neigh;
1666e8599ff4SDavid S. Miller 	struct icmp6hdr *icmph;
16676e157b6aSDavid S. Miller 	int optlen, on_link;
16686e157b6aSDavid S. Miller 	u8 *lladdr;
1669e8599ff4SDavid S. Miller 
1670e8599ff4SDavid S. Miller 	optlen = skb->tail - skb->transport_header;
1671e8599ff4SDavid S. Miller 	optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1672e8599ff4SDavid S. Miller 
1673e8599ff4SDavid S. Miller 	if (optlen < 0) {
16746e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
1675e8599ff4SDavid S. Miller 		return;
1676e8599ff4SDavid S. Miller 	}
1677e8599ff4SDavid S. Miller 
1678e8599ff4SDavid S. Miller 	icmph = icmp6_hdr(skb);
1679e8599ff4SDavid S. Miller 	target = (const struct in6_addr *) (icmph + 1);
1680e8599ff4SDavid S. Miller 	dest = target + 1;
1681e8599ff4SDavid S. Miller 
1682e8599ff4SDavid S. Miller 	if (ipv6_addr_is_multicast(dest)) {
16836e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
1684e8599ff4SDavid S. Miller 		return;
1685e8599ff4SDavid S. Miller 	}
1686e8599ff4SDavid S. Miller 
16876e157b6aSDavid S. Miller 	on_link = 0;
1688e8599ff4SDavid S. Miller 	if (ipv6_addr_equal(dest, target)) {
1689e8599ff4SDavid S. Miller 		on_link = 1;
1690e8599ff4SDavid S. Miller 	} else if (ipv6_addr_type(target) !=
1691e8599ff4SDavid S. Miller 		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
16926e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
1693e8599ff4SDavid S. Miller 		return;
1694e8599ff4SDavid S. Miller 	}
1695e8599ff4SDavid S. Miller 
1696e8599ff4SDavid S. Miller 	in6_dev = __in6_dev_get(skb->dev);
1697e8599ff4SDavid S. Miller 	if (!in6_dev)
1698e8599ff4SDavid S. Miller 		return;
1699e8599ff4SDavid S. Miller 	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1700e8599ff4SDavid S. Miller 		return;
1701e8599ff4SDavid S. Miller 
1702e8599ff4SDavid S. Miller 	/* RFC2461 8.1:
1703e8599ff4SDavid S. Miller 	 *	The IP source address of the Redirect MUST be the same as the current
1704e8599ff4SDavid S. Miller 	 *	first-hop router for the specified ICMP Destination Address.
1705e8599ff4SDavid S. Miller 	 */
1706e8599ff4SDavid S. Miller 
1707e8599ff4SDavid S. Miller 	if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1708e8599ff4SDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1709e8599ff4SDavid S. Miller 		return;
1710e8599ff4SDavid S. Miller 	}
17116e157b6aSDavid S. Miller 
17126e157b6aSDavid S. Miller 	lladdr = NULL;
1713e8599ff4SDavid S. Miller 	if (ndopts.nd_opts_tgt_lladdr) {
1714e8599ff4SDavid S. Miller 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1715e8599ff4SDavid S. Miller 					     skb->dev);
1716e8599ff4SDavid S. Miller 		if (!lladdr) {
1717e8599ff4SDavid S. Miller 			net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1718e8599ff4SDavid S. Miller 			return;
1719e8599ff4SDavid S. Miller 		}
1720e8599ff4SDavid S. Miller 	}
1721e8599ff4SDavid S. Miller 
17226e157b6aSDavid S. Miller 	rt = (struct rt6_info *) dst;
17236e157b6aSDavid S. Miller 	if (rt == net->ipv6.ip6_null_entry) {
17246e157b6aSDavid S. Miller 		net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
17256e157b6aSDavid S. Miller 		return;
17266e157b6aSDavid S. Miller 	}
17276e157b6aSDavid S. Miller 
17286e157b6aSDavid S. Miller 	/* Redirect received -> path was valid.
17296e157b6aSDavid S. Miller 	 * Look, redirects are sent only in response to data packets,
17306e157b6aSDavid S. Miller 	 * so that this nexthop apparently is reachable. --ANK
17316e157b6aSDavid S. Miller 	 */
17326e157b6aSDavid S. Miller 	dst_confirm(&rt->dst);
17336e157b6aSDavid S. Miller 
1734e8599ff4SDavid S. Miller 	neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1735e8599ff4SDavid S. Miller 	if (!neigh)
1736e8599ff4SDavid S. Miller 		return;
1737e8599ff4SDavid S. Miller 
17386e157b6aSDavid S. Miller 	/* Duplicate redirect: silently ignore. */
17396e157b6aSDavid S. Miller 	old_neigh = rt->n;
17406e157b6aSDavid S. Miller 	if (neigh == old_neigh)
1741a6279458SYOSHIFUJI Hideaki 		goto out;
17421da177e4SLinus Torvalds 
17431da177e4SLinus Torvalds 	/*
17441da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
17451da177e4SLinus Torvalds 	 */
17461da177e4SLinus Torvalds 
17471da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
17481da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
17491da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
17501da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
17511da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
17521da177e4SLinus Torvalds 		     );
17531da177e4SLinus Torvalds 
175421efcfa0SEric Dumazet 	nrt = ip6_rt_copy(rt, dest);
175538308473SDavid S. Miller 	if (!nrt)
17561da177e4SLinus Torvalds 		goto out;
17571da177e4SLinus Torvalds 
17581da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
17591da177e4SLinus Torvalds 	if (on_link)
17601da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
17611da177e4SLinus Torvalds 
17624e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
176397cac082SDavid S. Miller 	nrt->n = neigh_clone(neigh);
17641da177e4SLinus Torvalds 
176540e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
17661da177e4SLinus Torvalds 		goto out;
17671da177e4SLinus Torvalds 
1768d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
17691d248b1cSDavid S. Miller 	netevent.old_neigh = old_neigh;
1770d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
17711d248b1cSDavid S. Miller 	netevent.new_neigh = neigh;
17721d248b1cSDavid S. Miller 	netevent.daddr = dest;
17738d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
17748d71740cSTom Tucker 
17751da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
17766e157b6aSDavid S. Miller 		rt = (struct rt6_info *) dst_clone(&rt->dst);
1777e0a1ad73SThomas Graf 		ip6_del_rt(rt);
17781da177e4SLinus Torvalds 	}
17791da177e4SLinus Torvalds 
17801da177e4SLinus Torvalds out:
1781e8599ff4SDavid S. Miller 	neigh_release(neigh);
17826e157b6aSDavid S. Miller }
17836e157b6aSDavid S. Miller 
17841da177e4SLinus Torvalds /*
17851da177e4SLinus Torvalds  *	Misc support functions
17861da177e4SLinus Torvalds  */
17871da177e4SLinus Torvalds 
17881716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
178921efcfa0SEric Dumazet 				    const struct in6_addr *dest)
17901da177e4SLinus Torvalds {
1791d1918542SDavid S. Miller 	struct net *net = dev_net(ort->dst.dev);
17928b96d22dSDavid S. Miller 	struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
17938b96d22dSDavid S. Miller 					    ort->rt6i_table);
17941da177e4SLinus Torvalds 
17951da177e4SLinus Torvalds 	if (rt) {
1796d8d1f30bSChangli Gao 		rt->dst.input = ort->dst.input;
1797d8d1f30bSChangli Gao 		rt->dst.output = ort->dst.output;
17988e2ec639SYan, Zheng 		rt->dst.flags |= DST_HOST;
17991da177e4SLinus Torvalds 
18004e3fd7a0SAlexey Dobriyan 		rt->rt6i_dst.addr = *dest;
18018e2ec639SYan, Zheng 		rt->rt6i_dst.plen = 128;
1802defb3519SDavid S. Miller 		dst_copy_metrics(&rt->dst, &ort->dst);
1803d8d1f30bSChangli Gao 		rt->dst.error = ort->dst.error;
18041da177e4SLinus Torvalds 		rt->rt6i_idev = ort->rt6i_idev;
18051da177e4SLinus Torvalds 		if (rt->rt6i_idev)
18061da177e4SLinus Torvalds 			in6_dev_hold(rt->rt6i_idev);
1807d8d1f30bSChangli Gao 		rt->dst.lastuse = jiffies;
18081da177e4SLinus Torvalds 
18094e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
18101716a961SGao feng 		rt->rt6i_flags = ort->rt6i_flags;
18111716a961SGao feng 		if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
18121716a961SGao feng 		    (RTF_DEFAULT | RTF_ADDRCONF))
18131716a961SGao feng 			rt6_set_from(rt, ort);
18141716a961SGao feng 		else
18151716a961SGao feng 			rt6_clean_expires(rt);
18161da177e4SLinus Torvalds 		rt->rt6i_metric = 0;
18171da177e4SLinus Torvalds 
18181da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
18191da177e4SLinus Torvalds 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
18201da177e4SLinus Torvalds #endif
18210f6c6392SFlorian Westphal 		memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
1822c71099acSThomas Graf 		rt->rt6i_table = ort->rt6i_table;
18231da177e4SLinus Torvalds 	}
18241da177e4SLinus Torvalds 	return rt;
18251da177e4SLinus Torvalds }
18261da177e4SLinus Torvalds 
182770ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
1828efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
1829b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1830b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
183170ceb4f5SYOSHIFUJI Hideaki {
183270ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
183370ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
1834c71099acSThomas Graf 	struct fib6_table *table;
183570ceb4f5SYOSHIFUJI Hideaki 
1836efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
183738308473SDavid S. Miller 	if (!table)
1838c71099acSThomas Graf 		return NULL;
1839c71099acSThomas Graf 
1840*5744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
1841c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
184270ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
184370ceb4f5SYOSHIFUJI Hideaki 		goto out;
184470ceb4f5SYOSHIFUJI Hideaki 
1845d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1846d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
184770ceb4f5SYOSHIFUJI Hideaki 			continue;
184870ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
184970ceb4f5SYOSHIFUJI Hideaki 			continue;
185070ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
185170ceb4f5SYOSHIFUJI Hideaki 			continue;
1852d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
185370ceb4f5SYOSHIFUJI Hideaki 		break;
185470ceb4f5SYOSHIFUJI Hideaki 	}
185570ceb4f5SYOSHIFUJI Hideaki out:
1856*5744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
185770ceb4f5SYOSHIFUJI Hideaki 	return rt;
185870ceb4f5SYOSHIFUJI Hideaki }
185970ceb4f5SYOSHIFUJI Hideaki 
1860efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
1861b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1862b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
186395c96174SEric Dumazet 					   unsigned int pref)
186470ceb4f5SYOSHIFUJI Hideaki {
186586872cb5SThomas Graf 	struct fib6_config cfg = {
186686872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
1867238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
186886872cb5SThomas Graf 		.fc_ifindex	= ifindex,
186986872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
187086872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
187186872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
187215e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
1873efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
1874efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
187586872cb5SThomas Graf 	};
187670ceb4f5SYOSHIFUJI Hideaki 
18774e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
18784e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
187986872cb5SThomas Graf 
1880e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
1881e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
188286872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
188370ceb4f5SYOSHIFUJI Hideaki 
188486872cb5SThomas Graf 	ip6_route_add(&cfg);
188570ceb4f5SYOSHIFUJI Hideaki 
1886efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
188770ceb4f5SYOSHIFUJI Hideaki }
188870ceb4f5SYOSHIFUJI Hideaki #endif
188970ceb4f5SYOSHIFUJI Hideaki 
1890b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
18911da177e4SLinus Torvalds {
18921da177e4SLinus Torvalds 	struct rt6_info *rt;
1893c71099acSThomas Graf 	struct fib6_table *table;
18941da177e4SLinus Torvalds 
1895c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
189638308473SDavid S. Miller 	if (!table)
1897c71099acSThomas Graf 		return NULL;
18981da177e4SLinus Torvalds 
1899*5744dd9bSLi RongQing 	read_lock_bh(&table->tb6_lock);
1900d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
1901d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
1902045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
19031da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
19041da177e4SLinus Torvalds 			break;
19051da177e4SLinus Torvalds 	}
19061da177e4SLinus Torvalds 	if (rt)
1907d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
1908*5744dd9bSLi RongQing 	read_unlock_bh(&table->tb6_lock);
19091da177e4SLinus Torvalds 	return rt;
19101da177e4SLinus Torvalds }
19111da177e4SLinus Torvalds 
1912b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
1913ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
1914ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
19151da177e4SLinus Torvalds {
191686872cb5SThomas Graf 	struct fib6_config cfg = {
191786872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
1918238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
191986872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
192086872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
192186872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
192215e47304SEric W. Biederman 		.fc_nlinfo.portid = 0,
19235578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
1924c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
192586872cb5SThomas Graf 	};
19261da177e4SLinus Torvalds 
19274e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
19281da177e4SLinus Torvalds 
192986872cb5SThomas Graf 	ip6_route_add(&cfg);
19301da177e4SLinus Torvalds 
19311da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
19321da177e4SLinus Torvalds }
19331da177e4SLinus Torvalds 
19347b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
19351da177e4SLinus Torvalds {
19361da177e4SLinus Torvalds 	struct rt6_info *rt;
1937c71099acSThomas Graf 	struct fib6_table *table;
1938c71099acSThomas Graf 
1939c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
19407b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
194138308473SDavid S. Miller 	if (!table)
1942c71099acSThomas Graf 		return;
19431da177e4SLinus Torvalds 
19441da177e4SLinus Torvalds restart:
1945c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1946d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
19471da177e4SLinus Torvalds 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
1948d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1949c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
1950e0a1ad73SThomas Graf 			ip6_del_rt(rt);
19511da177e4SLinus Torvalds 			goto restart;
19521da177e4SLinus Torvalds 		}
19531da177e4SLinus Torvalds 	}
1954c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
19551da177e4SLinus Torvalds }
19561da177e4SLinus Torvalds 
19575578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
19585578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
195986872cb5SThomas Graf 				 struct fib6_config *cfg)
196086872cb5SThomas Graf {
196186872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
196286872cb5SThomas Graf 
196386872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
196486872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
196586872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
196686872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
196786872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
196886872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
196986872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
197086872cb5SThomas Graf 
19715578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
1972f1243c2dSBenjamin Thery 
19734e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
19744e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
19754e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
197686872cb5SThomas Graf }
197786872cb5SThomas Graf 
19785578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
19791da177e4SLinus Torvalds {
198086872cb5SThomas Graf 	struct fib6_config cfg;
19811da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
19821da177e4SLinus Torvalds 	int err;
19831da177e4SLinus Torvalds 
19841da177e4SLinus Torvalds 	switch(cmd) {
19851da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
19861da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
19871da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
19881da177e4SLinus Torvalds 			return -EPERM;
19891da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
19901da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
19911da177e4SLinus Torvalds 		if (err)
19921da177e4SLinus Torvalds 			return -EFAULT;
19931da177e4SLinus Torvalds 
19945578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
199586872cb5SThomas Graf 
19961da177e4SLinus Torvalds 		rtnl_lock();
19971da177e4SLinus Torvalds 		switch (cmd) {
19981da177e4SLinus Torvalds 		case SIOCADDRT:
199986872cb5SThomas Graf 			err = ip6_route_add(&cfg);
20001da177e4SLinus Torvalds 			break;
20011da177e4SLinus Torvalds 		case SIOCDELRT:
200286872cb5SThomas Graf 			err = ip6_route_del(&cfg);
20031da177e4SLinus Torvalds 			break;
20041da177e4SLinus Torvalds 		default:
20051da177e4SLinus Torvalds 			err = -EINVAL;
20061da177e4SLinus Torvalds 		}
20071da177e4SLinus Torvalds 		rtnl_unlock();
20081da177e4SLinus Torvalds 
20091da177e4SLinus Torvalds 		return err;
20103ff50b79SStephen Hemminger 	}
20111da177e4SLinus Torvalds 
20121da177e4SLinus Torvalds 	return -EINVAL;
20131da177e4SLinus Torvalds }
20141da177e4SLinus Torvalds 
20151da177e4SLinus Torvalds /*
20161da177e4SLinus Torvalds  *	Drop the packet on the floor
20171da177e4SLinus Torvalds  */
20181da177e4SLinus Torvalds 
2019d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
20201da177e4SLinus Torvalds {
2021612f09e8SYOSHIFUJI Hideaki 	int type;
2022adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2023612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2024612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
20250660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
202645bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
20273bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
20283bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2029612f09e8SYOSHIFUJI Hideaki 			break;
2030612f09e8SYOSHIFUJI Hideaki 		}
2031612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2032612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
20333bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
20343bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2035612f09e8SYOSHIFUJI Hideaki 		break;
2036612f09e8SYOSHIFUJI Hideaki 	}
20373ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
20381da177e4SLinus Torvalds 	kfree_skb(skb);
20391da177e4SLinus Torvalds 	return 0;
20401da177e4SLinus Torvalds }
20411da177e4SLinus Torvalds 
20429ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
20439ce8ade0SThomas Graf {
2044612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
20459ce8ade0SThomas Graf }
20469ce8ade0SThomas Graf 
204720380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb)
20481da177e4SLinus Torvalds {
2049adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2050612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
20511da177e4SLinus Torvalds }
20521da177e4SLinus Torvalds 
20536723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES
20546723ab54SDavid S. Miller 
20559ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
20569ce8ade0SThomas Graf {
2057612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
20589ce8ade0SThomas Graf }
20599ce8ade0SThomas Graf 
20609ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb)
20619ce8ade0SThomas Graf {
2062adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2063612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
20649ce8ade0SThomas Graf }
20659ce8ade0SThomas Graf 
20666723ab54SDavid S. Miller #endif
20676723ab54SDavid S. Miller 
20681da177e4SLinus Torvalds /*
20691da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
20701da177e4SLinus Torvalds  */
20711da177e4SLinus Torvalds 
20721da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
20731da177e4SLinus Torvalds 				    const struct in6_addr *addr,
20748f031519SDavid S. Miller 				    bool anycast)
20751da177e4SLinus Torvalds {
2076c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
20778b96d22dSDavid S. Miller 	struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
2078f83c7790SDavid S. Miller 	int err;
20791da177e4SLinus Torvalds 
208038308473SDavid S. Miller 	if (!rt) {
2081f3213831SJoe Perches 		net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
20821da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
208340385653SBen Greear 	}
20841da177e4SLinus Torvalds 
20851da177e4SLinus Torvalds 	in6_dev_hold(idev);
20861da177e4SLinus Torvalds 
208711d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2088d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2089d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
20901da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
2091d8d1f30bSChangli Gao 	rt->dst.obsolete = -1;
20921da177e4SLinus Torvalds 
20931da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
209458c4fb86SYOSHIFUJI Hideaki 	if (anycast)
209558c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
209658c4fb86SYOSHIFUJI Hideaki 	else
20971da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
20988ade06c6SDavid S. Miller 	err = rt6_bind_neighbour(rt, rt->dst.dev);
2099f83c7790SDavid S. Miller 	if (err) {
2100d8d1f30bSChangli Gao 		dst_free(&rt->dst);
2101f83c7790SDavid S. Miller 		return ERR_PTR(err);
21021da177e4SLinus Torvalds 	}
21031da177e4SLinus Torvalds 
21044e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
21051da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
21065578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
21071da177e4SLinus Torvalds 
2108d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
21091da177e4SLinus Torvalds 
21101da177e4SLinus Torvalds 	return rt;
21111da177e4SLinus Torvalds }
21121da177e4SLinus Torvalds 
2113c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2114c3968a85SDaniel Walter 			struct rt6_info *rt,
2115b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2116c3968a85SDaniel Walter 			unsigned int prefs,
2117c3968a85SDaniel Walter 			struct in6_addr *saddr)
2118c3968a85SDaniel Walter {
2119c3968a85SDaniel Walter 	struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2120c3968a85SDaniel Walter 	int err = 0;
2121c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen)
21224e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2123c3968a85SDaniel Walter 	else
2124c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2125c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2126c3968a85SDaniel Walter 	return err;
2127c3968a85SDaniel Walter }
2128c3968a85SDaniel Walter 
2129c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2130c3968a85SDaniel Walter struct arg_dev_net_ip {
2131c3968a85SDaniel Walter 	struct net_device *dev;
2132c3968a85SDaniel Walter 	struct net *net;
2133c3968a85SDaniel Walter 	struct in6_addr *addr;
2134c3968a85SDaniel Walter };
2135c3968a85SDaniel Walter 
2136c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2137c3968a85SDaniel Walter {
2138c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2139c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2140c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2141c3968a85SDaniel Walter 
2142d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2143c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2144c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2145c3968a85SDaniel Walter 		/* remove prefsrc entry */
2146c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2147c3968a85SDaniel Walter 	}
2148c3968a85SDaniel Walter 	return 0;
2149c3968a85SDaniel Walter }
2150c3968a85SDaniel Walter 
2151c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2152c3968a85SDaniel Walter {
2153c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2154c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2155c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2156c3968a85SDaniel Walter 		.net = net,
2157c3968a85SDaniel Walter 		.addr = &ifp->addr,
2158c3968a85SDaniel Walter 	};
2159c3968a85SDaniel Walter 	fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2160c3968a85SDaniel Walter }
2161c3968a85SDaniel Walter 
21628ed67789SDaniel Lezcano struct arg_dev_net {
21638ed67789SDaniel Lezcano 	struct net_device *dev;
21648ed67789SDaniel Lezcano 	struct net *net;
21658ed67789SDaniel Lezcano };
21668ed67789SDaniel Lezcano 
21671da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
21681da177e4SLinus Torvalds {
2169bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2170bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
21718ed67789SDaniel Lezcano 
2172d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2173c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
21741da177e4SLinus Torvalds 		return -1;
2175c159d30cSDavid S. Miller 
21761da177e4SLinus Torvalds 	return 0;
21771da177e4SLinus Torvalds }
21781da177e4SLinus Torvalds 
2179f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
21801da177e4SLinus Torvalds {
21818ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
21828ed67789SDaniel Lezcano 		.dev = dev,
21838ed67789SDaniel Lezcano 		.net = net,
21848ed67789SDaniel Lezcano 	};
21858ed67789SDaniel Lezcano 
21868ed67789SDaniel Lezcano 	fib6_clean_all(net, fib6_ifdown, 0, &adn);
21871e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
21881da177e4SLinus Torvalds }
21891da177e4SLinus Torvalds 
219095c96174SEric Dumazet struct rt6_mtu_change_arg {
21911da177e4SLinus Torvalds 	struct net_device *dev;
219295c96174SEric Dumazet 	unsigned int mtu;
21931da177e4SLinus Torvalds };
21941da177e4SLinus Torvalds 
21951da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
21961da177e4SLinus Torvalds {
21971da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
21981da177e4SLinus Torvalds 	struct inet6_dev *idev;
21991da177e4SLinus Torvalds 
22001da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
22011da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
22021da177e4SLinus Torvalds 	   We still use this lock to block changes
22031da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
22041da177e4SLinus Torvalds 	*/
22051da177e4SLinus Torvalds 
22061da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
220738308473SDavid S. Miller 	if (!idev)
22081da177e4SLinus Torvalds 		return 0;
22091da177e4SLinus Torvalds 
22101da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
22111da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
22121da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
22131da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
22141da177e4SLinus Torvalds 	 */
22151da177e4SLinus Torvalds 	/*
22161da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
22171da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
22181da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
22191da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
22201da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
22211da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
22221da177e4SLinus Torvalds 	   PMTU discouvery.
22231da177e4SLinus Torvalds 	 */
2224d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
2225d8d1f30bSChangli Gao 	    !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2226d8d1f30bSChangli Gao 	    (dst_mtu(&rt->dst) >= arg->mtu ||
2227d8d1f30bSChangli Gao 	     (dst_mtu(&rt->dst) < arg->mtu &&
2228d8d1f30bSChangli Gao 	      dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
2229defb3519SDavid S. Miller 		dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2230566cfd8fSSimon Arlott 	}
22311da177e4SLinus Torvalds 	return 0;
22321da177e4SLinus Torvalds }
22331da177e4SLinus Torvalds 
223495c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
22351da177e4SLinus Torvalds {
2236c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2237c71099acSThomas Graf 		.dev = dev,
2238c71099acSThomas Graf 		.mtu = mtu,
2239c71099acSThomas Graf 	};
22401da177e4SLinus Torvalds 
2241c346dca1SYOSHIFUJI Hideaki 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
22421da177e4SLinus Torvalds }
22431da177e4SLinus Torvalds 
2244ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
22455176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
224686872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2247ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
224886872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
224986872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
225086872cb5SThomas Graf };
225186872cb5SThomas Graf 
225286872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
225386872cb5SThomas Graf 			      struct fib6_config *cfg)
22541da177e4SLinus Torvalds {
225586872cb5SThomas Graf 	struct rtmsg *rtm;
225686872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
225786872cb5SThomas Graf 	int err;
22581da177e4SLinus Torvalds 
225986872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
226086872cb5SThomas Graf 	if (err < 0)
226186872cb5SThomas Graf 		goto errout;
22621da177e4SLinus Torvalds 
226386872cb5SThomas Graf 	err = -EINVAL;
226486872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
226586872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
226686872cb5SThomas Graf 
226786872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
226886872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
226986872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
227086872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
227186872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
2272ef2c7d7bSNicolas Dichtel 	cfg->fc_type = rtm->rtm_type;
227386872cb5SThomas Graf 
2274ef2c7d7bSNicolas Dichtel 	if (rtm->rtm_type == RTN_UNREACHABLE ||
2275ef2c7d7bSNicolas Dichtel 	    rtm->rtm_type == RTN_BLACKHOLE ||
2276b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_PROHIBIT ||
2277b4949ab2SNicolas Dichtel 	    rtm->rtm_type == RTN_THROW)
227886872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
227986872cb5SThomas Graf 
2280ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2281ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2282ab79ad14SMaciej Żenczykowski 
228315e47304SEric W. Biederman 	cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
228486872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
22853b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
228686872cb5SThomas Graf 
228786872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
228886872cb5SThomas Graf 		nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
228986872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
22901da177e4SLinus Torvalds 	}
229186872cb5SThomas Graf 
229286872cb5SThomas Graf 	if (tb[RTA_DST]) {
229386872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
229486872cb5SThomas Graf 
229586872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
229686872cb5SThomas Graf 			goto errout;
229786872cb5SThomas Graf 
229886872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
22991da177e4SLinus Torvalds 	}
230086872cb5SThomas Graf 
230186872cb5SThomas Graf 	if (tb[RTA_SRC]) {
230286872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
230386872cb5SThomas Graf 
230486872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
230586872cb5SThomas Graf 			goto errout;
230686872cb5SThomas Graf 
230786872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
23081da177e4SLinus Torvalds 	}
230986872cb5SThomas Graf 
2310c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
2311c3968a85SDaniel Walter 		nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2312c3968a85SDaniel Walter 
231386872cb5SThomas Graf 	if (tb[RTA_OIF])
231486872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
231586872cb5SThomas Graf 
231686872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
231786872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
231886872cb5SThomas Graf 
231986872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
232086872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
232186872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
23221da177e4SLinus Torvalds 	}
232386872cb5SThomas Graf 
232486872cb5SThomas Graf 	if (tb[RTA_TABLE])
232586872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
232686872cb5SThomas Graf 
232786872cb5SThomas Graf 	err = 0;
232886872cb5SThomas Graf errout:
232986872cb5SThomas Graf 	return err;
23301da177e4SLinus Torvalds }
23311da177e4SLinus Torvalds 
2332c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
23331da177e4SLinus Torvalds {
233486872cb5SThomas Graf 	struct fib6_config cfg;
233586872cb5SThomas Graf 	int err;
23361da177e4SLinus Torvalds 
233786872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
233886872cb5SThomas Graf 	if (err < 0)
233986872cb5SThomas Graf 		return err;
234086872cb5SThomas Graf 
234186872cb5SThomas Graf 	return ip6_route_del(&cfg);
23421da177e4SLinus Torvalds }
23431da177e4SLinus Torvalds 
2344c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
23451da177e4SLinus Torvalds {
234686872cb5SThomas Graf 	struct fib6_config cfg;
234786872cb5SThomas Graf 	int err;
23481da177e4SLinus Torvalds 
234986872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
235086872cb5SThomas Graf 	if (err < 0)
235186872cb5SThomas Graf 		return err;
235286872cb5SThomas Graf 
235386872cb5SThomas Graf 	return ip6_route_add(&cfg);
23541da177e4SLinus Torvalds }
23551da177e4SLinus Torvalds 
2356339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void)
2357339bf98fSThomas Graf {
2358339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2359339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2360339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2361339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2362339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2363339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2364339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2365339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2366339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
23676a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2368339bf98fSThomas Graf 	       + nla_total_size(sizeof(struct rta_cacheinfo));
2369339bf98fSThomas Graf }
2370339bf98fSThomas Graf 
2371191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2372191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
23730d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
237415e47304SEric W. Biederman 			 int iif, int type, u32 portid, u32 seq,
23757bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
23761da177e4SLinus Torvalds {
23771da177e4SLinus Torvalds 	struct rtmsg *rtm;
23781da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2379e3703b3dSThomas Graf 	long expires;
23809e762a4aSPatrick McHardy 	u32 table;
2381f2c31e32SEric Dumazet 	struct neighbour *n;
23821da177e4SLinus Torvalds 
23831da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
23841da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
23851da177e4SLinus Torvalds 			/* success since this is not a prefix route */
23861da177e4SLinus Torvalds 			return 1;
23871da177e4SLinus Torvalds 		}
23881da177e4SLinus Torvalds 	}
23891da177e4SLinus Torvalds 
239015e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
239138308473SDavid S. Miller 	if (!nlh)
239226932566SPatrick McHardy 		return -EMSGSIZE;
23932d7202bfSThomas Graf 
23942d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
23951da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
23961da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
23971da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
23981da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2399c71099acSThomas Graf 	if (rt->rt6i_table)
24009e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2401c71099acSThomas Graf 	else
24029e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
24039e762a4aSPatrick McHardy 	rtm->rtm_table = table;
2404c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_TABLE, table))
2405c78679e8SDavid S. Miller 		goto nla_put_failure;
2406ef2c7d7bSNicolas Dichtel 	if (rt->rt6i_flags & RTF_REJECT) {
2407ef2c7d7bSNicolas Dichtel 		switch (rt->dst.error) {
2408ef2c7d7bSNicolas Dichtel 		case -EINVAL:
2409ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_BLACKHOLE;
2410ef2c7d7bSNicolas Dichtel 			break;
2411ef2c7d7bSNicolas Dichtel 		case -EACCES:
2412ef2c7d7bSNicolas Dichtel 			rtm->rtm_type = RTN_PROHIBIT;
2413ef2c7d7bSNicolas Dichtel 			break;
2414b4949ab2SNicolas Dichtel 		case -EAGAIN:
2415b4949ab2SNicolas Dichtel 			rtm->rtm_type = RTN_THROW;
2416b4949ab2SNicolas Dichtel 			break;
2417ef2c7d7bSNicolas Dichtel 		default:
24181da177e4SLinus Torvalds 			rtm->rtm_type = RTN_UNREACHABLE;
2419ef2c7d7bSNicolas Dichtel 			break;
2420ef2c7d7bSNicolas Dichtel 		}
2421ef2c7d7bSNicolas Dichtel 	}
2422ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2423ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2424d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
24251da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
24261da177e4SLinus Torvalds 	else
24271da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
24281da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
24291da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
24301da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
24311da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
24321da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
2433f0396f60SDenis Ovsienko 	else if (rt->rt6i_flags & RTF_ADDRCONF) {
2434f0396f60SDenis Ovsienko 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
24351da177e4SLinus Torvalds 			rtm->rtm_protocol = RTPROT_RA;
2436f0396f60SDenis Ovsienko 		else
2437f0396f60SDenis Ovsienko 			rtm->rtm_protocol = RTPROT_KERNEL;
2438f0396f60SDenis Ovsienko 	}
24391da177e4SLinus Torvalds 
24401da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
24411da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
24421da177e4SLinus Torvalds 
24431da177e4SLinus Torvalds 	if (dst) {
2444c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_DST, 16, dst))
2445c78679e8SDavid S. Miller 			goto nla_put_failure;
24461da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
24471da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
2448c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2449c78679e8SDavid S. Miller 			goto nla_put_failure;
24501da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
24511da177e4SLinus Torvalds 	if (src) {
2452c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_SRC, 16, src))
2453c78679e8SDavid S. Miller 			goto nla_put_failure;
24541da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
2455c78679e8SDavid S. Miller 	} else if (rtm->rtm_src_len &&
2456c78679e8SDavid S. Miller 		   nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2457c78679e8SDavid S. Miller 		goto nla_put_failure;
24581da177e4SLinus Torvalds #endif
24597bc570c8SYOSHIFUJI Hideaki 	if (iif) {
24607bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
24617bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
24628229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
24637bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
24647bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
24657bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
24667bc570c8SYOSHIFUJI Hideaki 						return 0;
24677bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
24687bc570c8SYOSHIFUJI Hideaki 				} else {
24697bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
24707bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
24717bc570c8SYOSHIFUJI Hideaki 				}
24727bc570c8SYOSHIFUJI Hideaki 			}
24737bc570c8SYOSHIFUJI Hideaki 		} else
24747bc570c8SYOSHIFUJI Hideaki #endif
2475c78679e8SDavid S. Miller 			if (nla_put_u32(skb, RTA_IIF, iif))
2476c78679e8SDavid S. Miller 				goto nla_put_failure;
24777bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
24781da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2479c78679e8SDavid S. Miller 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2480c78679e8SDavid S. Miller 		    nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2481c78679e8SDavid S. Miller 			goto nla_put_failure;
2482c3968a85SDaniel Walter 	}
2483c3968a85SDaniel Walter 
2484c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2485c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
24864e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
2487c78679e8SDavid S. Miller 		if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2488c78679e8SDavid S. Miller 			goto nla_put_failure;
24891da177e4SLinus Torvalds 	}
24902d7202bfSThomas Graf 
2491defb3519SDavid S. Miller 	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
24922d7202bfSThomas Graf 		goto nla_put_failure;
24932d7202bfSThomas Graf 
249497cac082SDavid S. Miller 	n = rt->n;
249594f826b8SEric Dumazet 	if (n) {
2496fdd6681dSAmerigo Wang 		if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0)
249794f826b8SEric Dumazet 			goto nla_put_failure;
249894f826b8SEric Dumazet 	}
24992d7202bfSThomas Graf 
2500c78679e8SDavid S. Miller 	if (rt->dst.dev &&
2501c78679e8SDavid S. Miller 	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2502c78679e8SDavid S. Miller 		goto nla_put_failure;
2503c78679e8SDavid S. Miller 	if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2504c78679e8SDavid S. Miller 		goto nla_put_failure;
25058253947eSLi Wei 
25068253947eSLi Wei 	expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
250769cdf8f9SYOSHIFUJI Hideaki 
250887a50699SDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
2509e3703b3dSThomas Graf 		goto nla_put_failure;
25101da177e4SLinus Torvalds 
25112d7202bfSThomas Graf 	return nlmsg_end(skb, nlh);
25122d7202bfSThomas Graf 
25132d7202bfSThomas Graf nla_put_failure:
251426932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
251526932566SPatrick McHardy 	return -EMSGSIZE;
25161da177e4SLinus Torvalds }
25171da177e4SLinus Torvalds 
25181b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
25191da177e4SLinus Torvalds {
25201da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
25211da177e4SLinus Torvalds 	int prefix;
25221da177e4SLinus Torvalds 
25232d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
25242d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
25251da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
25261da177e4SLinus Torvalds 	} else
25271da177e4SLinus Torvalds 		prefix = 0;
25281da177e4SLinus Torvalds 
2529191cd582SBrian Haley 	return rt6_fill_node(arg->net,
2530191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
253115e47304SEric W. Biederman 		     NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
25327bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
25331da177e4SLinus Torvalds }
25341da177e4SLinus Torvalds 
2535c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
25361da177e4SLinus Torvalds {
25373b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
2538ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
25391da177e4SLinus Torvalds 	struct rt6_info *rt;
2540ab364a6fSThomas Graf 	struct sk_buff *skb;
2541ab364a6fSThomas Graf 	struct rtmsg *rtm;
25424c9483b2SDavid S. Miller 	struct flowi6 fl6;
254372331bc0SShmulik Ladkani 	int err, iif = 0, oif = 0;
2544ab364a6fSThomas Graf 
2545ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2546ab364a6fSThomas Graf 	if (err < 0)
2547ab364a6fSThomas Graf 		goto errout;
2548ab364a6fSThomas Graf 
2549ab364a6fSThomas Graf 	err = -EINVAL;
25504c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2551ab364a6fSThomas Graf 
2552ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
2553ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2554ab364a6fSThomas Graf 			goto errout;
2555ab364a6fSThomas Graf 
25564e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
2557ab364a6fSThomas Graf 	}
2558ab364a6fSThomas Graf 
2559ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
2560ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2561ab364a6fSThomas Graf 			goto errout;
2562ab364a6fSThomas Graf 
25634e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
2564ab364a6fSThomas Graf 	}
2565ab364a6fSThomas Graf 
2566ab364a6fSThomas Graf 	if (tb[RTA_IIF])
2567ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
2568ab364a6fSThomas Graf 
2569ab364a6fSThomas Graf 	if (tb[RTA_OIF])
257072331bc0SShmulik Ladkani 		oif = nla_get_u32(tb[RTA_OIF]);
2571ab364a6fSThomas Graf 
2572ab364a6fSThomas Graf 	if (iif) {
2573ab364a6fSThomas Graf 		struct net_device *dev;
257472331bc0SShmulik Ladkani 		int flags = 0;
257572331bc0SShmulik Ladkani 
25765578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
2577ab364a6fSThomas Graf 		if (!dev) {
2578ab364a6fSThomas Graf 			err = -ENODEV;
2579ab364a6fSThomas Graf 			goto errout;
2580ab364a6fSThomas Graf 		}
258172331bc0SShmulik Ladkani 
258272331bc0SShmulik Ladkani 		fl6.flowi6_iif = iif;
258372331bc0SShmulik Ladkani 
258472331bc0SShmulik Ladkani 		if (!ipv6_addr_any(&fl6.saddr))
258572331bc0SShmulik Ladkani 			flags |= RT6_LOOKUP_F_HAS_SADDR;
258672331bc0SShmulik Ladkani 
258772331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
258872331bc0SShmulik Ladkani 							       flags);
258972331bc0SShmulik Ladkani 	} else {
259072331bc0SShmulik Ladkani 		fl6.flowi6_oif = oif;
259172331bc0SShmulik Ladkani 
259272331bc0SShmulik Ladkani 		rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
2593ab364a6fSThomas Graf 	}
25941da177e4SLinus Torvalds 
25951da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
259638308473SDavid S. Miller 	if (!skb) {
25972173bff5SShmulik Ladkani 		dst_release(&rt->dst);
2598ab364a6fSThomas Graf 		err = -ENOBUFS;
2599ab364a6fSThomas Graf 		goto errout;
2600ab364a6fSThomas Graf 	}
26011da177e4SLinus Torvalds 
26021da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
26031da177e4SLinus Torvalds 	   through good chunk of routing engine.
26041da177e4SLinus Torvalds 	 */
2605459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
26061da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
26071da177e4SLinus Torvalds 
2608d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
26091da177e4SLinus Torvalds 
26104c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
261115e47304SEric W. Biederman 			    RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
26127bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
26131da177e4SLinus Torvalds 	if (err < 0) {
2614ab364a6fSThomas Graf 		kfree_skb(skb);
2615ab364a6fSThomas Graf 		goto errout;
26161da177e4SLinus Torvalds 	}
26171da177e4SLinus Torvalds 
261815e47304SEric W. Biederman 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
2619ab364a6fSThomas Graf errout:
26201da177e4SLinus Torvalds 	return err;
26211da177e4SLinus Torvalds }
26221da177e4SLinus Torvalds 
262386872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
26241da177e4SLinus Torvalds {
26251da177e4SLinus Torvalds 	struct sk_buff *skb;
26265578689aSDaniel Lezcano 	struct net *net = info->nl_net;
2627528c4cebSDenis V. Lunev 	u32 seq;
2628528c4cebSDenis V. Lunev 	int err;
26290d51aa80SJamal Hadi Salim 
2630528c4cebSDenis V. Lunev 	err = -ENOBUFS;
263138308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
263286872cb5SThomas Graf 
2633339bf98fSThomas Graf 	skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
263438308473SDavid S. Miller 	if (!skb)
263521713ebcSThomas Graf 		goto errout;
26361da177e4SLinus Torvalds 
2637191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
263815e47304SEric W. Biederman 				event, info->portid, seq, 0, 0, 0);
263926932566SPatrick McHardy 	if (err < 0) {
264026932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
264126932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
264226932566SPatrick McHardy 		kfree_skb(skb);
264326932566SPatrick McHardy 		goto errout;
264426932566SPatrick McHardy 	}
264515e47304SEric W. Biederman 	rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
26465578689aSDaniel Lezcano 		    info->nlh, gfp_any());
26471ce85fe4SPablo Neira Ayuso 	return;
264821713ebcSThomas Graf errout:
264921713ebcSThomas Graf 	if (err < 0)
26505578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
26511da177e4SLinus Torvalds }
26521da177e4SLinus Torvalds 
26538ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
26548ed67789SDaniel Lezcano 				unsigned long event, void *data)
26558ed67789SDaniel Lezcano {
26568ed67789SDaniel Lezcano 	struct net_device *dev = (struct net_device *)data;
2657c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
26588ed67789SDaniel Lezcano 
26598ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
2660d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
26618ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
26628ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2663d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
26648ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
2665d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
26668ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
26678ed67789SDaniel Lezcano #endif
26688ed67789SDaniel Lezcano 	}
26698ed67789SDaniel Lezcano 
26708ed67789SDaniel Lezcano 	return NOTIFY_OK;
26718ed67789SDaniel Lezcano }
26728ed67789SDaniel Lezcano 
26731da177e4SLinus Torvalds /*
26741da177e4SLinus Torvalds  *	/proc
26751da177e4SLinus Torvalds  */
26761da177e4SLinus Torvalds 
26771da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
26781da177e4SLinus Torvalds 
26791da177e4SLinus Torvalds struct rt6_proc_arg
26801da177e4SLinus Torvalds {
26811da177e4SLinus Torvalds 	char *buffer;
26821da177e4SLinus Torvalds 	int offset;
26831da177e4SLinus Torvalds 	int length;
26841da177e4SLinus Torvalds 	int skip;
26851da177e4SLinus Torvalds 	int len;
26861da177e4SLinus Torvalds };
26871da177e4SLinus Torvalds 
26881da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg)
26891da177e4SLinus Torvalds {
269033120b30SAlexey Dobriyan 	struct seq_file *m = p_arg;
269169cce1d1SDavid S. Miller 	struct neighbour *n;
26921da177e4SLinus Torvalds 
26934b7a4274SHarvey Harrison 	seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
26941da177e4SLinus Torvalds 
26951da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
26964b7a4274SHarvey Harrison 	seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
26971da177e4SLinus Torvalds #else
269833120b30SAlexey Dobriyan 	seq_puts(m, "00000000000000000000000000000000 00 ");
26991da177e4SLinus Torvalds #endif
270097cac082SDavid S. Miller 	n = rt->n;
270169cce1d1SDavid S. Miller 	if (n) {
270269cce1d1SDavid S. Miller 		seq_printf(m, "%pi6", n->primary_key);
27031da177e4SLinus Torvalds 	} else {
270433120b30SAlexey Dobriyan 		seq_puts(m, "00000000000000000000000000000000");
27051da177e4SLinus Torvalds 	}
270633120b30SAlexey Dobriyan 	seq_printf(m, " %08x %08x %08x %08x %8s\n",
2707d8d1f30bSChangli Gao 		   rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2708d8d1f30bSChangli Gao 		   rt->dst.__use, rt->rt6i_flags,
2709d1918542SDavid S. Miller 		   rt->dst.dev ? rt->dst.dev->name : "");
27101da177e4SLinus Torvalds 	return 0;
27111da177e4SLinus Torvalds }
27121da177e4SLinus Torvalds 
271333120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v)
27141da177e4SLinus Torvalds {
2715f3db4851SDaniel Lezcano 	struct net *net = (struct net *)m->private;
271632b293a5SJosh Hunt 	fib6_clean_all_ro(net, rt6_info_route, 0, m);
271733120b30SAlexey Dobriyan 	return 0;
27181da177e4SLinus Torvalds }
27191da177e4SLinus Torvalds 
272033120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file)
272133120b30SAlexey Dobriyan {
2722de05c557SPavel Emelyanov 	return single_open_net(inode, file, ipv6_route_show);
2723f3db4851SDaniel Lezcano }
2724f3db4851SDaniel Lezcano 
272533120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
272633120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
272733120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
272833120b30SAlexey Dobriyan 	.read		= seq_read,
272933120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
2730b6fcbdb4SPavel Emelyanov 	.release	= single_release_net,
273133120b30SAlexey Dobriyan };
273233120b30SAlexey Dobriyan 
27331da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
27341da177e4SLinus Torvalds {
273569ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
27361da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
273769ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
273869ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
273969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
274069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
274169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
2742fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
274369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
27441da177e4SLinus Torvalds 
27451da177e4SLinus Torvalds 	return 0;
27461da177e4SLinus Torvalds }
27471da177e4SLinus Torvalds 
27481da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
27491da177e4SLinus Torvalds {
2750de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
275169ddb805SDaniel Lezcano }
275269ddb805SDaniel Lezcano 
27539a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
27541da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
27551da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
27561da177e4SLinus Torvalds 	.read	 = seq_read,
27571da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
2758b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
27591da177e4SLinus Torvalds };
27601da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
27611da177e4SLinus Torvalds 
27621da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
27631da177e4SLinus Torvalds 
27641da177e4SLinus Torvalds static
27658d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
27661da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
27671da177e4SLinus Torvalds {
2768c486da34SLucian Adrian Grijincu 	struct net *net;
2769c486da34SLucian Adrian Grijincu 	int delay;
2770c486da34SLucian Adrian Grijincu 	if (!write)
2771c486da34SLucian Adrian Grijincu 		return -EINVAL;
2772c486da34SLucian Adrian Grijincu 
2773c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
2774c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
27758d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
27765b7c931dSDaniel Lezcano 	fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
27771da177e4SLinus Torvalds 	return 0;
27781da177e4SLinus Torvalds }
27791da177e4SLinus Torvalds 
2780760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = {
27811da177e4SLinus Torvalds 	{
27821da177e4SLinus Torvalds 		.procname	=	"flush",
27834990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
27841da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
278589c8b3a1SDave Jones 		.mode		=	0200,
27866d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
27871da177e4SLinus Torvalds 	},
27881da177e4SLinus Torvalds 	{
27891da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
27909a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
27911da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27921da177e4SLinus Torvalds 		.mode		=	0644,
27936d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
27941da177e4SLinus Torvalds 	},
27951da177e4SLinus Torvalds 	{
27961da177e4SLinus Torvalds 		.procname	=	"max_size",
27974990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
27981da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27991da177e4SLinus Torvalds 		.mode		=	0644,
28006d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
28011da177e4SLinus Torvalds 	},
28021da177e4SLinus Torvalds 	{
28031da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
28044990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
28051da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28061da177e4SLinus Torvalds 		.mode		=	0644,
28076d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28081da177e4SLinus Torvalds 	},
28091da177e4SLinus Torvalds 	{
28101da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
28114990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
28121da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28131da177e4SLinus Torvalds 		.mode		=	0644,
28146d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28151da177e4SLinus Torvalds 	},
28161da177e4SLinus Torvalds 	{
28171da177e4SLinus Torvalds 		.procname	=	"gc_interval",
28184990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
28191da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28201da177e4SLinus Torvalds 		.mode		=	0644,
28216d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28221da177e4SLinus Torvalds 	},
28231da177e4SLinus Torvalds 	{
28241da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
28254990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
28261da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28271da177e4SLinus Torvalds 		.mode		=	0644,
2828f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
28291da177e4SLinus Torvalds 	},
28301da177e4SLinus Torvalds 	{
28311da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
28324990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
28331da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28341da177e4SLinus Torvalds 		.mode		=	0644,
28356d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28361da177e4SLinus Torvalds 	},
28371da177e4SLinus Torvalds 	{
28381da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
28394990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
28401da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28411da177e4SLinus Torvalds 		.mode		=	0644,
2842f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
28431da177e4SLinus Torvalds 	},
28441da177e4SLinus Torvalds 	{
28451da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
28464990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
28471da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28481da177e4SLinus Torvalds 		.mode		=	0644,
28496d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
28501da177e4SLinus Torvalds 	},
2851f8572d8fSEric W. Biederman 	{ }
28521da177e4SLinus Torvalds };
28531da177e4SLinus Torvalds 
28542c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
2855760f2d01SDaniel Lezcano {
2856760f2d01SDaniel Lezcano 	struct ctl_table *table;
2857760f2d01SDaniel Lezcano 
2858760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
2859760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
2860760f2d01SDaniel Lezcano 			GFP_KERNEL);
28615ee09105SYOSHIFUJI Hideaki 
28625ee09105SYOSHIFUJI Hideaki 	if (table) {
28635ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
2864c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
286586393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
28665ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
28675ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
28685ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
28695ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
28705ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
28715ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
28725ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
28739c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
28745ee09105SYOSHIFUJI Hideaki 	}
28755ee09105SYOSHIFUJI Hideaki 
2876760f2d01SDaniel Lezcano 	return table;
2877760f2d01SDaniel Lezcano }
28781da177e4SLinus Torvalds #endif
28791da177e4SLinus Torvalds 
28802c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
2881cdb18761SDaniel Lezcano {
2882633d424bSPavel Emelyanov 	int ret = -ENOMEM;
28838ed67789SDaniel Lezcano 
288486393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
288586393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
2886f2fc6a54SBenjamin Thery 
2887fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2888fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
2889fc66f95cSEric Dumazet 
28908ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
28918ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
28928ed67789SDaniel Lezcano 					   GFP_KERNEL);
28938ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
2894fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
2895d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
28968ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
2897d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
289862fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
289962fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
29008ed67789SDaniel Lezcano 
29018ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
29028ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
29038ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
29048ed67789SDaniel Lezcano 					       GFP_KERNEL);
290568fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
290668fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
2907d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
29088ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
2909d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
291062fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
291162fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
29128ed67789SDaniel Lezcano 
29138ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
29148ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
29158ed67789SDaniel Lezcano 					       GFP_KERNEL);
291668fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
291768fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
2918d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
29198ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
2920d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
292162fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
292262fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
29238ed67789SDaniel Lezcano #endif
29248ed67789SDaniel Lezcano 
2925b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
2926b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
2927b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2928b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2929b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2930b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2931b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2932b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2933b339a47cSPeter Zijlstra 
29346891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
29356891a346SBenjamin Thery 
29368ed67789SDaniel Lezcano 	ret = 0;
29378ed67789SDaniel Lezcano out:
29388ed67789SDaniel Lezcano 	return ret;
2939f2fc6a54SBenjamin Thery 
294068fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
294168fffc67SPeter Zijlstra out_ip6_prohibit_entry:
294268fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
294368fffc67SPeter Zijlstra out_ip6_null_entry:
294468fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
294568fffc67SPeter Zijlstra #endif
2946fc66f95cSEric Dumazet out_ip6_dst_entries:
2947fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
2948f2fc6a54SBenjamin Thery out_ip6_dst_ops:
2949f2fc6a54SBenjamin Thery 	goto out;
2950cdb18761SDaniel Lezcano }
2951cdb18761SDaniel Lezcano 
29522c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
2953cdb18761SDaniel Lezcano {
29548ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
29558ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
29568ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
29578ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
29588ed67789SDaniel Lezcano #endif
295941bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
2960cdb18761SDaniel Lezcano }
2961cdb18761SDaniel Lezcano 
2962d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net)
2963d189634eSThomas Graf {
2964d189634eSThomas Graf #ifdef CONFIG_PROC_FS
2965d189634eSThomas Graf 	proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2966d189634eSThomas Graf 	proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2967d189634eSThomas Graf #endif
2968d189634eSThomas Graf 	return 0;
2969d189634eSThomas Graf }
2970d189634eSThomas Graf 
2971d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net)
2972d189634eSThomas Graf {
2973d189634eSThomas Graf #ifdef CONFIG_PROC_FS
2974d189634eSThomas Graf 	proc_net_remove(net, "ipv6_route");
2975d189634eSThomas Graf 	proc_net_remove(net, "rt6_stats");
2976d189634eSThomas Graf #endif
2977d189634eSThomas Graf }
2978d189634eSThomas Graf 
2979cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
2980cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
2981cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
2982cdb18761SDaniel Lezcano };
2983cdb18761SDaniel Lezcano 
2984c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net)
2985c3426b47SDavid S. Miller {
2986c3426b47SDavid S. Miller 	struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2987c3426b47SDavid S. Miller 
2988c3426b47SDavid S. Miller 	if (!bp)
2989c3426b47SDavid S. Miller 		return -ENOMEM;
2990c3426b47SDavid S. Miller 	inet_peer_base_init(bp);
2991c3426b47SDavid S. Miller 	net->ipv6.peers = bp;
2992c3426b47SDavid S. Miller 	return 0;
2993c3426b47SDavid S. Miller }
2994c3426b47SDavid S. Miller 
2995c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net)
2996c3426b47SDavid S. Miller {
2997c3426b47SDavid S. Miller 	struct inet_peer_base *bp = net->ipv6.peers;
2998c3426b47SDavid S. Miller 
2999c3426b47SDavid S. Miller 	net->ipv6.peers = NULL;
300056a6b248SDavid S. Miller 	inetpeer_invalidate_tree(bp);
3001c3426b47SDavid S. Miller 	kfree(bp);
3002c3426b47SDavid S. Miller }
3003c3426b47SDavid S. Miller 
30042b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = {
3005c3426b47SDavid S. Miller 	.init	=	ipv6_inetpeer_init,
3006c3426b47SDavid S. Miller 	.exit	=	ipv6_inetpeer_exit,
3007c3426b47SDavid S. Miller };
3008c3426b47SDavid S. Miller 
3009d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = {
3010d189634eSThomas Graf 	.init = ip6_route_net_init_late,
3011d189634eSThomas Graf 	.exit = ip6_route_net_exit_late,
3012d189634eSThomas Graf };
3013d189634eSThomas Graf 
30148ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
30158ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
30168ed67789SDaniel Lezcano 	.priority = 0,
30178ed67789SDaniel Lezcano };
30188ed67789SDaniel Lezcano 
3019433d49c3SDaniel Lezcano int __init ip6_route_init(void)
30201da177e4SLinus Torvalds {
3021433d49c3SDaniel Lezcano 	int ret;
3022433d49c3SDaniel Lezcano 
30239a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
30249a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
30259a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
30269a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
30279a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
3028c19a28e1SFernando Carrijo 		goto out;
302914e50e57SDavid S. Miller 
3030fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
30318ed67789SDaniel Lezcano 	if (ret)
3032bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
3033bdb3289fSDaniel Lezcano 
3034c3426b47SDavid S. Miller 	ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3035c3426b47SDavid S. Miller 	if (ret)
3036e8803b6cSDavid S. Miller 		goto out_dst_entries;
30372a0c451aSThomas Graf 
30387e52b33bSDavid S. Miller 	ret = register_pernet_subsys(&ip6_route_net_ops);
30397e52b33bSDavid S. Miller 	if (ret)
30407e52b33bSDavid S. Miller 		goto out_register_inetpeer;
3041c3426b47SDavid S. Miller 
30425dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
30435dc121e9SArnaud Ebalard 
30448ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
30458ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
30468ed67789SDaniel Lezcano 	 * manually for init_net */
3047d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
30488ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3049bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
3050d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
30518ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3052d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
30538ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3054bdb3289fSDaniel Lezcano   #endif
3055e8803b6cSDavid S. Miller 	ret = fib6_init();
3056433d49c3SDaniel Lezcano 	if (ret)
30578ed67789SDaniel Lezcano 		goto out_register_subsys;
3058433d49c3SDaniel Lezcano 
3059433d49c3SDaniel Lezcano 	ret = xfrm6_init();
3060433d49c3SDaniel Lezcano 	if (ret)
3061e8803b6cSDavid S. Miller 		goto out_fib6_init;
3062c35b7e72SDaniel Lezcano 
3063433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3064433d49c3SDaniel Lezcano 	if (ret)
3065433d49c3SDaniel Lezcano 		goto xfrm6_init;
30667e5449c2SDaniel Lezcano 
3067d189634eSThomas Graf 	ret = register_pernet_subsys(&ip6_route_net_late_ops);
3068d189634eSThomas Graf 	if (ret)
3069d189634eSThomas Graf 		goto fib6_rules_init;
3070d189634eSThomas Graf 
3071433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3072c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3073c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3074c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3075d189634eSThomas Graf 		goto out_register_late_subsys;
3076433d49c3SDaniel Lezcano 
30778ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3078cdb18761SDaniel Lezcano 	if (ret)
3079d189634eSThomas Graf 		goto out_register_late_subsys;
30808ed67789SDaniel Lezcano 
3081433d49c3SDaniel Lezcano out:
3082433d49c3SDaniel Lezcano 	return ret;
3083433d49c3SDaniel Lezcano 
3084d189634eSThomas Graf out_register_late_subsys:
3085d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3086433d49c3SDaniel Lezcano fib6_rules_init:
3087433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3088433d49c3SDaniel Lezcano xfrm6_init:
3089433d49c3SDaniel Lezcano 	xfrm6_fini();
30902a0c451aSThomas Graf out_fib6_init:
30912a0c451aSThomas Graf 	fib6_gc_cleanup();
30928ed67789SDaniel Lezcano out_register_subsys:
30938ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
30947e52b33bSDavid S. Miller out_register_inetpeer:
30957e52b33bSDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
3096fc66f95cSEric Dumazet out_dst_entries:
3097fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3098433d49c3SDaniel Lezcano out_kmem_cache:
3099f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3100433d49c3SDaniel Lezcano 	goto out;
31011da177e4SLinus Torvalds }
31021da177e4SLinus Torvalds 
31031da177e4SLinus Torvalds void ip6_route_cleanup(void)
31041da177e4SLinus Torvalds {
31058ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3106d189634eSThomas Graf 	unregister_pernet_subsys(&ip6_route_net_late_ops);
3107101367c2SThomas Graf 	fib6_rules_cleanup();
31081da177e4SLinus Torvalds 	xfrm6_fini();
31091da177e4SLinus Torvalds 	fib6_gc_cleanup();
3110c3426b47SDavid S. Miller 	unregister_pernet_subsys(&ipv6_inetpeer_ops);
31118ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
311241bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3113f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
31141da177e4SLinus Torvalds }
3115