xref: /openbmc/linux/net/ipv6/route.c (revision 39232973b779ab0c02cb6dcd8f819b7cb0fcd09a)
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 
274fc268d2SRandy Dunlap #include <linux/capability.h>
281da177e4SLinus Torvalds #include <linux/errno.h>
29bc3b2d7fSPaul Gortmaker #include <linux/export.h>
301da177e4SLinus Torvalds #include <linux/types.h>
311da177e4SLinus Torvalds #include <linux/times.h>
321da177e4SLinus Torvalds #include <linux/socket.h>
331da177e4SLinus Torvalds #include <linux/sockios.h>
341da177e4SLinus Torvalds #include <linux/net.h>
351da177e4SLinus Torvalds #include <linux/route.h>
361da177e4SLinus Torvalds #include <linux/netdevice.h>
371da177e4SLinus Torvalds #include <linux/in6.h>
387bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
391da177e4SLinus Torvalds #include <linux/init.h>
401da177e4SLinus Torvalds #include <linux/if_arp.h>
411da177e4SLinus Torvalds #include <linux/proc_fs.h>
421da177e4SLinus Torvalds #include <linux/seq_file.h>
435b7c931dSDaniel Lezcano #include <linux/nsproxy.h>
445a0e3ad6STejun Heo #include <linux/slab.h>
45457c4cbcSEric W. Biederman #include <net/net_namespace.h>
461da177e4SLinus Torvalds #include <net/snmp.h>
471da177e4SLinus Torvalds #include <net/ipv6.h>
481da177e4SLinus Torvalds #include <net/ip6_fib.h>
491da177e4SLinus Torvalds #include <net/ip6_route.h>
501da177e4SLinus Torvalds #include <net/ndisc.h>
511da177e4SLinus Torvalds #include <net/addrconf.h>
521da177e4SLinus Torvalds #include <net/tcp.h>
531da177e4SLinus Torvalds #include <linux/rtnetlink.h>
541da177e4SLinus Torvalds #include <net/dst.h>
551da177e4SLinus Torvalds #include <net/xfrm.h>
568d71740cSTom Tucker #include <net/netevent.h>
5721713ebcSThomas Graf #include <net/netlink.h>
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds #include <asm/uaccess.h>
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
621da177e4SLinus Torvalds #include <linux/sysctl.h>
631da177e4SLinus Torvalds #endif
641da177e4SLinus Torvalds 
6521efcfa0SEric Dumazet static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort,
6621efcfa0SEric Dumazet 				    const struct in6_addr *dest);
671da177e4SLinus Torvalds static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
680dbaee3bSDavid S. Miller static unsigned int	 ip6_default_advmss(const struct dst_entry *dst);
69ebb762f2SSteffen Klassert static unsigned int	 ip6_mtu(const struct dst_entry *dst);
701da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *);
711da177e4SLinus Torvalds static void		ip6_dst_destroy(struct dst_entry *);
721da177e4SLinus Torvalds static void		ip6_dst_ifdown(struct dst_entry *,
731da177e4SLinus Torvalds 				       struct net_device *dev, int how);
74569d3645SDaniel Lezcano static int		 ip6_dst_gc(struct dst_ops *ops);
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static int		ip6_pkt_discard(struct sk_buff *skb);
771da177e4SLinus Torvalds static int		ip6_pkt_discard_out(struct sk_buff *skb);
781da177e4SLinus Torvalds static void		ip6_link_failure(struct sk_buff *skb);
791da177e4SLinus Torvalds static void		ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
801da177e4SLinus Torvalds 
8170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
82efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
83b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
84b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
8570ceb4f5SYOSHIFUJI Hideaki 					   unsigned pref);
86efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
87b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
88b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex);
8970ceb4f5SYOSHIFUJI Hideaki #endif
9070ceb4f5SYOSHIFUJI Hideaki 
9106582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
9206582540SDavid S. Miller {
9306582540SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
9406582540SDavid S. Miller 	struct inet_peer *peer;
9506582540SDavid S. Miller 	u32 *p = NULL;
9606582540SDavid S. Miller 
978e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST))
988e2ec639SYan, Zheng 		return NULL;
998e2ec639SYan, Zheng 
10006582540SDavid S. Miller 	if (!rt->rt6i_peer)
10106582540SDavid S. Miller 		rt6_bind_peer(rt, 1);
10206582540SDavid S. Miller 
10306582540SDavid S. Miller 	peer = rt->rt6i_peer;
10406582540SDavid S. Miller 	if (peer) {
10506582540SDavid S. Miller 		u32 *old_p = __DST_METRICS_PTR(old);
10606582540SDavid S. Miller 		unsigned long prev, new;
10706582540SDavid S. Miller 
10806582540SDavid S. Miller 		p = peer->metrics;
10906582540SDavid S. Miller 		if (inet_metrics_new(peer))
11006582540SDavid S. Miller 			memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
11106582540SDavid S. Miller 
11206582540SDavid S. Miller 		new = (unsigned long) p;
11306582540SDavid S. Miller 		prev = cmpxchg(&dst->_metrics, old, new);
11406582540SDavid S. Miller 
11506582540SDavid S. Miller 		if (prev != old) {
11606582540SDavid S. Miller 			p = __DST_METRICS_PTR(prev);
11706582540SDavid S. Miller 			if (prev & DST_METRICS_READ_ONLY)
11806582540SDavid S. Miller 				p = NULL;
11906582540SDavid S. Miller 		}
12006582540SDavid S. Miller 	}
12106582540SDavid S. Miller 	return p;
12206582540SDavid S. Miller }
12306582540SDavid S. Miller 
124*39232973SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, const void *daddr)
125*39232973SDavid S. Miller {
126*39232973SDavid S. Miller 	struct in6_addr *p = &rt->rt6i_gateway;
127*39232973SDavid S. Miller 
128*39232973SDavid S. Miller 	if (p->s6_addr32[0] | p->s6_addr32[1] |
129*39232973SDavid S. Miller 	    p->s6_addr32[2] | p->s6_addr32[3])
130*39232973SDavid S. Miller 		return (const void *) p;
131*39232973SDavid S. Miller 	return daddr;
132*39232973SDavid S. Miller }
133*39232973SDavid S. Miller 
134d3aaeb38SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, const void *daddr)
135d3aaeb38SDavid S. Miller {
136*39232973SDavid S. Miller 	struct rt6_info *rt = (struct rt6_info *) dst;
137*39232973SDavid S. Miller 	struct neighbour *n;
138*39232973SDavid S. Miller 
139*39232973SDavid S. Miller 	daddr = choose_neigh_daddr(rt, daddr);
140*39232973SDavid S. Miller 	n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
141f83c7790SDavid S. Miller 	if (n)
142f83c7790SDavid S. Miller 		return n;
143f83c7790SDavid S. Miller 	return neigh_create(&nd_tbl, daddr, dst->dev);
144f83c7790SDavid S. Miller }
145f83c7790SDavid S. Miller 
1468ade06c6SDavid S. Miller static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
147f83c7790SDavid S. Miller {
1488ade06c6SDavid S. Miller 	struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
1498ade06c6SDavid S. Miller 	if (!n) {
1508ade06c6SDavid S. Miller 		n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
151f83c7790SDavid S. Miller 		if (IS_ERR(n))
152f83c7790SDavid S. Miller 			return PTR_ERR(n);
1538ade06c6SDavid S. Miller 	}
154f83c7790SDavid S. Miller 	dst_set_neighbour(&rt->dst, n);
155f83c7790SDavid S. Miller 
156f83c7790SDavid S. Miller 	return 0;
157d3aaeb38SDavid S. Miller }
158d3aaeb38SDavid S. Miller 
1599a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = {
1601da177e4SLinus Torvalds 	.family			=	AF_INET6,
16109640e63SHarvey Harrison 	.protocol		=	cpu_to_be16(ETH_P_IPV6),
1621da177e4SLinus Torvalds 	.gc			=	ip6_dst_gc,
1631da177e4SLinus Torvalds 	.gc_thresh		=	1024,
1641da177e4SLinus Torvalds 	.check			=	ip6_dst_check,
1650dbaee3bSDavid S. Miller 	.default_advmss		=	ip6_default_advmss,
166ebb762f2SSteffen Klassert 	.mtu			=	ip6_mtu,
16706582540SDavid S. Miller 	.cow_metrics		=	ipv6_cow_metrics,
1681da177e4SLinus Torvalds 	.destroy		=	ip6_dst_destroy,
1691da177e4SLinus Torvalds 	.ifdown			=	ip6_dst_ifdown,
1701da177e4SLinus Torvalds 	.negative_advice	=	ip6_negative_advice,
1711da177e4SLinus Torvalds 	.link_failure		=	ip6_link_failure,
1721da177e4SLinus Torvalds 	.update_pmtu		=	ip6_rt_update_pmtu,
1731ac06e03SHerbert Xu 	.local_out		=	__ip6_local_out,
174d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
1751da177e4SLinus Torvalds };
1761da177e4SLinus Torvalds 
177ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
178ec831ea7SRoland Dreier {
179618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
180618f9bc7SSteffen Klassert 
181618f9bc7SSteffen Klassert 	return mtu ? : dst->dev->mtu;
182ec831ea7SRoland Dreier }
183ec831ea7SRoland Dreier 
18414e50e57SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
18514e50e57SDavid S. Miller {
18614e50e57SDavid S. Miller }
18714e50e57SDavid S. Miller 
1880972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
1890972ddb2SHeld Bernhard 					 unsigned long old)
1900972ddb2SHeld Bernhard {
1910972ddb2SHeld Bernhard 	return NULL;
1920972ddb2SHeld Bernhard }
1930972ddb2SHeld Bernhard 
19414e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = {
19514e50e57SDavid S. Miller 	.family			=	AF_INET6,
19609640e63SHarvey Harrison 	.protocol		=	cpu_to_be16(ETH_P_IPV6),
19714e50e57SDavid S. Miller 	.destroy		=	ip6_dst_destroy,
19814e50e57SDavid S. Miller 	.check			=	ip6_dst_check,
199ebb762f2SSteffen Klassert 	.mtu			=	ip6_blackhole_mtu,
200214f45c9SEric Dumazet 	.default_advmss		=	ip6_default_advmss,
20114e50e57SDavid S. Miller 	.update_pmtu		=	ip6_rt_blackhole_update_pmtu,
2020972ddb2SHeld Bernhard 	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,
203d3aaeb38SDavid S. Miller 	.neigh_lookup		=	ip6_neigh_lookup,
20414e50e57SDavid S. Miller };
20514e50e57SDavid S. Miller 
20662fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = {
20762fa8a84SDavid S. Miller 	[RTAX_HOPLIMIT - 1] = 255,
20862fa8a84SDavid S. Miller };
20962fa8a84SDavid S. Miller 
210bdb3289fSDaniel Lezcano static struct rt6_info ip6_null_entry_template = {
2111da177e4SLinus Torvalds 	.dst = {
2121da177e4SLinus Torvalds 		.__refcnt	= ATOMIC_INIT(1),
2131da177e4SLinus Torvalds 		.__use		= 1,
2141da177e4SLinus Torvalds 		.obsolete	= -1,
2151da177e4SLinus Torvalds 		.error		= -ENETUNREACH,
2161da177e4SLinus Torvalds 		.input		= ip6_pkt_discard,
2171da177e4SLinus Torvalds 		.output		= ip6_pkt_discard_out,
2181da177e4SLinus Torvalds 	},
2191da177e4SLinus Torvalds 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2204f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
2211da177e4SLinus Torvalds 	.rt6i_metric	= ~(u32) 0,
2221da177e4SLinus Torvalds 	.rt6i_ref	= ATOMIC_INIT(1),
2231da177e4SLinus Torvalds };
2241da177e4SLinus Torvalds 
225101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES
226101367c2SThomas Graf 
2276723ab54SDavid S. Miller static int ip6_pkt_prohibit(struct sk_buff *skb);
2286723ab54SDavid S. Miller static int ip6_pkt_prohibit_out(struct sk_buff *skb);
2296723ab54SDavid S. Miller 
230280a34c8SAdrian Bunk static struct rt6_info ip6_prohibit_entry_template = {
231101367c2SThomas Graf 	.dst = {
232101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
233101367c2SThomas Graf 		.__use		= 1,
234101367c2SThomas Graf 		.obsolete	= -1,
235101367c2SThomas Graf 		.error		= -EACCES,
2369ce8ade0SThomas Graf 		.input		= ip6_pkt_prohibit,
2379ce8ade0SThomas Graf 		.output		= ip6_pkt_prohibit_out,
238101367c2SThomas Graf 	},
239101367c2SThomas Graf 	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
2404f724279SJean-Mickael Guerin 	.rt6i_protocol  = RTPROT_KERNEL,
241101367c2SThomas Graf 	.rt6i_metric	= ~(u32) 0,
242101367c2SThomas Graf 	.rt6i_ref	= ATOMIC_INIT(1),
243101367c2SThomas Graf };
244101367c2SThomas Graf 
245bdb3289fSDaniel Lezcano static struct rt6_info ip6_blk_hole_entry_template = {
246101367c2SThomas Graf 	.dst = {
247101367c2SThomas Graf 		.__refcnt	= ATOMIC_INIT(1),
248101367c2SThomas Graf 		.__use		= 1,
249101367c2SThomas Graf 		.obsolete	= -1,
250101367c2SThomas Graf 		.error		= -EINVAL,
251352e512cSHerbert Xu 		.input		= dst_discard,
252352e512cSHerbert Xu 		.output		= dst_discard,
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 
260101367c2SThomas Graf #endif
261101367c2SThomas Graf 
2621da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */
2635c1e6aa3SDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops,
264957c665fSDavid S. Miller 					     struct net_device *dev,
265957c665fSDavid S. Miller 					     int flags)
2661da177e4SLinus Torvalds {
267957c665fSDavid S. Miller 	struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, flags);
268cf911662SDavid S. Miller 
26938308473SDavid S. Miller 	if (rt)
270fbe58186SMadalin Bucur 		memset(&rt->rt6i_table, 0,
271fbe58186SMadalin Bucur 		       sizeof(*rt) - sizeof(struct dst_entry));
272cf911662SDavid S. Miller 
273cf911662SDavid S. Miller 	return rt;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst)
2771da177e4SLinus Torvalds {
2781da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
2791da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
280b3419363SDavid S. Miller 	struct inet_peer *peer = rt->rt6i_peer;
2811da177e4SLinus Torvalds 
2828e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST))
2838e2ec639SYan, Zheng 		dst_destroy_metrics_generic(dst);
2848e2ec639SYan, Zheng 
28538308473SDavid S. Miller 	if (idev) {
2861da177e4SLinus Torvalds 		rt->rt6i_idev = NULL;
2871da177e4SLinus Torvalds 		in6_dev_put(idev);
2881da177e4SLinus Torvalds 	}
289b3419363SDavid S. Miller 	if (peer) {
290b3419363SDavid S. Miller 		rt->rt6i_peer = NULL;
291b3419363SDavid S. Miller 		inet_putpeer(peer);
292b3419363SDavid S. Miller 	}
293b3419363SDavid S. Miller }
294b3419363SDavid S. Miller 
2956431cbc2SDavid S. Miller static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
2966431cbc2SDavid S. Miller 
2976431cbc2SDavid S. Miller static u32 rt6_peer_genid(void)
2986431cbc2SDavid S. Miller {
2996431cbc2SDavid S. Miller 	return atomic_read(&__rt6_peer_genid);
3006431cbc2SDavid S. Miller }
3016431cbc2SDavid S. Miller 
302b3419363SDavid S. Miller void rt6_bind_peer(struct rt6_info *rt, int create)
303b3419363SDavid S. Miller {
304b3419363SDavid S. Miller 	struct inet_peer *peer;
305b3419363SDavid S. Miller 
306b3419363SDavid S. Miller 	peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create);
307b3419363SDavid S. Miller 	if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL)
308b3419363SDavid S. Miller 		inet_putpeer(peer);
3096431cbc2SDavid S. Miller 	else
3106431cbc2SDavid S. Miller 		rt->rt6i_peer_genid = rt6_peer_genid();
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
3141da177e4SLinus Torvalds 			   int how)
3151da177e4SLinus Torvalds {
3161da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *)dst;
3171da177e4SLinus Torvalds 	struct inet6_dev *idev = rt->rt6i_idev;
3185a3e55d6SDenis V. Lunev 	struct net_device *loopback_dev =
319c346dca1SYOSHIFUJI Hideaki 		dev_net(dev)->loopback_dev;
3201da177e4SLinus Torvalds 
32138308473SDavid S. Miller 	if (dev != loopback_dev && idev && idev->dev == dev) {
3225a3e55d6SDenis V. Lunev 		struct inet6_dev *loopback_idev =
3235a3e55d6SDenis V. Lunev 			in6_dev_get(loopback_dev);
32438308473SDavid S. Miller 		if (loopback_idev) {
3251da177e4SLinus Torvalds 			rt->rt6i_idev = loopback_idev;
3261da177e4SLinus Torvalds 			in6_dev_put(idev);
3271da177e4SLinus Torvalds 		}
3281da177e4SLinus Torvalds 	}
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds static __inline__ int rt6_check_expired(const struct rt6_info *rt)
3321da177e4SLinus Torvalds {
333a02cec21SEric Dumazet 	return (rt->rt6i_flags & RTF_EXPIRES) &&
334d1918542SDavid S. Miller 		time_after(jiffies, rt->dst.expires);
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
337b71d1d42SEric Dumazet static inline int rt6_need_strict(const struct in6_addr *daddr)
338c71099acSThomas Graf {
339a02cec21SEric Dumazet 	return ipv6_addr_type(daddr) &
340a02cec21SEric Dumazet 		(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
341c71099acSThomas Graf }
342c71099acSThomas Graf 
3431da177e4SLinus Torvalds /*
344c71099acSThomas Graf  *	Route lookup. Any table->tb6_lock is implied.
3451da177e4SLinus Torvalds  */
3461da177e4SLinus Torvalds 
3478ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net,
3488ed67789SDaniel Lezcano 						    struct rt6_info *rt,
349b71d1d42SEric Dumazet 						    const struct in6_addr *saddr,
3501da177e4SLinus Torvalds 						    int oif,
351d420895eSYOSHIFUJI Hideaki 						    int flags)
3521da177e4SLinus Torvalds {
3531da177e4SLinus Torvalds 	struct rt6_info *local = NULL;
3541da177e4SLinus Torvalds 	struct rt6_info *sprt;
3551da177e4SLinus Torvalds 
356dd3abc4eSYOSHIFUJI Hideaki 	if (!oif && ipv6_addr_any(saddr))
357dd3abc4eSYOSHIFUJI Hideaki 		goto out;
358dd3abc4eSYOSHIFUJI Hideaki 
359d8d1f30bSChangli Gao 	for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
360d1918542SDavid S. Miller 		struct net_device *dev = sprt->dst.dev;
361dd3abc4eSYOSHIFUJI Hideaki 
362dd3abc4eSYOSHIFUJI Hideaki 		if (oif) {
3631da177e4SLinus Torvalds 			if (dev->ifindex == oif)
3641da177e4SLinus Torvalds 				return sprt;
3651da177e4SLinus Torvalds 			if (dev->flags & IFF_LOOPBACK) {
36638308473SDavid S. Miller 				if (!sprt->rt6i_idev ||
3671da177e4SLinus Torvalds 				    sprt->rt6i_idev->dev->ifindex != oif) {
368d420895eSYOSHIFUJI Hideaki 					if (flags & RT6_LOOKUP_F_IFACE && oif)
3691da177e4SLinus Torvalds 						continue;
3701da177e4SLinus Torvalds 					if (local && (!oif ||
3711da177e4SLinus Torvalds 						      local->rt6i_idev->dev->ifindex == oif))
3721da177e4SLinus Torvalds 						continue;
3731da177e4SLinus Torvalds 				}
3741da177e4SLinus Torvalds 				local = sprt;
3751da177e4SLinus Torvalds 			}
376dd3abc4eSYOSHIFUJI Hideaki 		} else {
377dd3abc4eSYOSHIFUJI Hideaki 			if (ipv6_chk_addr(net, saddr, dev,
378dd3abc4eSYOSHIFUJI Hideaki 					  flags & RT6_LOOKUP_F_IFACE))
379dd3abc4eSYOSHIFUJI Hideaki 				return sprt;
380dd3abc4eSYOSHIFUJI Hideaki 		}
3811da177e4SLinus Torvalds 	}
3821da177e4SLinus Torvalds 
383dd3abc4eSYOSHIFUJI Hideaki 	if (oif) {
3841da177e4SLinus Torvalds 		if (local)
3851da177e4SLinus Torvalds 			return local;
3861da177e4SLinus Torvalds 
387d420895eSYOSHIFUJI Hideaki 		if (flags & RT6_LOOKUP_F_IFACE)
3888ed67789SDaniel Lezcano 			return net->ipv6.ip6_null_entry;
3891da177e4SLinus Torvalds 	}
390dd3abc4eSYOSHIFUJI Hideaki out:
3911da177e4SLinus Torvalds 	return rt;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
39427097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
39527097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt)
39627097255SYOSHIFUJI Hideaki {
397f2c31e32SEric Dumazet 	struct neighbour *neigh;
39827097255SYOSHIFUJI Hideaki 	/*
39927097255SYOSHIFUJI Hideaki 	 * Okay, this does not seem to be appropriate
40027097255SYOSHIFUJI Hideaki 	 * for now, however, we need to check if it
40127097255SYOSHIFUJI Hideaki 	 * is really so; aka Router Reachability Probing.
40227097255SYOSHIFUJI Hideaki 	 *
40327097255SYOSHIFUJI Hideaki 	 * Router Reachability Probe MUST be rate-limited
40427097255SYOSHIFUJI Hideaki 	 * to no more than one per minute.
40527097255SYOSHIFUJI Hideaki 	 */
406f2c31e32SEric Dumazet 	rcu_read_lock();
40727217455SDavid Miller 	neigh = rt ? dst_get_neighbour_noref(&rt->dst) : NULL;
40827097255SYOSHIFUJI Hideaki 	if (!neigh || (neigh->nud_state & NUD_VALID))
409f2c31e32SEric Dumazet 		goto out;
41027097255SYOSHIFUJI Hideaki 	read_lock_bh(&neigh->lock);
41127097255SYOSHIFUJI Hideaki 	if (!(neigh->nud_state & NUD_VALID) &&
41252e16356SYOSHIFUJI Hideaki 	    time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
41327097255SYOSHIFUJI Hideaki 		struct in6_addr mcaddr;
41427097255SYOSHIFUJI Hideaki 		struct in6_addr *target;
41527097255SYOSHIFUJI Hideaki 
41627097255SYOSHIFUJI Hideaki 		neigh->updated = jiffies;
41727097255SYOSHIFUJI Hideaki 		read_unlock_bh(&neigh->lock);
41827097255SYOSHIFUJI Hideaki 
41927097255SYOSHIFUJI Hideaki 		target = (struct in6_addr *)&neigh->primary_key;
42027097255SYOSHIFUJI Hideaki 		addrconf_addr_solict_mult(target, &mcaddr);
421d1918542SDavid S. Miller 		ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
422f2c31e32SEric Dumazet 	} else {
42327097255SYOSHIFUJI Hideaki 		read_unlock_bh(&neigh->lock);
42427097255SYOSHIFUJI Hideaki 	}
425f2c31e32SEric Dumazet out:
426f2c31e32SEric Dumazet 	rcu_read_unlock();
427f2c31e32SEric Dumazet }
42827097255SYOSHIFUJI Hideaki #else
42927097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt)
43027097255SYOSHIFUJI Hideaki {
43127097255SYOSHIFUJI Hideaki }
43227097255SYOSHIFUJI Hideaki #endif
43327097255SYOSHIFUJI Hideaki 
4341da177e4SLinus Torvalds /*
435554cfb7eSYOSHIFUJI Hideaki  * Default Router Selection (RFC 2461 6.3.6)
4361da177e4SLinus Torvalds  */
437b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif)
4381da177e4SLinus Torvalds {
439d1918542SDavid S. Miller 	struct net_device *dev = rt->dst.dev;
440161980f4SDavid S. Miller 	if (!oif || dev->ifindex == oif)
441554cfb7eSYOSHIFUJI Hideaki 		return 2;
442161980f4SDavid S. Miller 	if ((dev->flags & IFF_LOOPBACK) &&
443161980f4SDavid S. Miller 	    rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
444161980f4SDavid S. Miller 		return 1;
445554cfb7eSYOSHIFUJI Hideaki 	return 0;
4461da177e4SLinus Torvalds }
4471da177e4SLinus Torvalds 
448b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt)
4491da177e4SLinus Torvalds {
450f2c31e32SEric Dumazet 	struct neighbour *neigh;
451398bcbebSYOSHIFUJI Hideaki 	int m;
452f2c31e32SEric Dumazet 
453f2c31e32SEric Dumazet 	rcu_read_lock();
45427217455SDavid Miller 	neigh = dst_get_neighbour_noref(&rt->dst);
4554d0c5911SYOSHIFUJI Hideaki 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
4564d0c5911SYOSHIFUJI Hideaki 	    !(rt->rt6i_flags & RTF_GATEWAY))
4574d0c5911SYOSHIFUJI Hideaki 		m = 1;
4584d0c5911SYOSHIFUJI Hideaki 	else if (neigh) {
4591da177e4SLinus Torvalds 		read_lock_bh(&neigh->lock);
460554cfb7eSYOSHIFUJI Hideaki 		if (neigh->nud_state & NUD_VALID)
4614d0c5911SYOSHIFUJI Hideaki 			m = 2;
462398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
463398bcbebSYOSHIFUJI Hideaki 		else if (neigh->nud_state & NUD_FAILED)
464398bcbebSYOSHIFUJI Hideaki 			m = 0;
465398bcbebSYOSHIFUJI Hideaki #endif
466398bcbebSYOSHIFUJI Hideaki 		else
467ea73ee23SYOSHIFUJI Hideaki 			m = 1;
4681da177e4SLinus Torvalds 		read_unlock_bh(&neigh->lock);
469398bcbebSYOSHIFUJI Hideaki 	} else
470398bcbebSYOSHIFUJI Hideaki 		m = 0;
471f2c31e32SEric Dumazet 	rcu_read_unlock();
472554cfb7eSYOSHIFUJI Hideaki 	return m;
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds 
475554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif,
476554cfb7eSYOSHIFUJI Hideaki 			   int strict)
477554cfb7eSYOSHIFUJI Hideaki {
4784d0c5911SYOSHIFUJI Hideaki 	int m, n;
4794d0c5911SYOSHIFUJI Hideaki 
4804d0c5911SYOSHIFUJI Hideaki 	m = rt6_check_dev(rt, oif);
48177d16f45SYOSHIFUJI Hideaki 	if (!m && (strict & RT6_LOOKUP_F_IFACE))
482554cfb7eSYOSHIFUJI Hideaki 		return -1;
483ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
484ebacaaa0SYOSHIFUJI Hideaki 	m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
485ebacaaa0SYOSHIFUJI Hideaki #endif
4864d0c5911SYOSHIFUJI Hideaki 	n = rt6_check_neigh(rt);
487557e92efSYOSHIFUJI Hideaki 	if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
488554cfb7eSYOSHIFUJI Hideaki 		return -1;
489554cfb7eSYOSHIFUJI Hideaki 	return m;
490554cfb7eSYOSHIFUJI Hideaki }
491554cfb7eSYOSHIFUJI Hideaki 
492f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
493f11e6659SDavid S. Miller 				   int *mpri, struct rt6_info *match)
494554cfb7eSYOSHIFUJI Hideaki {
495554cfb7eSYOSHIFUJI Hideaki 	int m;
496554cfb7eSYOSHIFUJI Hideaki 
497554cfb7eSYOSHIFUJI Hideaki 	if (rt6_check_expired(rt))
498f11e6659SDavid S. Miller 		goto out;
499554cfb7eSYOSHIFUJI Hideaki 
500554cfb7eSYOSHIFUJI Hideaki 	m = rt6_score_route(rt, oif, strict);
501554cfb7eSYOSHIFUJI Hideaki 	if (m < 0)
502f11e6659SDavid S. Miller 		goto out;
503554cfb7eSYOSHIFUJI Hideaki 
504f11e6659SDavid S. Miller 	if (m > *mpri) {
505ea659e07SYOSHIFUJI Hideaki 		if (strict & RT6_LOOKUP_F_REACHABLE)
50627097255SYOSHIFUJI Hideaki 			rt6_probe(match);
507f11e6659SDavid S. Miller 		*mpri = m;
508554cfb7eSYOSHIFUJI Hideaki 		match = rt;
509ea659e07SYOSHIFUJI Hideaki 	} else if (strict & RT6_LOOKUP_F_REACHABLE) {
51027097255SYOSHIFUJI Hideaki 		rt6_probe(rt);
5111da177e4SLinus Torvalds 	}
512f11e6659SDavid S. Miller 
513f11e6659SDavid S. Miller out:
514f11e6659SDavid S. Miller 	return match;
5151da177e4SLinus Torvalds }
5161da177e4SLinus Torvalds 
517f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
518f11e6659SDavid S. Miller 				     struct rt6_info *rr_head,
519f11e6659SDavid S. Miller 				     u32 metric, int oif, int strict)
520f11e6659SDavid S. Miller {
521f11e6659SDavid S. Miller 	struct rt6_info *rt, *match;
522f11e6659SDavid S. Miller 	int mpri = -1;
523f11e6659SDavid S. Miller 
524f11e6659SDavid S. Miller 	match = NULL;
525f11e6659SDavid S. Miller 	for (rt = rr_head; rt && rt->rt6i_metric == metric;
526d8d1f30bSChangli Gao 	     rt = rt->dst.rt6_next)
527f11e6659SDavid S. Miller 		match = find_match(rt, oif, strict, &mpri, match);
528f11e6659SDavid S. Miller 	for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
529d8d1f30bSChangli Gao 	     rt = rt->dst.rt6_next)
530f11e6659SDavid S. Miller 		match = find_match(rt, oif, strict, &mpri, match);
531f11e6659SDavid S. Miller 
532f11e6659SDavid S. Miller 	return match;
533f11e6659SDavid S. Miller }
534f11e6659SDavid S. Miller 
535f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
536f11e6659SDavid S. Miller {
537f11e6659SDavid S. Miller 	struct rt6_info *match, *rt0;
5388ed67789SDaniel Lezcano 	struct net *net;
539f11e6659SDavid S. Miller 
540f11e6659SDavid S. Miller 	rt0 = fn->rr_ptr;
541f11e6659SDavid S. Miller 	if (!rt0)
542f11e6659SDavid S. Miller 		fn->rr_ptr = rt0 = fn->leaf;
543f11e6659SDavid S. Miller 
544f11e6659SDavid S. Miller 	match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
545f11e6659SDavid S. Miller 
546554cfb7eSYOSHIFUJI Hideaki 	if (!match &&
547f11e6659SDavid S. Miller 	    (strict & RT6_LOOKUP_F_REACHABLE)) {
548d8d1f30bSChangli Gao 		struct rt6_info *next = rt0->dst.rt6_next;
549f11e6659SDavid S. Miller 
550554cfb7eSYOSHIFUJI Hideaki 		/* no entries matched; do round-robin */
551f11e6659SDavid S. Miller 		if (!next || next->rt6i_metric != rt0->rt6i_metric)
552f11e6659SDavid S. Miller 			next = fn->leaf;
553f11e6659SDavid S. Miller 
554f11e6659SDavid S. Miller 		if (next != rt0)
555f11e6659SDavid S. Miller 			fn->rr_ptr = next;
556554cfb7eSYOSHIFUJI Hideaki 	}
557554cfb7eSYOSHIFUJI Hideaki 
558d1918542SDavid S. Miller 	net = dev_net(rt0->dst.dev);
559a02cec21SEric Dumazet 	return match ? match : net->ipv6.ip6_null_entry;
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds 
56270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
56370ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
564b71d1d42SEric Dumazet 		  const struct in6_addr *gwaddr)
56570ceb4f5SYOSHIFUJI Hideaki {
566c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
56770ceb4f5SYOSHIFUJI Hideaki 	struct route_info *rinfo = (struct route_info *) opt;
56870ceb4f5SYOSHIFUJI Hideaki 	struct in6_addr prefix_buf, *prefix;
56970ceb4f5SYOSHIFUJI Hideaki 	unsigned int pref;
5704bed72e4SYOSHIFUJI Hideaki 	unsigned long lifetime;
57170ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt;
57270ceb4f5SYOSHIFUJI Hideaki 
57370ceb4f5SYOSHIFUJI Hideaki 	if (len < sizeof(struct route_info)) {
57470ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
57570ceb4f5SYOSHIFUJI Hideaki 	}
57670ceb4f5SYOSHIFUJI Hideaki 
57770ceb4f5SYOSHIFUJI Hideaki 	/* Sanity check for prefix_len and length */
57870ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length > 3) {
57970ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
58070ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 128) {
58170ceb4f5SYOSHIFUJI Hideaki 		return -EINVAL;
58270ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 64) {
58370ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 2) {
58470ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
58570ceb4f5SYOSHIFUJI Hideaki 		}
58670ceb4f5SYOSHIFUJI Hideaki 	} else if (rinfo->prefix_len > 0) {
58770ceb4f5SYOSHIFUJI Hideaki 		if (rinfo->length < 1) {
58870ceb4f5SYOSHIFUJI Hideaki 			return -EINVAL;
58970ceb4f5SYOSHIFUJI Hideaki 		}
59070ceb4f5SYOSHIFUJI Hideaki 	}
59170ceb4f5SYOSHIFUJI Hideaki 
59270ceb4f5SYOSHIFUJI Hideaki 	pref = rinfo->route_pref;
59370ceb4f5SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID)
5943933fc95SJens Rosenboom 		return -EINVAL;
59570ceb4f5SYOSHIFUJI Hideaki 
5964bed72e4SYOSHIFUJI Hideaki 	lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
59770ceb4f5SYOSHIFUJI Hideaki 
59870ceb4f5SYOSHIFUJI Hideaki 	if (rinfo->length == 3)
59970ceb4f5SYOSHIFUJI Hideaki 		prefix = (struct in6_addr *)rinfo->prefix;
60070ceb4f5SYOSHIFUJI Hideaki 	else {
60170ceb4f5SYOSHIFUJI Hideaki 		/* this function is safe */
60270ceb4f5SYOSHIFUJI Hideaki 		ipv6_addr_prefix(&prefix_buf,
60370ceb4f5SYOSHIFUJI Hideaki 				 (struct in6_addr *)rinfo->prefix,
60470ceb4f5SYOSHIFUJI Hideaki 				 rinfo->prefix_len);
60570ceb4f5SYOSHIFUJI Hideaki 		prefix = &prefix_buf;
60670ceb4f5SYOSHIFUJI Hideaki 	}
60770ceb4f5SYOSHIFUJI Hideaki 
608efa2cea0SDaniel Lezcano 	rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
609efa2cea0SDaniel Lezcano 				dev->ifindex);
61070ceb4f5SYOSHIFUJI Hideaki 
61170ceb4f5SYOSHIFUJI Hideaki 	if (rt && !lifetime) {
612e0a1ad73SThomas Graf 		ip6_del_rt(rt);
61370ceb4f5SYOSHIFUJI Hideaki 		rt = NULL;
61470ceb4f5SYOSHIFUJI Hideaki 	}
61570ceb4f5SYOSHIFUJI Hideaki 
61670ceb4f5SYOSHIFUJI Hideaki 	if (!rt && lifetime)
617efa2cea0SDaniel Lezcano 		rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
61870ceb4f5SYOSHIFUJI Hideaki 					pref);
61970ceb4f5SYOSHIFUJI Hideaki 	else if (rt)
62070ceb4f5SYOSHIFUJI Hideaki 		rt->rt6i_flags = RTF_ROUTEINFO |
62170ceb4f5SYOSHIFUJI Hideaki 				 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
62270ceb4f5SYOSHIFUJI Hideaki 
62370ceb4f5SYOSHIFUJI Hideaki 	if (rt) {
6244bed72e4SYOSHIFUJI Hideaki 		if (!addrconf_finite_timeout(lifetime)) {
62570ceb4f5SYOSHIFUJI Hideaki 			rt->rt6i_flags &= ~RTF_EXPIRES;
62670ceb4f5SYOSHIFUJI Hideaki 		} else {
627d1918542SDavid S. Miller 			rt->dst.expires = jiffies + HZ * lifetime;
62870ceb4f5SYOSHIFUJI Hideaki 			rt->rt6i_flags |= RTF_EXPIRES;
62970ceb4f5SYOSHIFUJI Hideaki 		}
630d8d1f30bSChangli Gao 		dst_release(&rt->dst);
63170ceb4f5SYOSHIFUJI Hideaki 	}
63270ceb4f5SYOSHIFUJI Hideaki 	return 0;
63370ceb4f5SYOSHIFUJI Hideaki }
63470ceb4f5SYOSHIFUJI Hideaki #endif
63570ceb4f5SYOSHIFUJI Hideaki 
6368ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr)			\
637982f56f3SYOSHIFUJI Hideaki do { \
6388ed67789SDaniel Lezcano 	if (rt == __net->ipv6.ip6_null_entry) {	\
639982f56f3SYOSHIFUJI Hideaki 		struct fib6_node *pn; \
640e0eda7bbSVille Nuorvala 		while (1) { \
641982f56f3SYOSHIFUJI Hideaki 			if (fn->fn_flags & RTN_TL_ROOT) \
642c71099acSThomas Graf 				goto out; \
643982f56f3SYOSHIFUJI Hideaki 			pn = fn->parent; \
644982f56f3SYOSHIFUJI Hideaki 			if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
6458bce65b9SKim Nordlund 				fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
646982f56f3SYOSHIFUJI Hideaki 			else \
647982f56f3SYOSHIFUJI Hideaki 				fn = pn; \
648c71099acSThomas Graf 			if (fn->fn_flags & RTN_RTINFO) \
649c71099acSThomas Graf 				goto restart; \
650c71099acSThomas Graf 		} \
651982f56f3SYOSHIFUJI Hideaki 	} \
652982f56f3SYOSHIFUJI Hideaki } while (0)
653c71099acSThomas Graf 
6548ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net,
6558ed67789SDaniel Lezcano 					     struct fib6_table *table,
6564c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
6571da177e4SLinus Torvalds {
6581da177e4SLinus Torvalds 	struct fib6_node *fn;
6591da177e4SLinus Torvalds 	struct rt6_info *rt;
6601da177e4SLinus Torvalds 
661c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
6624c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
663c71099acSThomas Graf restart:
664c71099acSThomas Graf 	rt = fn->leaf;
6654c9483b2SDavid S. Miller 	rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
6664c9483b2SDavid S. Miller 	BACKTRACK(net, &fl6->saddr);
667c71099acSThomas Graf out:
668d8d1f30bSChangli Gao 	dst_use(&rt->dst, jiffies);
669c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
6701da177e4SLinus Torvalds 	return rt;
671c71099acSThomas Graf 
672c71099acSThomas Graf }
673c71099acSThomas Graf 
674ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
675ea6e574eSFlorian Westphal 				    int flags)
676ea6e574eSFlorian Westphal {
677ea6e574eSFlorian Westphal 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
678ea6e574eSFlorian Westphal }
679ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup);
680ea6e574eSFlorian Westphal 
6819acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
6829acd9f3aSYOSHIFUJI Hideaki 			    const struct in6_addr *saddr, int oif, int strict)
683c71099acSThomas Graf {
6844c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
6854c9483b2SDavid S. Miller 		.flowi6_oif = oif,
6864c9483b2SDavid S. Miller 		.daddr = *daddr,
687c71099acSThomas Graf 	};
688c71099acSThomas Graf 	struct dst_entry *dst;
68977d16f45SYOSHIFUJI Hideaki 	int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
690c71099acSThomas Graf 
691adaa70bbSThomas Graf 	if (saddr) {
6924c9483b2SDavid S. Miller 		memcpy(&fl6.saddr, saddr, sizeof(*saddr));
693adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
694adaa70bbSThomas Graf 	}
695adaa70bbSThomas Graf 
6964c9483b2SDavid S. Miller 	dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
697c71099acSThomas Graf 	if (dst->error == 0)
698c71099acSThomas Graf 		return (struct rt6_info *) dst;
699c71099acSThomas Graf 
700c71099acSThomas Graf 	dst_release(dst);
701c71099acSThomas Graf 
7021da177e4SLinus Torvalds 	return NULL;
7031da177e4SLinus Torvalds }
7041da177e4SLinus Torvalds 
7057159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup);
7067159039aSYOSHIFUJI Hideaki 
707c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock.
7081da177e4SLinus Torvalds    It takes new route entry, the addition fails by any reason the
7091da177e4SLinus Torvalds    route is freed. In any case, if caller does not hold it, it may
7101da177e4SLinus Torvalds    be destroyed.
7111da177e4SLinus Torvalds  */
7121da177e4SLinus Torvalds 
71386872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
7141da177e4SLinus Torvalds {
7151da177e4SLinus Torvalds 	int err;
716c71099acSThomas Graf 	struct fib6_table *table;
7171da177e4SLinus Torvalds 
718c71099acSThomas Graf 	table = rt->rt6i_table;
719c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
72086872cb5SThomas Graf 	err = fib6_add(&table->tb6_root, rt, info);
721c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 	return err;
7241da177e4SLinus Torvalds }
7251da177e4SLinus Torvalds 
72640e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt)
72740e22e8fSThomas Graf {
7284d1169c1SDenis V. Lunev 	struct nl_info info = {
729d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
7304d1169c1SDenis V. Lunev 	};
731528c4cebSDenis V. Lunev 	return __ip6_ins_rt(rt, &info);
73240e22e8fSThomas Graf }
73340e22e8fSThomas Graf 
73421efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_cow(const struct rt6_info *ort,
73521efcfa0SEric Dumazet 				      const struct in6_addr *daddr,
736b71d1d42SEric Dumazet 				      const struct in6_addr *saddr)
7371da177e4SLinus Torvalds {
7381da177e4SLinus Torvalds 	struct rt6_info *rt;
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds 	/*
7411da177e4SLinus Torvalds 	 *	Clone the route.
7421da177e4SLinus Torvalds 	 */
7431da177e4SLinus Torvalds 
74421efcfa0SEric Dumazet 	rt = ip6_rt_copy(ort, daddr);
7451da177e4SLinus Torvalds 
7461da177e4SLinus Torvalds 	if (rt) {
74714deae41SDavid S. Miller 		int attempts = !in_softirq();
74814deae41SDavid S. Miller 
74958c4fb86SYOSHIFUJI Hideaki 		if (!(rt->rt6i_flags & RTF_GATEWAY)) {
750bb3c3686SDavid S. Miller 			if (ort->rt6i_dst.plen != 128 &&
75121efcfa0SEric Dumazet 			    ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
75258c4fb86SYOSHIFUJI Hideaki 				rt->rt6i_flags |= RTF_ANYCAST;
7534e3fd7a0SAlexey Dobriyan 			rt->rt6i_gateway = *daddr;
75458c4fb86SYOSHIFUJI Hideaki 		}
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_CACHE;
7571da177e4SLinus Torvalds 
7581da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
7591da177e4SLinus Torvalds 		if (rt->rt6i_src.plen && saddr) {
7604e3fd7a0SAlexey Dobriyan 			rt->rt6i_src.addr = *saddr;
7611da177e4SLinus Torvalds 			rt->rt6i_src.plen = 128;
7621da177e4SLinus Torvalds 		}
7631da177e4SLinus Torvalds #endif
7641da177e4SLinus Torvalds 
76514deae41SDavid S. Miller 	retry:
7668ade06c6SDavid S. Miller 		if (rt6_bind_neighbour(rt, rt->dst.dev)) {
767d1918542SDavid S. Miller 			struct net *net = dev_net(rt->dst.dev);
76814deae41SDavid S. Miller 			int saved_rt_min_interval =
76914deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval;
77014deae41SDavid S. Miller 			int saved_rt_elasticity =
77114deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity;
77214deae41SDavid S. Miller 
77314deae41SDavid S. Miller 			if (attempts-- > 0) {
77414deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
77514deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
77614deae41SDavid S. Miller 
77786393e52SAlexey Dobriyan 				ip6_dst_gc(&net->ipv6.ip6_dst_ops);
77814deae41SDavid S. Miller 
77914deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_elasticity =
78014deae41SDavid S. Miller 					saved_rt_elasticity;
78114deae41SDavid S. Miller 				net->ipv6.sysctl.ip6_rt_gc_min_interval =
78214deae41SDavid S. Miller 					saved_rt_min_interval;
78314deae41SDavid S. Miller 				goto retry;
78414deae41SDavid S. Miller 			}
78514deae41SDavid S. Miller 
78614deae41SDavid S. Miller 			if (net_ratelimit())
78714deae41SDavid S. Miller 				printk(KERN_WARNING
7887e1b33e5SUlrich Weber 				       "ipv6: Neighbour table overflow.\n");
789d8d1f30bSChangli Gao 			dst_free(&rt->dst);
79014deae41SDavid S. Miller 			return NULL;
79114deae41SDavid S. Miller 		}
79295a9a5baSYOSHIFUJI Hideaki 	}
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 	return rt;
7951da177e4SLinus Torvalds }
79695a9a5baSYOSHIFUJI Hideaki 
79721efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
79821efcfa0SEric Dumazet 					const struct in6_addr *daddr)
799299d9939SYOSHIFUJI Hideaki {
80021efcfa0SEric Dumazet 	struct rt6_info *rt = ip6_rt_copy(ort, daddr);
80121efcfa0SEric Dumazet 
802299d9939SYOSHIFUJI Hideaki 	if (rt) {
803299d9939SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_CACHE;
80427217455SDavid Miller 		dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_noref_raw(&ort->dst)));
805299d9939SYOSHIFUJI Hideaki 	}
806299d9939SYOSHIFUJI Hideaki 	return rt;
807299d9939SYOSHIFUJI Hideaki }
808299d9939SYOSHIFUJI Hideaki 
8098ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
8104c9483b2SDavid S. Miller 				      struct flowi6 *fl6, int flags)
8111da177e4SLinus Torvalds {
8121da177e4SLinus Torvalds 	struct fib6_node *fn;
813519fbd87SYOSHIFUJI Hideaki 	struct rt6_info *rt, *nrt;
814c71099acSThomas Graf 	int strict = 0;
8151da177e4SLinus Torvalds 	int attempts = 3;
816519fbd87SYOSHIFUJI Hideaki 	int err;
81753b7997fSYOSHIFUJI Hideaki 	int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
8181da177e4SLinus Torvalds 
81977d16f45SYOSHIFUJI Hideaki 	strict |= flags & RT6_LOOKUP_F_IFACE;
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds relookup:
822c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
8231da177e4SLinus Torvalds 
8248238dd06SYOSHIFUJI Hideaki restart_2:
8254c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds restart:
8284acad72dSPavel Emelyanov 	rt = rt6_select(fn, oif, strict | reachable);
8298ed67789SDaniel Lezcano 
8304c9483b2SDavid S. Miller 	BACKTRACK(net, &fl6->saddr);
8318ed67789SDaniel Lezcano 	if (rt == net->ipv6.ip6_null_entry ||
8328238dd06SYOSHIFUJI Hideaki 	    rt->rt6i_flags & RTF_CACHE)
8331da177e4SLinus Torvalds 		goto out;
8341da177e4SLinus Torvalds 
835d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
836c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8371da177e4SLinus Torvalds 
83827217455SDavid Miller 	if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
8394c9483b2SDavid S. Miller 		nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
8407343ff31SDavid S. Miller 	else if (!(rt->dst.flags & DST_HOST))
8414c9483b2SDavid S. Miller 		nrt = rt6_alloc_clone(rt, &fl6->daddr);
8427343ff31SDavid S. Miller 	else
8437343ff31SDavid S. Miller 		goto out2;
8441da177e4SLinus Torvalds 
845d8d1f30bSChangli Gao 	dst_release(&rt->dst);
8468ed67789SDaniel Lezcano 	rt = nrt ? : net->ipv6.ip6_null_entry;
8471da177e4SLinus Torvalds 
848d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
849e40cf353SYOSHIFUJI Hideaki 	if (nrt) {
85040e22e8fSThomas Graf 		err = ip6_ins_rt(nrt);
851e40cf353SYOSHIFUJI Hideaki 		if (!err)
852e40cf353SYOSHIFUJI Hideaki 			goto out2;
853e40cf353SYOSHIFUJI Hideaki 	}
854e40cf353SYOSHIFUJI Hideaki 
855e40cf353SYOSHIFUJI Hideaki 	if (--attempts <= 0)
8561da177e4SLinus Torvalds 		goto out2;
8571da177e4SLinus Torvalds 
858519fbd87SYOSHIFUJI Hideaki 	/*
859c71099acSThomas Graf 	 * Race condition! In the gap, when table->tb6_lock was
860519fbd87SYOSHIFUJI Hideaki 	 * released someone could insert this route.  Relookup.
8611da177e4SLinus Torvalds 	 */
862d8d1f30bSChangli Gao 	dst_release(&rt->dst);
8631da177e4SLinus Torvalds 	goto relookup;
864e40cf353SYOSHIFUJI Hideaki 
865519fbd87SYOSHIFUJI Hideaki out:
8668238dd06SYOSHIFUJI Hideaki 	if (reachable) {
8678238dd06SYOSHIFUJI Hideaki 		reachable = 0;
8688238dd06SYOSHIFUJI Hideaki 		goto restart_2;
8698238dd06SYOSHIFUJI Hideaki 	}
870d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
871c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
8721da177e4SLinus Torvalds out2:
873d8d1f30bSChangli Gao 	rt->dst.lastuse = jiffies;
874d8d1f30bSChangli Gao 	rt->dst.__use++;
875c71099acSThomas Graf 
876c71099acSThomas Graf 	return rt;
877c71099acSThomas Graf }
878c71099acSThomas Graf 
8798ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
8804c9483b2SDavid S. Miller 					    struct flowi6 *fl6, int flags)
8814acad72dSPavel Emelyanov {
8824c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
8834acad72dSPavel Emelyanov }
8844acad72dSPavel Emelyanov 
885c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb)
886c71099acSThomas Graf {
887b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
888c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(skb->dev);
889adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
8904c9483b2SDavid S. Miller 	struct flowi6 fl6 = {
8914c9483b2SDavid S. Miller 		.flowi6_iif = skb->dev->ifindex,
8924c9483b2SDavid S. Miller 		.daddr = iph->daddr,
8934c9483b2SDavid S. Miller 		.saddr = iph->saddr,
8944c9483b2SDavid S. Miller 		.flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
8954c9483b2SDavid S. Miller 		.flowi6_mark = skb->mark,
8964c9483b2SDavid S. Miller 		.flowi6_proto = iph->nexthdr,
897c71099acSThomas Graf 	};
898adaa70bbSThomas Graf 
8991d6e55f1SThomas Goff 	if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG)
900adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_IFACE;
901c71099acSThomas Graf 
9024c9483b2SDavid S. Miller 	skb_dst_set(skb, fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input));
903c71099acSThomas Graf }
904c71099acSThomas Graf 
9058ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
9064c9483b2SDavid S. Miller 					     struct flowi6 *fl6, int flags)
907c71099acSThomas Graf {
9084c9483b2SDavid S. Miller 	return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
909c71099acSThomas Graf }
910c71099acSThomas Graf 
9119c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
9124c9483b2SDavid S. Miller 				    struct flowi6 *fl6)
913c71099acSThomas Graf {
914c71099acSThomas Graf 	int flags = 0;
915c71099acSThomas Graf 
9164c9483b2SDavid S. Miller 	if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
91777d16f45SYOSHIFUJI Hideaki 		flags |= RT6_LOOKUP_F_IFACE;
918c71099acSThomas Graf 
9194c9483b2SDavid S. Miller 	if (!ipv6_addr_any(&fl6->saddr))
920adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_HAS_SADDR;
9210c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 	else if (sk)
9220c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 		flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
923adaa70bbSThomas Graf 
9244c9483b2SDavid S. Miller 	return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
9251da177e4SLinus Torvalds }
9261da177e4SLinus Torvalds 
9277159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output);
9281da177e4SLinus Torvalds 
9292774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
93014e50e57SDavid S. Miller {
9315c1e6aa3SDavid S. Miller 	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
93214e50e57SDavid S. Miller 	struct dst_entry *new = NULL;
93314e50e57SDavid S. Miller 
9345c1e6aa3SDavid S. Miller 	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
93514e50e57SDavid S. Miller 	if (rt) {
936cf911662SDavid S. Miller 		memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
937cf911662SDavid S. Miller 
938d8d1f30bSChangli Gao 		new = &rt->dst;
93914e50e57SDavid S. Miller 
94014e50e57SDavid S. Miller 		new->__use = 1;
941352e512cSHerbert Xu 		new->input = dst_discard;
942352e512cSHerbert Xu 		new->output = dst_discard;
94314e50e57SDavid S. Miller 
94421efcfa0SEric Dumazet 		if (dst_metrics_read_only(&ort->dst))
94521efcfa0SEric Dumazet 			new->_metrics = ort->dst._metrics;
94621efcfa0SEric Dumazet 		else
947defb3519SDavid S. Miller 			dst_copy_metrics(new, &ort->dst);
94814e50e57SDavid S. Miller 		rt->rt6i_idev = ort->rt6i_idev;
94914e50e57SDavid S. Miller 		if (rt->rt6i_idev)
95014e50e57SDavid S. Miller 			in6_dev_hold(rt->rt6i_idev);
951d1918542SDavid S. Miller 		rt->dst.expires = 0;
95214e50e57SDavid S. Miller 
9534e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
95414e50e57SDavid S. Miller 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
95514e50e57SDavid S. Miller 		rt->rt6i_metric = 0;
95614e50e57SDavid S. Miller 
95714e50e57SDavid S. Miller 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
95814e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES
95914e50e57SDavid S. Miller 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
96014e50e57SDavid S. Miller #endif
96114e50e57SDavid S. Miller 
96214e50e57SDavid S. Miller 		dst_free(new);
96314e50e57SDavid S. Miller 	}
96414e50e57SDavid S. Miller 
96569ead7afSDavid S. Miller 	dst_release(dst_orig);
96669ead7afSDavid S. Miller 	return new ? new : ERR_PTR(-ENOMEM);
96714e50e57SDavid S. Miller }
96814e50e57SDavid S. Miller 
9691da177e4SLinus Torvalds /*
9701da177e4SLinus Torvalds  *	Destination cache support functions
9711da177e4SLinus Torvalds  */
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
9741da177e4SLinus Torvalds {
9751da177e4SLinus Torvalds 	struct rt6_info *rt;
9761da177e4SLinus Torvalds 
9771da177e4SLinus Torvalds 	rt = (struct rt6_info *) dst;
9781da177e4SLinus Torvalds 
9796431cbc2SDavid S. Miller 	if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
9806431cbc2SDavid S. Miller 		if (rt->rt6i_peer_genid != rt6_peer_genid()) {
9816431cbc2SDavid S. Miller 			if (!rt->rt6i_peer)
9826431cbc2SDavid S. Miller 				rt6_bind_peer(rt, 0);
9836431cbc2SDavid S. Miller 			rt->rt6i_peer_genid = rt6_peer_genid();
9846431cbc2SDavid S. Miller 		}
9851da177e4SLinus Torvalds 		return dst;
9866431cbc2SDavid S. Miller 	}
9871da177e4SLinus Torvalds 	return NULL;
9881da177e4SLinus Torvalds }
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
9911da177e4SLinus Torvalds {
9921da177e4SLinus Torvalds 	struct rt6_info *rt = (struct rt6_info *) dst;
9931da177e4SLinus Torvalds 
9941da177e4SLinus Torvalds 	if (rt) {
99554c1a859SYOSHIFUJI Hideaki / 吉藤英明 		if (rt->rt6i_flags & RTF_CACHE) {
99654c1a859SYOSHIFUJI Hideaki / 吉藤英明 			if (rt6_check_expired(rt)) {
997e0a1ad73SThomas Graf 				ip6_del_rt(rt);
99854c1a859SYOSHIFUJI Hideaki / 吉藤英明 				dst = NULL;
9991da177e4SLinus Torvalds 			}
100054c1a859SYOSHIFUJI Hideaki / 吉藤英明 		} else {
100154c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst_release(dst);
100254c1a859SYOSHIFUJI Hideaki / 吉藤英明 			dst = NULL;
100354c1a859SYOSHIFUJI Hideaki / 吉藤英明 		}
100454c1a859SYOSHIFUJI Hideaki / 吉藤英明 	}
100554c1a859SYOSHIFUJI Hideaki / 吉藤英明 	return dst;
10061da177e4SLinus Torvalds }
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb)
10091da177e4SLinus Torvalds {
10101da177e4SLinus Torvalds 	struct rt6_info *rt;
10111da177e4SLinus Torvalds 
10123ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
10131da177e4SLinus Torvalds 
1014adf30907SEric Dumazet 	rt = (struct rt6_info *) skb_dst(skb);
10151da177e4SLinus Torvalds 	if (rt) {
10161da177e4SLinus Torvalds 		if (rt->rt6i_flags & RTF_CACHE) {
1017d8d1f30bSChangli Gao 			dst_set_expires(&rt->dst, 0);
10181da177e4SLinus Torvalds 			rt->rt6i_flags |= RTF_EXPIRES;
10191da177e4SLinus Torvalds 		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
10201da177e4SLinus Torvalds 			rt->rt6i_node->fn_sernum = -1;
10211da177e4SLinus Torvalds 	}
10221da177e4SLinus Torvalds }
10231da177e4SLinus Torvalds 
10241da177e4SLinus Torvalds static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
10251da177e4SLinus Torvalds {
10261da177e4SLinus Torvalds 	struct rt6_info *rt6 = (struct rt6_info*)dst;
10271da177e4SLinus Torvalds 
10281da177e4SLinus Torvalds 	if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
10291da177e4SLinus Torvalds 		rt6->rt6i_flags |= RTF_MODIFIED;
10301da177e4SLinus Torvalds 		if (mtu < IPV6_MIN_MTU) {
1031defb3519SDavid S. Miller 			u32 features = dst_metric(dst, RTAX_FEATURES);
10321da177e4SLinus Torvalds 			mtu = IPV6_MIN_MTU;
1033defb3519SDavid S. Miller 			features |= RTAX_FEATURE_ALLFRAG;
1034defb3519SDavid S. Miller 			dst_metric_set(dst, RTAX_FEATURES, features);
10351da177e4SLinus Torvalds 		}
1036defb3519SDavid S. Miller 		dst_metric_set(dst, RTAX_MTU, mtu);
10371da177e4SLinus Torvalds 	}
10381da177e4SLinus Torvalds }
10391da177e4SLinus Torvalds 
10400dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst)
10411da177e4SLinus Torvalds {
10420dbaee3bSDavid S. Miller 	struct net_device *dev = dst->dev;
10430dbaee3bSDavid S. Miller 	unsigned int mtu = dst_mtu(dst);
10440dbaee3bSDavid S. Miller 	struct net *net = dev_net(dev);
10450dbaee3bSDavid S. Miller 
10461da177e4SLinus Torvalds 	mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
10471da177e4SLinus Torvalds 
10485578689aSDaniel Lezcano 	if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
10495578689aSDaniel Lezcano 		mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	/*
10521da177e4SLinus Torvalds 	 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
10531da177e4SLinus Torvalds 	 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
10541da177e4SLinus Torvalds 	 * IPV6_MAXPLEN is also valid and means: "any MSS,
10551da177e4SLinus Torvalds 	 * rely only on pmtu discovery"
10561da177e4SLinus Torvalds 	 */
10571da177e4SLinus Torvalds 	if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
10581da177e4SLinus Torvalds 		mtu = IPV6_MAXPLEN;
10591da177e4SLinus Torvalds 	return mtu;
10601da177e4SLinus Torvalds }
10611da177e4SLinus Torvalds 
1062ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst)
1063d33e4553SDavid S. Miller {
1064d33e4553SDavid S. Miller 	struct inet6_dev *idev;
1065618f9bc7SSteffen Klassert 	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1066618f9bc7SSteffen Klassert 
1067618f9bc7SSteffen Klassert 	if (mtu)
1068618f9bc7SSteffen Klassert 		return mtu;
1069618f9bc7SSteffen Klassert 
1070618f9bc7SSteffen Klassert 	mtu = IPV6_MIN_MTU;
1071d33e4553SDavid S. Miller 
1072d33e4553SDavid S. Miller 	rcu_read_lock();
1073d33e4553SDavid S. Miller 	idev = __in6_dev_get(dst->dev);
1074d33e4553SDavid S. Miller 	if (idev)
1075d33e4553SDavid S. Miller 		mtu = idev->cnf.mtu6;
1076d33e4553SDavid S. Miller 	rcu_read_unlock();
1077d33e4553SDavid S. Miller 
1078d33e4553SDavid S. Miller 	return mtu;
1079d33e4553SDavid S. Miller }
1080d33e4553SDavid S. Miller 
10813b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list;
10823b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock);
10835d0bbeebSThomas Graf 
10843b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
10851da177e4SLinus Torvalds 				  struct neighbour *neigh,
108687a11578SDavid S. Miller 				  struct flowi6 *fl6)
10871da177e4SLinus Torvalds {
108887a11578SDavid S. Miller 	struct dst_entry *dst;
10891da177e4SLinus Torvalds 	struct rt6_info *rt;
10901da177e4SLinus Torvalds 	struct inet6_dev *idev = in6_dev_get(dev);
1091c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
10921da177e4SLinus Torvalds 
109338308473SDavid S. Miller 	if (unlikely(!idev))
10941da177e4SLinus Torvalds 		return NULL;
10951da177e4SLinus Torvalds 
1096957c665fSDavid S. Miller 	rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0);
109738308473SDavid S. Miller 	if (unlikely(!rt)) {
10981da177e4SLinus Torvalds 		in6_dev_put(idev);
109987a11578SDavid S. Miller 		dst = ERR_PTR(-ENOMEM);
11001da177e4SLinus Torvalds 		goto out;
11011da177e4SLinus Torvalds 	}
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds 	if (neigh)
11041da177e4SLinus Torvalds 		neigh_hold(neigh);
110514deae41SDavid S. Miller 	else {
1106f83c7790SDavid S. Miller 		neigh = ip6_neigh_lookup(&rt->dst, &fl6->daddr);
1107b43faac6SDavid S. Miller 		if (IS_ERR(neigh)) {
1108252c3d84SRongQing.Li 			in6_dev_put(idev);
1109b43faac6SDavid S. Miller 			dst_free(&rt->dst);
1110b43faac6SDavid S. Miller 			return ERR_CAST(neigh);
1111b43faac6SDavid S. Miller 		}
111214deae41SDavid S. Miller 	}
11131da177e4SLinus Torvalds 
11148e2ec639SYan, Zheng 	rt->dst.flags |= DST_HOST;
11158e2ec639SYan, Zheng 	rt->dst.output  = ip6_output;
111669cce1d1SDavid S. Miller 	dst_set_neighbour(&rt->dst, neigh);
1117d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
111887a11578SDavid S. Miller 	rt->rt6i_dst.addr = fl6->daddr;
11198e2ec639SYan, Zheng 	rt->rt6i_dst.plen = 128;
11208e2ec639SYan, Zheng 	rt->rt6i_idev     = idev;
11217011687fSGao feng 	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
11221da177e4SLinus Torvalds 
11233b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
1124d8d1f30bSChangli Gao 	rt->dst.next = icmp6_dst_gc_list;
1125d8d1f30bSChangli Gao 	icmp6_dst_gc_list = &rt->dst;
11263b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
11271da177e4SLinus Torvalds 
11285578689aSDaniel Lezcano 	fib6_force_start_gc(net);
11291da177e4SLinus Torvalds 
113087a11578SDavid S. Miller 	dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
113187a11578SDavid S. Miller 
11321da177e4SLinus Torvalds out:
113387a11578SDavid S. Miller 	return dst;
11341da177e4SLinus Torvalds }
11351da177e4SLinus Torvalds 
11363d0f24a7SStephen Hemminger int icmp6_dst_gc(void)
11371da177e4SLinus Torvalds {
1138e9476e95SHagen Paul Pfeifer 	struct dst_entry *dst, **pprev;
11393d0f24a7SStephen Hemminger 	int more = 0;
11401da177e4SLinus Torvalds 
11413b00944cSYOSHIFUJI Hideaki 	spin_lock_bh(&icmp6_dst_lock);
11423b00944cSYOSHIFUJI Hideaki 	pprev = &icmp6_dst_gc_list;
11435d0bbeebSThomas Graf 
11441da177e4SLinus Torvalds 	while ((dst = *pprev) != NULL) {
11451da177e4SLinus Torvalds 		if (!atomic_read(&dst->__refcnt)) {
11461da177e4SLinus Torvalds 			*pprev = dst->next;
11471da177e4SLinus Torvalds 			dst_free(dst);
11481da177e4SLinus Torvalds 		} else {
11491da177e4SLinus Torvalds 			pprev = &dst->next;
11503d0f24a7SStephen Hemminger 			++more;
11511da177e4SLinus Torvalds 		}
11521da177e4SLinus Torvalds 	}
11531da177e4SLinus Torvalds 
11543b00944cSYOSHIFUJI Hideaki 	spin_unlock_bh(&icmp6_dst_lock);
11555d0bbeebSThomas Graf 
11563d0f24a7SStephen Hemminger 	return more;
11571da177e4SLinus Torvalds }
11581da177e4SLinus Torvalds 
11591e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
11601e493d19SDavid S. Miller 			    void *arg)
11611e493d19SDavid S. Miller {
11621e493d19SDavid S. Miller 	struct dst_entry *dst, **pprev;
11631e493d19SDavid S. Miller 
11641e493d19SDavid S. Miller 	spin_lock_bh(&icmp6_dst_lock);
11651e493d19SDavid S. Miller 	pprev = &icmp6_dst_gc_list;
11661e493d19SDavid S. Miller 	while ((dst = *pprev) != NULL) {
11671e493d19SDavid S. Miller 		struct rt6_info *rt = (struct rt6_info *) dst;
11681e493d19SDavid S. Miller 		if (func(rt, arg)) {
11691e493d19SDavid S. Miller 			*pprev = dst->next;
11701e493d19SDavid S. Miller 			dst_free(dst);
11711e493d19SDavid S. Miller 		} else {
11721e493d19SDavid S. Miller 			pprev = &dst->next;
11731e493d19SDavid S. Miller 		}
11741e493d19SDavid S. Miller 	}
11751e493d19SDavid S. Miller 	spin_unlock_bh(&icmp6_dst_lock);
11761e493d19SDavid S. Miller }
11771e493d19SDavid S. Miller 
1178569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops)
11791da177e4SLinus Torvalds {
11801da177e4SLinus Torvalds 	unsigned long now = jiffies;
118186393e52SAlexey Dobriyan 	struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
11827019b78eSDaniel Lezcano 	int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
11837019b78eSDaniel Lezcano 	int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
11847019b78eSDaniel Lezcano 	int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
11857019b78eSDaniel Lezcano 	int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
11867019b78eSDaniel Lezcano 	unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
1187fc66f95cSEric Dumazet 	int entries;
11881da177e4SLinus Torvalds 
1189fc66f95cSEric Dumazet 	entries = dst_entries_get_fast(ops);
11907019b78eSDaniel Lezcano 	if (time_after(rt_last_gc + rt_min_interval, now) &&
1191fc66f95cSEric Dumazet 	    entries <= rt_max_size)
11921da177e4SLinus Torvalds 		goto out;
11931da177e4SLinus Torvalds 
11946891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire++;
11956891a346SBenjamin Thery 	fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
11966891a346SBenjamin Thery 	net->ipv6.ip6_rt_last_gc = now;
1197fc66f95cSEric Dumazet 	entries = dst_entries_get_slow(ops);
1198fc66f95cSEric Dumazet 	if (entries < ops->gc_thresh)
11997019b78eSDaniel Lezcano 		net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
12001da177e4SLinus Torvalds out:
12017019b78eSDaniel Lezcano 	net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1202fc66f95cSEric Dumazet 	return entries > rt_max_size;
12031da177e4SLinus Torvalds }
12041da177e4SLinus Torvalds 
12051da177e4SLinus Torvalds /* Clean host part of a prefix. Not necessary in radix tree,
12061da177e4SLinus Torvalds    but results in cleaner routing tables.
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds    Remove it only when all the things will work!
12091da177e4SLinus Torvalds  */
12101da177e4SLinus Torvalds 
12116b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst)
12121da177e4SLinus Torvalds {
12135170ae82SDavid S. Miller 	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
1214a02e4b7dSDavid S. Miller 	if (hoplimit == 0) {
12156b75d090SYOSHIFUJI Hideaki 		struct net_device *dev = dst->dev;
1216c68f24ccSEric Dumazet 		struct inet6_dev *idev;
1217c68f24ccSEric Dumazet 
1218c68f24ccSEric Dumazet 		rcu_read_lock();
1219c68f24ccSEric Dumazet 		idev = __in6_dev_get(dev);
1220c68f24ccSEric Dumazet 		if (idev)
12211da177e4SLinus Torvalds 			hoplimit = idev->cnf.hop_limit;
1222c68f24ccSEric Dumazet 		else
122353b7997fSYOSHIFUJI Hideaki 			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
1224c68f24ccSEric Dumazet 		rcu_read_unlock();
12251da177e4SLinus Torvalds 	}
12261da177e4SLinus Torvalds 	return hoplimit;
12271da177e4SLinus Torvalds }
1228abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit);
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds /*
12311da177e4SLinus Torvalds  *
12321da177e4SLinus Torvalds  */
12331da177e4SLinus Torvalds 
123486872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg)
12351da177e4SLinus Torvalds {
12361da177e4SLinus Torvalds 	int err;
12375578689aSDaniel Lezcano 	struct net *net = cfg->fc_nlinfo.nl_net;
12381da177e4SLinus Torvalds 	struct rt6_info *rt = NULL;
12391da177e4SLinus Torvalds 	struct net_device *dev = NULL;
12401da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
1241c71099acSThomas Graf 	struct fib6_table *table;
12421da177e4SLinus Torvalds 	int addr_type;
12431da177e4SLinus Torvalds 
124486872cb5SThomas Graf 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
12451da177e4SLinus Torvalds 		return -EINVAL;
12461da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES
124786872cb5SThomas Graf 	if (cfg->fc_src_len)
12481da177e4SLinus Torvalds 		return -EINVAL;
12491da177e4SLinus Torvalds #endif
125086872cb5SThomas Graf 	if (cfg->fc_ifindex) {
12511da177e4SLinus Torvalds 		err = -ENODEV;
12525578689aSDaniel Lezcano 		dev = dev_get_by_index(net, cfg->fc_ifindex);
12531da177e4SLinus Torvalds 		if (!dev)
12541da177e4SLinus Torvalds 			goto out;
12551da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
12561da177e4SLinus Torvalds 		if (!idev)
12571da177e4SLinus Torvalds 			goto out;
12581da177e4SLinus Torvalds 	}
12591da177e4SLinus Torvalds 
126086872cb5SThomas Graf 	if (cfg->fc_metric == 0)
126186872cb5SThomas Graf 		cfg->fc_metric = IP6_RT_PRIO_USER;
12621da177e4SLinus Torvalds 
1263c71099acSThomas Graf 	err = -ENOBUFS;
126438308473SDavid S. Miller 	if (cfg->fc_nlinfo.nlh &&
1265d71314b4SMatti Vaittinen 	    !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
1266d71314b4SMatti Vaittinen 		table = fib6_get_table(net, cfg->fc_table);
126738308473SDavid S. Miller 		if (!table) {
1268d71314b4SMatti Vaittinen 			printk(KERN_WARNING "IPv6: NLM_F_CREATE should be specified when creating new route\n");
1269d71314b4SMatti Vaittinen 			table = fib6_new_table(net, cfg->fc_table);
1270d71314b4SMatti Vaittinen 		}
1271d71314b4SMatti Vaittinen 	} else {
1272d71314b4SMatti Vaittinen 		table = fib6_new_table(net, cfg->fc_table);
1273d71314b4SMatti Vaittinen 	}
127438308473SDavid S. Miller 
127538308473SDavid S. Miller 	if (!table)
1276c71099acSThomas Graf 		goto out;
1277c71099acSThomas Graf 
1278957c665fSDavid S. Miller 	rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL, DST_NOCOUNT);
12791da177e4SLinus Torvalds 
128038308473SDavid S. Miller 	if (!rt) {
12811da177e4SLinus Torvalds 		err = -ENOMEM;
12821da177e4SLinus Torvalds 		goto out;
12831da177e4SLinus Torvalds 	}
12841da177e4SLinus Torvalds 
1285d8d1f30bSChangli Gao 	rt->dst.obsolete = -1;
1286d1918542SDavid S. Miller 	rt->dst.expires = (cfg->fc_flags & RTF_EXPIRES) ?
12876f704992SYOSHIFUJI Hideaki 				jiffies + clock_t_to_jiffies(cfg->fc_expires) :
12886f704992SYOSHIFUJI Hideaki 				0;
12891da177e4SLinus Torvalds 
129086872cb5SThomas Graf 	if (cfg->fc_protocol == RTPROT_UNSPEC)
129186872cb5SThomas Graf 		cfg->fc_protocol = RTPROT_BOOT;
129286872cb5SThomas Graf 	rt->rt6i_protocol = cfg->fc_protocol;
129386872cb5SThomas Graf 
129486872cb5SThomas Graf 	addr_type = ipv6_addr_type(&cfg->fc_dst);
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 	if (addr_type & IPV6_ADDR_MULTICAST)
1297d8d1f30bSChangli Gao 		rt->dst.input = ip6_mc_input;
1298ab79ad14SMaciej Żenczykowski 	else if (cfg->fc_flags & RTF_LOCAL)
1299ab79ad14SMaciej Żenczykowski 		rt->dst.input = ip6_input;
13001da177e4SLinus Torvalds 	else
1301d8d1f30bSChangli Gao 		rt->dst.input = ip6_forward;
13021da177e4SLinus Torvalds 
1303d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
13041da177e4SLinus Torvalds 
130586872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
130686872cb5SThomas Graf 	rt->rt6i_dst.plen = cfg->fc_dst_len;
13071da177e4SLinus Torvalds 	if (rt->rt6i_dst.plen == 128)
130811d53b49SDavid S. Miller 	       rt->dst.flags |= DST_HOST;
13091da177e4SLinus Torvalds 
13108e2ec639SYan, Zheng 	if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
13118e2ec639SYan, Zheng 		u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
13128e2ec639SYan, Zheng 		if (!metrics) {
13138e2ec639SYan, Zheng 			err = -ENOMEM;
13148e2ec639SYan, Zheng 			goto out;
13158e2ec639SYan, Zheng 		}
13168e2ec639SYan, Zheng 		dst_init_metrics(&rt->dst, metrics, 0);
13178e2ec639SYan, Zheng 	}
13181da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
131986872cb5SThomas Graf 	ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
132086872cb5SThomas Graf 	rt->rt6i_src.plen = cfg->fc_src_len;
13211da177e4SLinus Torvalds #endif
13221da177e4SLinus Torvalds 
132386872cb5SThomas Graf 	rt->rt6i_metric = cfg->fc_metric;
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds 	/* We cannot add true routes via loopback here,
13261da177e4SLinus Torvalds 	   they would result in kernel looping; promote them to reject routes
13271da177e4SLinus Torvalds 	 */
132886872cb5SThomas Graf 	if ((cfg->fc_flags & RTF_REJECT) ||
132938308473SDavid S. Miller 	    (dev && (dev->flags & IFF_LOOPBACK) &&
133038308473SDavid S. Miller 	     !(addr_type & IPV6_ADDR_LOOPBACK) &&
133138308473SDavid S. Miller 	     !(cfg->fc_flags & RTF_LOCAL))) {
13321da177e4SLinus Torvalds 		/* hold loopback dev/idev if we haven't done so. */
13335578689aSDaniel Lezcano 		if (dev != net->loopback_dev) {
13341da177e4SLinus Torvalds 			if (dev) {
13351da177e4SLinus Torvalds 				dev_put(dev);
13361da177e4SLinus Torvalds 				in6_dev_put(idev);
13371da177e4SLinus Torvalds 			}
13385578689aSDaniel Lezcano 			dev = net->loopback_dev;
13391da177e4SLinus Torvalds 			dev_hold(dev);
13401da177e4SLinus Torvalds 			idev = in6_dev_get(dev);
13411da177e4SLinus Torvalds 			if (!idev) {
13421da177e4SLinus Torvalds 				err = -ENODEV;
13431da177e4SLinus Torvalds 				goto out;
13441da177e4SLinus Torvalds 			}
13451da177e4SLinus Torvalds 		}
1346d8d1f30bSChangli Gao 		rt->dst.output = ip6_pkt_discard_out;
1347d8d1f30bSChangli Gao 		rt->dst.input = ip6_pkt_discard;
1348d8d1f30bSChangli Gao 		rt->dst.error = -ENETUNREACH;
13491da177e4SLinus Torvalds 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
13501da177e4SLinus Torvalds 		goto install_route;
13511da177e4SLinus Torvalds 	}
13521da177e4SLinus Torvalds 
135386872cb5SThomas Graf 	if (cfg->fc_flags & RTF_GATEWAY) {
1354b71d1d42SEric Dumazet 		const struct in6_addr *gw_addr;
13551da177e4SLinus Torvalds 		int gwa_type;
13561da177e4SLinus Torvalds 
135786872cb5SThomas Graf 		gw_addr = &cfg->fc_gateway;
13584e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = *gw_addr;
13591da177e4SLinus Torvalds 		gwa_type = ipv6_addr_type(gw_addr);
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
13621da177e4SLinus Torvalds 			struct rt6_info *grt;
13631da177e4SLinus Torvalds 
13641da177e4SLinus Torvalds 			/* IPv6 strictly inhibits using not link-local
13651da177e4SLinus Torvalds 			   addresses as nexthop address.
13661da177e4SLinus Torvalds 			   Otherwise, router will not able to send redirects.
13671da177e4SLinus Torvalds 			   It is very good, but in some (rare!) circumstances
13681da177e4SLinus Torvalds 			   (SIT, PtP, NBMA NOARP links) it is handy to allow
13691da177e4SLinus Torvalds 			   some exceptions. --ANK
13701da177e4SLinus Torvalds 			 */
13711da177e4SLinus Torvalds 			err = -EINVAL;
13721da177e4SLinus Torvalds 			if (!(gwa_type & IPV6_ADDR_UNICAST))
13731da177e4SLinus Torvalds 				goto out;
13741da177e4SLinus Torvalds 
13755578689aSDaniel Lezcano 			grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
13761da177e4SLinus Torvalds 
13771da177e4SLinus Torvalds 			err = -EHOSTUNREACH;
137838308473SDavid S. Miller 			if (!grt)
13791da177e4SLinus Torvalds 				goto out;
13801da177e4SLinus Torvalds 			if (dev) {
1381d1918542SDavid S. Miller 				if (dev != grt->dst.dev) {
1382d8d1f30bSChangli Gao 					dst_release(&grt->dst);
13831da177e4SLinus Torvalds 					goto out;
13841da177e4SLinus Torvalds 				}
13851da177e4SLinus Torvalds 			} else {
1386d1918542SDavid S. Miller 				dev = grt->dst.dev;
13871da177e4SLinus Torvalds 				idev = grt->rt6i_idev;
13881da177e4SLinus Torvalds 				dev_hold(dev);
13891da177e4SLinus Torvalds 				in6_dev_hold(grt->rt6i_idev);
13901da177e4SLinus Torvalds 			}
13911da177e4SLinus Torvalds 			if (!(grt->rt6i_flags & RTF_GATEWAY))
13921da177e4SLinus Torvalds 				err = 0;
1393d8d1f30bSChangli Gao 			dst_release(&grt->dst);
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds 			if (err)
13961da177e4SLinus Torvalds 				goto out;
13971da177e4SLinus Torvalds 		}
13981da177e4SLinus Torvalds 		err = -EINVAL;
139938308473SDavid S. Miller 		if (!dev || (dev->flags & IFF_LOOPBACK))
14001da177e4SLinus Torvalds 			goto out;
14011da177e4SLinus Torvalds 	}
14021da177e4SLinus Torvalds 
14031da177e4SLinus Torvalds 	err = -ENODEV;
140438308473SDavid S. Miller 	if (!dev)
14051da177e4SLinus Torvalds 		goto out;
14061da177e4SLinus Torvalds 
1407c3968a85SDaniel Walter 	if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1408c3968a85SDaniel Walter 		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1409c3968a85SDaniel Walter 			err = -EINVAL;
1410c3968a85SDaniel Walter 			goto out;
1411c3968a85SDaniel Walter 		}
14124e3fd7a0SAlexey Dobriyan 		rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
1413c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 128;
1414c3968a85SDaniel Walter 	} else
1415c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
1416c3968a85SDaniel Walter 
141786872cb5SThomas Graf 	if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
14188ade06c6SDavid S. Miller 		err = rt6_bind_neighbour(rt, dev);
1419f83c7790SDavid S. Miller 		if (err)
14201da177e4SLinus Torvalds 			goto out;
14211da177e4SLinus Torvalds 	}
14221da177e4SLinus Torvalds 
142386872cb5SThomas Graf 	rt->rt6i_flags = cfg->fc_flags;
14241da177e4SLinus Torvalds 
14251da177e4SLinus Torvalds install_route:
142686872cb5SThomas Graf 	if (cfg->fc_mx) {
142786872cb5SThomas Graf 		struct nlattr *nla;
142886872cb5SThomas Graf 		int remaining;
14291da177e4SLinus Torvalds 
143086872cb5SThomas Graf 		nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
14318f4c1f9bSThomas Graf 			int type = nla_type(nla);
143286872cb5SThomas Graf 
143386872cb5SThomas Graf 			if (type) {
143486872cb5SThomas Graf 				if (type > RTAX_MAX) {
14351da177e4SLinus Torvalds 					err = -EINVAL;
14361da177e4SLinus Torvalds 					goto out;
14371da177e4SLinus Torvalds 				}
143886872cb5SThomas Graf 
1439defb3519SDavid S. Miller 				dst_metric_set(&rt->dst, type, nla_get_u32(nla));
14401da177e4SLinus Torvalds 			}
14411da177e4SLinus Torvalds 		}
14421da177e4SLinus Torvalds 	}
14431da177e4SLinus Torvalds 
1444d8d1f30bSChangli Gao 	rt->dst.dev = dev;
14451da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
1446c71099acSThomas Graf 	rt->rt6i_table = table;
144763152fc0SDaniel Lezcano 
1448c346dca1SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = dev_net(dev);
144963152fc0SDaniel Lezcano 
145086872cb5SThomas Graf 	return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds out:
14531da177e4SLinus Torvalds 	if (dev)
14541da177e4SLinus Torvalds 		dev_put(dev);
14551da177e4SLinus Torvalds 	if (idev)
14561da177e4SLinus Torvalds 		in6_dev_put(idev);
14571da177e4SLinus Torvalds 	if (rt)
1458d8d1f30bSChangli Gao 		dst_free(&rt->dst);
14591da177e4SLinus Torvalds 	return err;
14601da177e4SLinus Torvalds }
14611da177e4SLinus Torvalds 
146286872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
14631da177e4SLinus Torvalds {
14641da177e4SLinus Torvalds 	int err;
1465c71099acSThomas Graf 	struct fib6_table *table;
1466d1918542SDavid S. Miller 	struct net *net = dev_net(rt->dst.dev);
14671da177e4SLinus Torvalds 
14688ed67789SDaniel Lezcano 	if (rt == net->ipv6.ip6_null_entry)
14696c813a72SPatrick McHardy 		return -ENOENT;
14706c813a72SPatrick McHardy 
1471c71099acSThomas Graf 	table = rt->rt6i_table;
1472c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
14731da177e4SLinus Torvalds 
147486872cb5SThomas Graf 	err = fib6_del(rt, info);
1475d8d1f30bSChangli Gao 	dst_release(&rt->dst);
14761da177e4SLinus Torvalds 
1477c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
14781da177e4SLinus Torvalds 
14791da177e4SLinus Torvalds 	return err;
14801da177e4SLinus Torvalds }
14811da177e4SLinus Torvalds 
1482e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt)
1483e0a1ad73SThomas Graf {
14844d1169c1SDenis V. Lunev 	struct nl_info info = {
1485d1918542SDavid S. Miller 		.nl_net = dev_net(rt->dst.dev),
14864d1169c1SDenis V. Lunev 	};
1487528c4cebSDenis V. Lunev 	return __ip6_del_rt(rt, &info);
1488e0a1ad73SThomas Graf }
1489e0a1ad73SThomas Graf 
149086872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg)
14911da177e4SLinus Torvalds {
1492c71099acSThomas Graf 	struct fib6_table *table;
14931da177e4SLinus Torvalds 	struct fib6_node *fn;
14941da177e4SLinus Torvalds 	struct rt6_info *rt;
14951da177e4SLinus Torvalds 	int err = -ESRCH;
14961da177e4SLinus Torvalds 
14975578689aSDaniel Lezcano 	table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
149838308473SDavid S. Miller 	if (!table)
1499c71099acSThomas Graf 		return err;
15001da177e4SLinus Torvalds 
1501c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1502c71099acSThomas Graf 
1503c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root,
150486872cb5SThomas Graf 			 &cfg->fc_dst, cfg->fc_dst_len,
150586872cb5SThomas Graf 			 &cfg->fc_src, cfg->fc_src_len);
15061da177e4SLinus Torvalds 
15071da177e4SLinus Torvalds 	if (fn) {
1508d8d1f30bSChangli Gao 		for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
150986872cb5SThomas Graf 			if (cfg->fc_ifindex &&
1510d1918542SDavid S. Miller 			    (!rt->dst.dev ||
1511d1918542SDavid S. Miller 			     rt->dst.dev->ifindex != cfg->fc_ifindex))
15121da177e4SLinus Torvalds 				continue;
151386872cb5SThomas Graf 			if (cfg->fc_flags & RTF_GATEWAY &&
151486872cb5SThomas Graf 			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
15151da177e4SLinus Torvalds 				continue;
151686872cb5SThomas Graf 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
15171da177e4SLinus Torvalds 				continue;
1518d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1519c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
15201da177e4SLinus Torvalds 
152186872cb5SThomas Graf 			return __ip6_del_rt(rt, &cfg->fc_nlinfo);
15221da177e4SLinus Torvalds 		}
15231da177e4SLinus Torvalds 	}
1524c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
15251da177e4SLinus Torvalds 
15261da177e4SLinus Torvalds 	return err;
15271da177e4SLinus Torvalds }
15281da177e4SLinus Torvalds 
15291da177e4SLinus Torvalds /*
15301da177e4SLinus Torvalds  *	Handle redirects
15311da177e4SLinus Torvalds  */
1532a6279458SYOSHIFUJI Hideaki struct ip6rd_flowi {
15334c9483b2SDavid S. Miller 	struct flowi6 fl6;
1534a6279458SYOSHIFUJI Hideaki 	struct in6_addr gateway;
1535a6279458SYOSHIFUJI Hideaki };
15361da177e4SLinus Torvalds 
15378ed67789SDaniel Lezcano static struct rt6_info *__ip6_route_redirect(struct net *net,
15388ed67789SDaniel Lezcano 					     struct fib6_table *table,
15394c9483b2SDavid S. Miller 					     struct flowi6 *fl6,
1540a6279458SYOSHIFUJI Hideaki 					     int flags)
1541a6279458SYOSHIFUJI Hideaki {
15424c9483b2SDavid S. Miller 	struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
1543a6279458SYOSHIFUJI Hideaki 	struct rt6_info *rt;
1544a6279458SYOSHIFUJI Hideaki 	struct fib6_node *fn;
1545c71099acSThomas Graf 
1546e843b9e1SYOSHIFUJI Hideaki 	/*
1547e843b9e1SYOSHIFUJI Hideaki 	 * Get the "current" route for this destination and
1548e843b9e1SYOSHIFUJI Hideaki 	 * check if the redirect has come from approriate router.
1549e843b9e1SYOSHIFUJI Hideaki 	 *
1550e843b9e1SYOSHIFUJI Hideaki 	 * RFC 2461 specifies that redirects should only be
1551e843b9e1SYOSHIFUJI Hideaki 	 * accepted if they come from the nexthop to the target.
1552e843b9e1SYOSHIFUJI Hideaki 	 * Due to the way the routes are chosen, this notion
1553e843b9e1SYOSHIFUJI Hideaki 	 * is a bit fuzzy and one might need to check all possible
1554e843b9e1SYOSHIFUJI Hideaki 	 * routes.
1555e843b9e1SYOSHIFUJI Hideaki 	 */
15561da177e4SLinus Torvalds 
1557c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
15584c9483b2SDavid S. Miller 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
1559e843b9e1SYOSHIFUJI Hideaki restart:
1560d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
15611da177e4SLinus Torvalds 		/*
15621da177e4SLinus Torvalds 		 * Current route is on-link; redirect is always invalid.
15631da177e4SLinus Torvalds 		 *
15641da177e4SLinus Torvalds 		 * Seems, previous statement is not true. It could
15651da177e4SLinus Torvalds 		 * be node, which looks for us as on-link (f.e. proxy ndisc)
15661da177e4SLinus Torvalds 		 * But then router serving it might decide, that we should
15671da177e4SLinus Torvalds 		 * know truth 8)8) --ANK (980726).
15681da177e4SLinus Torvalds 		 */
1569e843b9e1SYOSHIFUJI Hideaki 		if (rt6_check_expired(rt))
1570e843b9e1SYOSHIFUJI Hideaki 			continue;
15711da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_GATEWAY))
1572e843b9e1SYOSHIFUJI Hideaki 			continue;
1573d1918542SDavid S. Miller 		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
1574e843b9e1SYOSHIFUJI Hideaki 			continue;
1575a6279458SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
1576e843b9e1SYOSHIFUJI Hideaki 			continue;
1577e843b9e1SYOSHIFUJI Hideaki 		break;
1578e843b9e1SYOSHIFUJI Hideaki 	}
1579a6279458SYOSHIFUJI Hideaki 
1580cb15d9c2SYOSHIFUJI Hideaki 	if (!rt)
15818ed67789SDaniel Lezcano 		rt = net->ipv6.ip6_null_entry;
15824c9483b2SDavid S. Miller 	BACKTRACK(net, &fl6->saddr);
1583cb15d9c2SYOSHIFUJI Hideaki out:
1584d8d1f30bSChangli Gao 	dst_hold(&rt->dst);
1585a6279458SYOSHIFUJI Hideaki 
1586c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
15871da177e4SLinus Torvalds 
1588a6279458SYOSHIFUJI Hideaki 	return rt;
1589a6279458SYOSHIFUJI Hideaki };
1590a6279458SYOSHIFUJI Hideaki 
1591b71d1d42SEric Dumazet static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1592b71d1d42SEric Dumazet 					   const struct in6_addr *src,
1593b71d1d42SEric Dumazet 					   const struct in6_addr *gateway,
1594a6279458SYOSHIFUJI Hideaki 					   struct net_device *dev)
1595a6279458SYOSHIFUJI Hideaki {
1596adaa70bbSThomas Graf 	int flags = RT6_LOOKUP_F_HAS_SADDR;
1597c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
1598a6279458SYOSHIFUJI Hideaki 	struct ip6rd_flowi rdfl = {
15994c9483b2SDavid S. Miller 		.fl6 = {
16004c9483b2SDavid S. Miller 			.flowi6_oif = dev->ifindex,
16014c9483b2SDavid S. Miller 			.daddr = *dest,
16024c9483b2SDavid S. Miller 			.saddr = *src,
1603a6279458SYOSHIFUJI Hideaki 		},
1604a6279458SYOSHIFUJI Hideaki 	};
1605adaa70bbSThomas Graf 
16064e3fd7a0SAlexey Dobriyan 	rdfl.gateway = *gateway;
160786c36ce4SBrian Haley 
1608adaa70bbSThomas Graf 	if (rt6_need_strict(dest))
1609adaa70bbSThomas Graf 		flags |= RT6_LOOKUP_F_IFACE;
1610a6279458SYOSHIFUJI Hideaki 
16114c9483b2SDavid S. Miller 	return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
161258f09b78SDaniel Lezcano 						   flags, __ip6_route_redirect);
1613a6279458SYOSHIFUJI Hideaki }
1614a6279458SYOSHIFUJI Hideaki 
1615b71d1d42SEric Dumazet void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
1616b71d1d42SEric Dumazet 		  const struct in6_addr *saddr,
1617a6279458SYOSHIFUJI Hideaki 		  struct neighbour *neigh, u8 *lladdr, int on_link)
1618a6279458SYOSHIFUJI Hideaki {
1619a6279458SYOSHIFUJI Hideaki 	struct rt6_info *rt, *nrt = NULL;
1620a6279458SYOSHIFUJI Hideaki 	struct netevent_redirect netevent;
1621c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(neigh->dev);
1622a6279458SYOSHIFUJI Hideaki 
1623a6279458SYOSHIFUJI Hideaki 	rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1624a6279458SYOSHIFUJI Hideaki 
16258ed67789SDaniel Lezcano 	if (rt == net->ipv6.ip6_null_entry) {
16261da177e4SLinus Torvalds 		if (net_ratelimit())
16271da177e4SLinus Torvalds 			printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
16281da177e4SLinus Torvalds 			       "for redirect target\n");
1629a6279458SYOSHIFUJI Hideaki 		goto out;
16301da177e4SLinus Torvalds 	}
16311da177e4SLinus Torvalds 
16321da177e4SLinus Torvalds 	/*
16331da177e4SLinus Torvalds 	 *	We have finally decided to accept it.
16341da177e4SLinus Torvalds 	 */
16351da177e4SLinus Torvalds 
16361da177e4SLinus Torvalds 	neigh_update(neigh, lladdr, NUD_STALE,
16371da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_WEAK_OVERRIDE|
16381da177e4SLinus Torvalds 		     NEIGH_UPDATE_F_OVERRIDE|
16391da177e4SLinus Torvalds 		     (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
16401da177e4SLinus Torvalds 				     NEIGH_UPDATE_F_ISROUTER))
16411da177e4SLinus Torvalds 		     );
16421da177e4SLinus Torvalds 
16431da177e4SLinus Torvalds 	/*
16441da177e4SLinus Torvalds 	 * Redirect received -> path was valid.
16451da177e4SLinus Torvalds 	 * Look, redirects are sent only in response to data packets,
16461da177e4SLinus Torvalds 	 * so that this nexthop apparently is reachable. --ANK
16471da177e4SLinus Torvalds 	 */
1648d8d1f30bSChangli Gao 	dst_confirm(&rt->dst);
16491da177e4SLinus Torvalds 
16501da177e4SLinus Torvalds 	/* Duplicate redirect: silently ignore. */
165127217455SDavid Miller 	if (neigh == dst_get_neighbour_noref_raw(&rt->dst))
16521da177e4SLinus Torvalds 		goto out;
16531da177e4SLinus Torvalds 
165421efcfa0SEric Dumazet 	nrt = ip6_rt_copy(rt, dest);
165538308473SDavid S. Miller 	if (!nrt)
16561da177e4SLinus Torvalds 		goto out;
16571da177e4SLinus Torvalds 
16581da177e4SLinus Torvalds 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
16591da177e4SLinus Torvalds 	if (on_link)
16601da177e4SLinus Torvalds 		nrt->rt6i_flags &= ~RTF_GATEWAY;
16611da177e4SLinus Torvalds 
16624e3fd7a0SAlexey Dobriyan 	nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
166369cce1d1SDavid S. Miller 	dst_set_neighbour(&nrt->dst, neigh_clone(neigh));
16641da177e4SLinus Torvalds 
166540e22e8fSThomas Graf 	if (ip6_ins_rt(nrt))
16661da177e4SLinus Torvalds 		goto out;
16671da177e4SLinus Torvalds 
1668d8d1f30bSChangli Gao 	netevent.old = &rt->dst;
1669d8d1f30bSChangli Gao 	netevent.new = &nrt->dst;
16708d71740cSTom Tucker 	call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
16718d71740cSTom Tucker 
16721da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
1673e0a1ad73SThomas Graf 		ip6_del_rt(rt);
16741da177e4SLinus Torvalds 		return;
16751da177e4SLinus Torvalds 	}
16761da177e4SLinus Torvalds 
16771da177e4SLinus Torvalds out:
1678d8d1f30bSChangli Gao 	dst_release(&rt->dst);
16791da177e4SLinus Torvalds }
16801da177e4SLinus Torvalds 
16811da177e4SLinus Torvalds /*
16821da177e4SLinus Torvalds  *	Handle ICMP "packet too big" messages
16831da177e4SLinus Torvalds  *	i.e. Path MTU discovery
16841da177e4SLinus Torvalds  */
16851da177e4SLinus Torvalds 
1686b71d1d42SEric Dumazet static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr,
1687ae878ae2SMaciej Żenczykowski 			     struct net *net, u32 pmtu, int ifindex)
16881da177e4SLinus Torvalds {
16891da177e4SLinus Torvalds 	struct rt6_info *rt, *nrt;
16901da177e4SLinus Torvalds 	int allfrag = 0;
1691d3052b55SAndrey Vagin again:
1692ae878ae2SMaciej Żenczykowski 	rt = rt6_lookup(net, daddr, saddr, ifindex, 0);
169338308473SDavid S. Miller 	if (!rt)
16941da177e4SLinus Torvalds 		return;
16951da177e4SLinus Torvalds 
1696d3052b55SAndrey Vagin 	if (rt6_check_expired(rt)) {
1697d3052b55SAndrey Vagin 		ip6_del_rt(rt);
1698d3052b55SAndrey Vagin 		goto again;
1699d3052b55SAndrey Vagin 	}
1700d3052b55SAndrey Vagin 
1701d8d1f30bSChangli Gao 	if (pmtu >= dst_mtu(&rt->dst))
17021da177e4SLinus Torvalds 		goto out;
17031da177e4SLinus Torvalds 
17041da177e4SLinus Torvalds 	if (pmtu < IPV6_MIN_MTU) {
17051da177e4SLinus Torvalds 		/*
17061da177e4SLinus Torvalds 		 * According to RFC2460, PMTU is set to the IPv6 Minimum Link
17071da177e4SLinus Torvalds 		 * MTU (1280) and a fragment header should always be included
17081da177e4SLinus Torvalds 		 * after a node receiving Too Big message reporting PMTU is
17091da177e4SLinus Torvalds 		 * less than the IPv6 Minimum Link MTU.
17101da177e4SLinus Torvalds 		 */
17111da177e4SLinus Torvalds 		pmtu = IPV6_MIN_MTU;
17121da177e4SLinus Torvalds 		allfrag = 1;
17131da177e4SLinus Torvalds 	}
17141da177e4SLinus Torvalds 
17151da177e4SLinus Torvalds 	/* New mtu received -> path was valid.
17161da177e4SLinus Torvalds 	   They are sent only in response to data packets,
17171da177e4SLinus Torvalds 	   so that this nexthop apparently is reachable. --ANK
17181da177e4SLinus Torvalds 	 */
1719d8d1f30bSChangli Gao 	dst_confirm(&rt->dst);
17201da177e4SLinus Torvalds 
17211da177e4SLinus Torvalds 	/* Host route. If it is static, it would be better
17221da177e4SLinus Torvalds 	   not to override it, but add new one, so that
17231da177e4SLinus Torvalds 	   when cache entry will expire old pmtu
17241da177e4SLinus Torvalds 	   would return automatically.
17251da177e4SLinus Torvalds 	 */
17261da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE) {
1727defb3519SDavid S. Miller 		dst_metric_set(&rt->dst, RTAX_MTU, pmtu);
1728defb3519SDavid S. Miller 		if (allfrag) {
1729defb3519SDavid S. Miller 			u32 features = dst_metric(&rt->dst, RTAX_FEATURES);
1730defb3519SDavid S. Miller 			features |= RTAX_FEATURE_ALLFRAG;
1731defb3519SDavid S. Miller 			dst_metric_set(&rt->dst, RTAX_FEATURES, features);
1732defb3519SDavid S. Miller 		}
1733d8d1f30bSChangli Gao 		dst_set_expires(&rt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
17341da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
17351da177e4SLinus Torvalds 		goto out;
17361da177e4SLinus Torvalds 	}
17371da177e4SLinus Torvalds 
17381da177e4SLinus Torvalds 	/* Network route.
17391da177e4SLinus Torvalds 	   Two cases are possible:
17401da177e4SLinus Torvalds 	   1. It is connected route. Action: COW
17411da177e4SLinus Torvalds 	   2. It is gatewayed route or NONEXTHOP route. Action: clone it.
17421da177e4SLinus Torvalds 	 */
174327217455SDavid Miller 	if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
1744a1e78363SYOSHIFUJI Hideaki 		nrt = rt6_alloc_cow(rt, daddr, saddr);
1745d5315b50SYOSHIFUJI Hideaki 	else
1746d5315b50SYOSHIFUJI Hideaki 		nrt = rt6_alloc_clone(rt, daddr);
1747a1e78363SYOSHIFUJI Hideaki 
1748d5315b50SYOSHIFUJI Hideaki 	if (nrt) {
1749defb3519SDavid S. Miller 		dst_metric_set(&nrt->dst, RTAX_MTU, pmtu);
1750defb3519SDavid S. Miller 		if (allfrag) {
1751defb3519SDavid S. Miller 			u32 features = dst_metric(&nrt->dst, RTAX_FEATURES);
1752defb3519SDavid S. Miller 			features |= RTAX_FEATURE_ALLFRAG;
1753defb3519SDavid S. Miller 			dst_metric_set(&nrt->dst, RTAX_FEATURES, features);
1754defb3519SDavid S. Miller 		}
1755a1e78363SYOSHIFUJI Hideaki 
17561da177e4SLinus Torvalds 		/* According to RFC 1981, detecting PMTU increase shouldn't be
1757a1e78363SYOSHIFUJI Hideaki 		 * happened within 5 mins, the recommended timer is 10 mins.
1758a1e78363SYOSHIFUJI Hideaki 		 * Here this route expiration time is set to ip6_rt_mtu_expires
1759a1e78363SYOSHIFUJI Hideaki 		 * which is 10 mins. After 10 mins the decreased pmtu is expired
1760a1e78363SYOSHIFUJI Hideaki 		 * and detecting PMTU increase will be automatically happened.
17611da177e4SLinus Torvalds 		 */
1762d8d1f30bSChangli Gao 		dst_set_expires(&nrt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
17631da177e4SLinus Torvalds 		nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
1764a1e78363SYOSHIFUJI Hideaki 
176540e22e8fSThomas Graf 		ip6_ins_rt(nrt);
17661da177e4SLinus Torvalds 	}
17671da177e4SLinus Torvalds out:
1768d8d1f30bSChangli Gao 	dst_release(&rt->dst);
17691da177e4SLinus Torvalds }
17701da177e4SLinus Torvalds 
1771b71d1d42SEric Dumazet void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr,
1772ae878ae2SMaciej Żenczykowski 			struct net_device *dev, u32 pmtu)
1773ae878ae2SMaciej Żenczykowski {
1774ae878ae2SMaciej Żenczykowski 	struct net *net = dev_net(dev);
1775ae878ae2SMaciej Żenczykowski 
1776ae878ae2SMaciej Żenczykowski 	/*
1777ae878ae2SMaciej Żenczykowski 	 * RFC 1981 states that a node "MUST reduce the size of the packets it
1778ae878ae2SMaciej Żenczykowski 	 * is sending along the path" that caused the Packet Too Big message.
1779ae878ae2SMaciej Żenczykowski 	 * Since it's not possible in the general case to determine which
1780ae878ae2SMaciej Żenczykowski 	 * interface was used to send the original packet, we update the MTU
1781ae878ae2SMaciej Żenczykowski 	 * on the interface that will be used to send future packets. We also
1782ae878ae2SMaciej Żenczykowski 	 * update the MTU on the interface that received the Packet Too Big in
1783ae878ae2SMaciej Żenczykowski 	 * case the original packet was forced out that interface with
1784ae878ae2SMaciej Żenczykowski 	 * SO_BINDTODEVICE or similar. This is the next best thing to the
1785ae878ae2SMaciej Żenczykowski 	 * correct behaviour, which would be to update the MTU on all
1786ae878ae2SMaciej Żenczykowski 	 * interfaces.
1787ae878ae2SMaciej Żenczykowski 	 */
1788ae878ae2SMaciej Żenczykowski 	rt6_do_pmtu_disc(daddr, saddr, net, pmtu, 0);
1789ae878ae2SMaciej Żenczykowski 	rt6_do_pmtu_disc(daddr, saddr, net, pmtu, dev->ifindex);
1790ae878ae2SMaciej Żenczykowski }
1791ae878ae2SMaciej Żenczykowski 
17921da177e4SLinus Torvalds /*
17931da177e4SLinus Torvalds  *	Misc support functions
17941da177e4SLinus Torvalds  */
17951da177e4SLinus Torvalds 
179621efcfa0SEric Dumazet static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort,
179721efcfa0SEric Dumazet 				    const struct in6_addr *dest)
17981da177e4SLinus Torvalds {
1799d1918542SDavid S. Miller 	struct net *net = dev_net(ort->dst.dev);
18005c1e6aa3SDavid S. Miller 	struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
1801957c665fSDavid S. Miller 					    ort->dst.dev, 0);
18021da177e4SLinus Torvalds 
18031da177e4SLinus Torvalds 	if (rt) {
1804d8d1f30bSChangli Gao 		rt->dst.input = ort->dst.input;
1805d8d1f30bSChangli Gao 		rt->dst.output = ort->dst.output;
18068e2ec639SYan, Zheng 		rt->dst.flags |= DST_HOST;
18071da177e4SLinus Torvalds 
18084e3fd7a0SAlexey Dobriyan 		rt->rt6i_dst.addr = *dest;
18098e2ec639SYan, Zheng 		rt->rt6i_dst.plen = 128;
1810defb3519SDavid S. Miller 		dst_copy_metrics(&rt->dst, &ort->dst);
1811d8d1f30bSChangli Gao 		rt->dst.error = ort->dst.error;
18121da177e4SLinus Torvalds 		rt->rt6i_idev = ort->rt6i_idev;
18131da177e4SLinus Torvalds 		if (rt->rt6i_idev)
18141da177e4SLinus Torvalds 			in6_dev_hold(rt->rt6i_idev);
1815d8d1f30bSChangli Gao 		rt->dst.lastuse = jiffies;
1816d1918542SDavid S. Miller 		rt->dst.expires = 0;
18171da177e4SLinus Torvalds 
18184e3fd7a0SAlexey Dobriyan 		rt->rt6i_gateway = ort->rt6i_gateway;
18191da177e4SLinus Torvalds 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
18201da177e4SLinus Torvalds 		rt->rt6i_metric = 0;
18211da177e4SLinus Torvalds 
18221da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
18231da177e4SLinus Torvalds 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
18241da177e4SLinus Torvalds #endif
18250f6c6392SFlorian Westphal 		memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
1826c71099acSThomas Graf 		rt->rt6i_table = ort->rt6i_table;
18271da177e4SLinus Torvalds 	}
18281da177e4SLinus Torvalds 	return rt;
18291da177e4SLinus Torvalds }
18301da177e4SLinus Torvalds 
183170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
1832efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net,
1833b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1834b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex)
183570ceb4f5SYOSHIFUJI Hideaki {
183670ceb4f5SYOSHIFUJI Hideaki 	struct fib6_node *fn;
183770ceb4f5SYOSHIFUJI Hideaki 	struct rt6_info *rt = NULL;
1838c71099acSThomas Graf 	struct fib6_table *table;
183970ceb4f5SYOSHIFUJI Hideaki 
1840efa2cea0SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_INFO);
184138308473SDavid S. Miller 	if (!table)
1842c71099acSThomas Graf 		return NULL;
1843c71099acSThomas Graf 
1844c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
1845c71099acSThomas Graf 	fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
184670ceb4f5SYOSHIFUJI Hideaki 	if (!fn)
184770ceb4f5SYOSHIFUJI Hideaki 		goto out;
184870ceb4f5SYOSHIFUJI Hideaki 
1849d8d1f30bSChangli Gao 	for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
1850d1918542SDavid S. Miller 		if (rt->dst.dev->ifindex != ifindex)
185170ceb4f5SYOSHIFUJI Hideaki 			continue;
185270ceb4f5SYOSHIFUJI Hideaki 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
185370ceb4f5SYOSHIFUJI Hideaki 			continue;
185470ceb4f5SYOSHIFUJI Hideaki 		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
185570ceb4f5SYOSHIFUJI Hideaki 			continue;
1856d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
185770ceb4f5SYOSHIFUJI Hideaki 		break;
185870ceb4f5SYOSHIFUJI Hideaki 	}
185970ceb4f5SYOSHIFUJI Hideaki out:
1860c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
186170ceb4f5SYOSHIFUJI Hideaki 	return rt;
186270ceb4f5SYOSHIFUJI Hideaki }
186370ceb4f5SYOSHIFUJI Hideaki 
1864efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net,
1865b71d1d42SEric Dumazet 					   const struct in6_addr *prefix, int prefixlen,
1866b71d1d42SEric Dumazet 					   const struct in6_addr *gwaddr, int ifindex,
186770ceb4f5SYOSHIFUJI Hideaki 					   unsigned pref)
186870ceb4f5SYOSHIFUJI Hideaki {
186986872cb5SThomas Graf 	struct fib6_config cfg = {
187086872cb5SThomas Graf 		.fc_table	= RT6_TABLE_INFO,
1871238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
187286872cb5SThomas Graf 		.fc_ifindex	= ifindex,
187386872cb5SThomas Graf 		.fc_dst_len	= prefixlen,
187486872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
187586872cb5SThomas Graf 				  RTF_UP | RTF_PREF(pref),
1876efa2cea0SDaniel Lezcano 		.fc_nlinfo.pid = 0,
1877efa2cea0SDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
1878efa2cea0SDaniel Lezcano 		.fc_nlinfo.nl_net = net,
187986872cb5SThomas Graf 	};
188070ceb4f5SYOSHIFUJI Hideaki 
18814e3fd7a0SAlexey Dobriyan 	cfg.fc_dst = *prefix;
18824e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
188386872cb5SThomas Graf 
1884e317da96SYOSHIFUJI Hideaki 	/* We should treat it as a default route if prefix length is 0. */
1885e317da96SYOSHIFUJI Hideaki 	if (!prefixlen)
188686872cb5SThomas Graf 		cfg.fc_flags |= RTF_DEFAULT;
188770ceb4f5SYOSHIFUJI Hideaki 
188886872cb5SThomas Graf 	ip6_route_add(&cfg);
188970ceb4f5SYOSHIFUJI Hideaki 
1890efa2cea0SDaniel Lezcano 	return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
189170ceb4f5SYOSHIFUJI Hideaki }
189270ceb4f5SYOSHIFUJI Hideaki #endif
189370ceb4f5SYOSHIFUJI Hideaki 
1894b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
18951da177e4SLinus Torvalds {
18961da177e4SLinus Torvalds 	struct rt6_info *rt;
1897c71099acSThomas Graf 	struct fib6_table *table;
18981da177e4SLinus Torvalds 
1899c346dca1SYOSHIFUJI Hideaki 	table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
190038308473SDavid S. Miller 	if (!table)
1901c71099acSThomas Graf 		return NULL;
19021da177e4SLinus Torvalds 
1903c71099acSThomas Graf 	write_lock_bh(&table->tb6_lock);
1904d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
1905d1918542SDavid S. Miller 		if (dev == rt->dst.dev &&
1906045927ffSYOSHIFUJI Hideaki 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
19071da177e4SLinus Torvalds 		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
19081da177e4SLinus Torvalds 			break;
19091da177e4SLinus Torvalds 	}
19101da177e4SLinus Torvalds 	if (rt)
1911d8d1f30bSChangli Gao 		dst_hold(&rt->dst);
1912c71099acSThomas Graf 	write_unlock_bh(&table->tb6_lock);
19131da177e4SLinus Torvalds 	return rt;
19141da177e4SLinus Torvalds }
19151da177e4SLinus Torvalds 
1916b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
1917ebacaaa0SYOSHIFUJI Hideaki 				     struct net_device *dev,
1918ebacaaa0SYOSHIFUJI Hideaki 				     unsigned int pref)
19191da177e4SLinus Torvalds {
192086872cb5SThomas Graf 	struct fib6_config cfg = {
192186872cb5SThomas Graf 		.fc_table	= RT6_TABLE_DFLT,
1922238fc7eaSRami Rosen 		.fc_metric	= IP6_RT_PRIO_USER,
192386872cb5SThomas Graf 		.fc_ifindex	= dev->ifindex,
192486872cb5SThomas Graf 		.fc_flags	= RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
192586872cb5SThomas Graf 				  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
19265578689aSDaniel Lezcano 		.fc_nlinfo.pid = 0,
19275578689aSDaniel Lezcano 		.fc_nlinfo.nlh = NULL,
1928c346dca1SYOSHIFUJI Hideaki 		.fc_nlinfo.nl_net = dev_net(dev),
192986872cb5SThomas Graf 	};
19301da177e4SLinus Torvalds 
19314e3fd7a0SAlexey Dobriyan 	cfg.fc_gateway = *gwaddr;
19321da177e4SLinus Torvalds 
193386872cb5SThomas Graf 	ip6_route_add(&cfg);
19341da177e4SLinus Torvalds 
19351da177e4SLinus Torvalds 	return rt6_get_dflt_router(gwaddr, dev);
19361da177e4SLinus Torvalds }
19371da177e4SLinus Torvalds 
19387b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net)
19391da177e4SLinus Torvalds {
19401da177e4SLinus Torvalds 	struct rt6_info *rt;
1941c71099acSThomas Graf 	struct fib6_table *table;
1942c71099acSThomas Graf 
1943c71099acSThomas Graf 	/* NOTE: Keep consistent with rt6_get_dflt_router */
19447b4da532SDaniel Lezcano 	table = fib6_get_table(net, RT6_TABLE_DFLT);
194538308473SDavid S. Miller 	if (!table)
1946c71099acSThomas Graf 		return;
19471da177e4SLinus Torvalds 
19481da177e4SLinus Torvalds restart:
1949c71099acSThomas Graf 	read_lock_bh(&table->tb6_lock);
1950d8d1f30bSChangli Gao 	for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
19511da177e4SLinus Torvalds 		if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
1952d8d1f30bSChangli Gao 			dst_hold(&rt->dst);
1953c71099acSThomas Graf 			read_unlock_bh(&table->tb6_lock);
1954e0a1ad73SThomas Graf 			ip6_del_rt(rt);
19551da177e4SLinus Torvalds 			goto restart;
19561da177e4SLinus Torvalds 		}
19571da177e4SLinus Torvalds 	}
1958c71099acSThomas Graf 	read_unlock_bh(&table->tb6_lock);
19591da177e4SLinus Torvalds }
19601da177e4SLinus Torvalds 
19615578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net,
19625578689aSDaniel Lezcano 				 struct in6_rtmsg *rtmsg,
196386872cb5SThomas Graf 				 struct fib6_config *cfg)
196486872cb5SThomas Graf {
196586872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
196686872cb5SThomas Graf 
196786872cb5SThomas Graf 	cfg->fc_table = RT6_TABLE_MAIN;
196886872cb5SThomas Graf 	cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
196986872cb5SThomas Graf 	cfg->fc_metric = rtmsg->rtmsg_metric;
197086872cb5SThomas Graf 	cfg->fc_expires = rtmsg->rtmsg_info;
197186872cb5SThomas Graf 	cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
197286872cb5SThomas Graf 	cfg->fc_src_len = rtmsg->rtmsg_src_len;
197386872cb5SThomas Graf 	cfg->fc_flags = rtmsg->rtmsg_flags;
197486872cb5SThomas Graf 
19755578689aSDaniel Lezcano 	cfg->fc_nlinfo.nl_net = net;
1976f1243c2dSBenjamin Thery 
19774e3fd7a0SAlexey Dobriyan 	cfg->fc_dst = rtmsg->rtmsg_dst;
19784e3fd7a0SAlexey Dobriyan 	cfg->fc_src = rtmsg->rtmsg_src;
19794e3fd7a0SAlexey Dobriyan 	cfg->fc_gateway = rtmsg->rtmsg_gateway;
198086872cb5SThomas Graf }
198186872cb5SThomas Graf 
19825578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
19831da177e4SLinus Torvalds {
198486872cb5SThomas Graf 	struct fib6_config cfg;
19851da177e4SLinus Torvalds 	struct in6_rtmsg rtmsg;
19861da177e4SLinus Torvalds 	int err;
19871da177e4SLinus Torvalds 
19881da177e4SLinus Torvalds 	switch(cmd) {
19891da177e4SLinus Torvalds 	case SIOCADDRT:		/* Add a route */
19901da177e4SLinus Torvalds 	case SIOCDELRT:		/* Delete a route */
19911da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
19921da177e4SLinus Torvalds 			return -EPERM;
19931da177e4SLinus Torvalds 		err = copy_from_user(&rtmsg, arg,
19941da177e4SLinus Torvalds 				     sizeof(struct in6_rtmsg));
19951da177e4SLinus Torvalds 		if (err)
19961da177e4SLinus Torvalds 			return -EFAULT;
19971da177e4SLinus Torvalds 
19985578689aSDaniel Lezcano 		rtmsg_to_fib6_config(net, &rtmsg, &cfg);
199986872cb5SThomas Graf 
20001da177e4SLinus Torvalds 		rtnl_lock();
20011da177e4SLinus Torvalds 		switch (cmd) {
20021da177e4SLinus Torvalds 		case SIOCADDRT:
200386872cb5SThomas Graf 			err = ip6_route_add(&cfg);
20041da177e4SLinus Torvalds 			break;
20051da177e4SLinus Torvalds 		case SIOCDELRT:
200686872cb5SThomas Graf 			err = ip6_route_del(&cfg);
20071da177e4SLinus Torvalds 			break;
20081da177e4SLinus Torvalds 		default:
20091da177e4SLinus Torvalds 			err = -EINVAL;
20101da177e4SLinus Torvalds 		}
20111da177e4SLinus Torvalds 		rtnl_unlock();
20121da177e4SLinus Torvalds 
20131da177e4SLinus Torvalds 		return err;
20143ff50b79SStephen Hemminger 	}
20151da177e4SLinus Torvalds 
20161da177e4SLinus Torvalds 	return -EINVAL;
20171da177e4SLinus Torvalds }
20181da177e4SLinus Torvalds 
20191da177e4SLinus Torvalds /*
20201da177e4SLinus Torvalds  *	Drop the packet on the floor
20211da177e4SLinus Torvalds  */
20221da177e4SLinus Torvalds 
2023d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
20241da177e4SLinus Torvalds {
2025612f09e8SYOSHIFUJI Hideaki 	int type;
2026adf30907SEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
2027612f09e8SYOSHIFUJI Hideaki 	switch (ipstats_mib_noroutes) {
2028612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_INNOROUTES:
20290660e03fSArnaldo Carvalho de Melo 		type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
203045bb0060SUlrich Weber 		if (type == IPV6_ADDR_ANY) {
20313bd653c8SDenis V. Lunev 			IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
20323bd653c8SDenis V. Lunev 				      IPSTATS_MIB_INADDRERRORS);
2033612f09e8SYOSHIFUJI Hideaki 			break;
2034612f09e8SYOSHIFUJI Hideaki 		}
2035612f09e8SYOSHIFUJI Hideaki 		/* FALLTHROUGH */
2036612f09e8SYOSHIFUJI Hideaki 	case IPSTATS_MIB_OUTNOROUTES:
20373bd653c8SDenis V. Lunev 		IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
20383bd653c8SDenis V. Lunev 			      ipstats_mib_noroutes);
2039612f09e8SYOSHIFUJI Hideaki 		break;
2040612f09e8SYOSHIFUJI Hideaki 	}
20413ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
20421da177e4SLinus Torvalds 	kfree_skb(skb);
20431da177e4SLinus Torvalds 	return 0;
20441da177e4SLinus Torvalds }
20451da177e4SLinus Torvalds 
20469ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb)
20479ce8ade0SThomas Graf {
2048612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
20499ce8ade0SThomas Graf }
20509ce8ade0SThomas Graf 
205120380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb)
20521da177e4SLinus Torvalds {
2053adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2054612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
20551da177e4SLinus Torvalds }
20561da177e4SLinus Torvalds 
20576723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES
20586723ab54SDavid S. Miller 
20599ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb)
20609ce8ade0SThomas Graf {
2061612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
20629ce8ade0SThomas Graf }
20639ce8ade0SThomas Graf 
20649ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb)
20659ce8ade0SThomas Graf {
2066adf30907SEric Dumazet 	skb->dev = skb_dst(skb)->dev;
2067612f09e8SYOSHIFUJI Hideaki 	return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
20689ce8ade0SThomas Graf }
20699ce8ade0SThomas Graf 
20706723ab54SDavid S. Miller #endif
20716723ab54SDavid S. Miller 
20721da177e4SLinus Torvalds /*
20731da177e4SLinus Torvalds  *	Allocate a dst for local (unicast / anycast) address.
20741da177e4SLinus Torvalds  */
20751da177e4SLinus Torvalds 
20761da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
20771da177e4SLinus Torvalds 				    const struct in6_addr *addr,
20788f031519SDavid S. Miller 				    bool anycast)
20791da177e4SLinus Torvalds {
2080c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(idev->dev);
20815c1e6aa3SDavid S. Miller 	struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
2082957c665fSDavid S. Miller 					    net->loopback_dev, 0);
2083f83c7790SDavid S. Miller 	int err;
20841da177e4SLinus Torvalds 
208538308473SDavid S. Miller 	if (!rt) {
208640385653SBen Greear 		if (net_ratelimit())
208740385653SBen Greear 			pr_warning("IPv6:  Maximum number of routes reached,"
208840385653SBen Greear 				   " consider increasing route/max_size.\n");
20891da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
209040385653SBen Greear 	}
20911da177e4SLinus Torvalds 
20921da177e4SLinus Torvalds 	in6_dev_hold(idev);
20931da177e4SLinus Torvalds 
209411d53b49SDavid S. Miller 	rt->dst.flags |= DST_HOST;
2095d8d1f30bSChangli Gao 	rt->dst.input = ip6_input;
2096d8d1f30bSChangli Gao 	rt->dst.output = ip6_output;
20971da177e4SLinus Torvalds 	rt->rt6i_idev = idev;
2098d8d1f30bSChangli Gao 	rt->dst.obsolete = -1;
20991da177e4SLinus Torvalds 
21001da177e4SLinus Torvalds 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
210158c4fb86SYOSHIFUJI Hideaki 	if (anycast)
210258c4fb86SYOSHIFUJI Hideaki 		rt->rt6i_flags |= RTF_ANYCAST;
210358c4fb86SYOSHIFUJI Hideaki 	else
21041da177e4SLinus Torvalds 		rt->rt6i_flags |= RTF_LOCAL;
21058ade06c6SDavid S. Miller 	err = rt6_bind_neighbour(rt, rt->dst.dev);
2106f83c7790SDavid S. Miller 	if (err) {
2107d8d1f30bSChangli Gao 		dst_free(&rt->dst);
2108f83c7790SDavid S. Miller 		return ERR_PTR(err);
21091da177e4SLinus Torvalds 	}
21101da177e4SLinus Torvalds 
21114e3fd7a0SAlexey Dobriyan 	rt->rt6i_dst.addr = *addr;
21121da177e4SLinus Torvalds 	rt->rt6i_dst.plen = 128;
21135578689aSDaniel Lezcano 	rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
21141da177e4SLinus Torvalds 
2115d8d1f30bSChangli Gao 	atomic_set(&rt->dst.__refcnt, 1);
21161da177e4SLinus Torvalds 
21171da177e4SLinus Torvalds 	return rt;
21181da177e4SLinus Torvalds }
21191da177e4SLinus Torvalds 
2120c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net,
2121c3968a85SDaniel Walter 			struct rt6_info *rt,
2122b71d1d42SEric Dumazet 			const struct in6_addr *daddr,
2123c3968a85SDaniel Walter 			unsigned int prefs,
2124c3968a85SDaniel Walter 			struct in6_addr *saddr)
2125c3968a85SDaniel Walter {
2126c3968a85SDaniel Walter 	struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2127c3968a85SDaniel Walter 	int err = 0;
2128c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen)
21294e3fd7a0SAlexey Dobriyan 		*saddr = rt->rt6i_prefsrc.addr;
2130c3968a85SDaniel Walter 	else
2131c3968a85SDaniel Walter 		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2132c3968a85SDaniel Walter 					 daddr, prefs, saddr);
2133c3968a85SDaniel Walter 	return err;
2134c3968a85SDaniel Walter }
2135c3968a85SDaniel Walter 
2136c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */
2137c3968a85SDaniel Walter struct arg_dev_net_ip {
2138c3968a85SDaniel Walter 	struct net_device *dev;
2139c3968a85SDaniel Walter 	struct net *net;
2140c3968a85SDaniel Walter 	struct in6_addr *addr;
2141c3968a85SDaniel Walter };
2142c3968a85SDaniel Walter 
2143c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2144c3968a85SDaniel Walter {
2145c3968a85SDaniel Walter 	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2146c3968a85SDaniel Walter 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2147c3968a85SDaniel Walter 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2148c3968a85SDaniel Walter 
2149d1918542SDavid S. Miller 	if (((void *)rt->dst.dev == dev || !dev) &&
2150c3968a85SDaniel Walter 	    rt != net->ipv6.ip6_null_entry &&
2151c3968a85SDaniel Walter 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2152c3968a85SDaniel Walter 		/* remove prefsrc entry */
2153c3968a85SDaniel Walter 		rt->rt6i_prefsrc.plen = 0;
2154c3968a85SDaniel Walter 	}
2155c3968a85SDaniel Walter 	return 0;
2156c3968a85SDaniel Walter }
2157c3968a85SDaniel Walter 
2158c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2159c3968a85SDaniel Walter {
2160c3968a85SDaniel Walter 	struct net *net = dev_net(ifp->idev->dev);
2161c3968a85SDaniel Walter 	struct arg_dev_net_ip adni = {
2162c3968a85SDaniel Walter 		.dev = ifp->idev->dev,
2163c3968a85SDaniel Walter 		.net = net,
2164c3968a85SDaniel Walter 		.addr = &ifp->addr,
2165c3968a85SDaniel Walter 	};
2166c3968a85SDaniel Walter 	fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2167c3968a85SDaniel Walter }
2168c3968a85SDaniel Walter 
21698ed67789SDaniel Lezcano struct arg_dev_net {
21708ed67789SDaniel Lezcano 	struct net_device *dev;
21718ed67789SDaniel Lezcano 	struct net *net;
21728ed67789SDaniel Lezcano };
21738ed67789SDaniel Lezcano 
21741da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg)
21751da177e4SLinus Torvalds {
2176bc3ef660Sstephen hemminger 	const struct arg_dev_net *adn = arg;
2177bc3ef660Sstephen hemminger 	const struct net_device *dev = adn->dev;
21788ed67789SDaniel Lezcano 
2179d1918542SDavid S. Miller 	if ((rt->dst.dev == dev || !dev) &&
2180c159d30cSDavid S. Miller 	    rt != adn->net->ipv6.ip6_null_entry)
21811da177e4SLinus Torvalds 		return -1;
2182c159d30cSDavid S. Miller 
21831da177e4SLinus Torvalds 	return 0;
21841da177e4SLinus Torvalds }
21851da177e4SLinus Torvalds 
2186f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev)
21871da177e4SLinus Torvalds {
21888ed67789SDaniel Lezcano 	struct arg_dev_net adn = {
21898ed67789SDaniel Lezcano 		.dev = dev,
21908ed67789SDaniel Lezcano 		.net = net,
21918ed67789SDaniel Lezcano 	};
21928ed67789SDaniel Lezcano 
21938ed67789SDaniel Lezcano 	fib6_clean_all(net, fib6_ifdown, 0, &adn);
21941e493d19SDavid S. Miller 	icmp6_clean_all(fib6_ifdown, &adn);
21951da177e4SLinus Torvalds }
21961da177e4SLinus Torvalds 
21971da177e4SLinus Torvalds struct rt6_mtu_change_arg
21981da177e4SLinus Torvalds {
21991da177e4SLinus Torvalds 	struct net_device *dev;
22001da177e4SLinus Torvalds 	unsigned mtu;
22011da177e4SLinus Torvalds };
22021da177e4SLinus Torvalds 
22031da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
22041da177e4SLinus Torvalds {
22051da177e4SLinus Torvalds 	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
22061da177e4SLinus Torvalds 	struct inet6_dev *idev;
22071da177e4SLinus Torvalds 
22081da177e4SLinus Torvalds 	/* In IPv6 pmtu discovery is not optional,
22091da177e4SLinus Torvalds 	   so that RTAX_MTU lock cannot disable it.
22101da177e4SLinus Torvalds 	   We still use this lock to block changes
22111da177e4SLinus Torvalds 	   caused by addrconf/ndisc.
22121da177e4SLinus Torvalds 	*/
22131da177e4SLinus Torvalds 
22141da177e4SLinus Torvalds 	idev = __in6_dev_get(arg->dev);
221538308473SDavid S. Miller 	if (!idev)
22161da177e4SLinus Torvalds 		return 0;
22171da177e4SLinus Torvalds 
22181da177e4SLinus Torvalds 	/* For administrative MTU increase, there is no way to discover
22191da177e4SLinus Torvalds 	   IPv6 PMTU increase, so PMTU increase should be updated here.
22201da177e4SLinus Torvalds 	   Since RFC 1981 doesn't include administrative MTU increase
22211da177e4SLinus Torvalds 	   update PMTU increase is a MUST. (i.e. jumbo frame)
22221da177e4SLinus Torvalds 	 */
22231da177e4SLinus Torvalds 	/*
22241da177e4SLinus Torvalds 	   If new MTU is less than route PMTU, this new MTU will be the
22251da177e4SLinus Torvalds 	   lowest MTU in the path, update the route PMTU to reflect PMTU
22261da177e4SLinus Torvalds 	   decreases; if new MTU is greater than route PMTU, and the
22271da177e4SLinus Torvalds 	   old MTU is the lowest MTU in the path, update the route PMTU
22281da177e4SLinus Torvalds 	   to reflect the increase. In this case if the other nodes' MTU
22291da177e4SLinus Torvalds 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
22301da177e4SLinus Torvalds 	   PMTU discouvery.
22311da177e4SLinus Torvalds 	 */
2232d1918542SDavid S. Miller 	if (rt->dst.dev == arg->dev &&
2233d8d1f30bSChangli Gao 	    !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2234d8d1f30bSChangli Gao 	    (dst_mtu(&rt->dst) >= arg->mtu ||
2235d8d1f30bSChangli Gao 	     (dst_mtu(&rt->dst) < arg->mtu &&
2236d8d1f30bSChangli Gao 	      dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
2237defb3519SDavid S. Miller 		dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
2238566cfd8fSSimon Arlott 	}
22391da177e4SLinus Torvalds 	return 0;
22401da177e4SLinus Torvalds }
22411da177e4SLinus Torvalds 
22421da177e4SLinus Torvalds void rt6_mtu_change(struct net_device *dev, unsigned mtu)
22431da177e4SLinus Torvalds {
2244c71099acSThomas Graf 	struct rt6_mtu_change_arg arg = {
2245c71099acSThomas Graf 		.dev = dev,
2246c71099acSThomas Graf 		.mtu = mtu,
2247c71099acSThomas Graf 	};
22481da177e4SLinus Torvalds 
2249c346dca1SYOSHIFUJI Hideaki 	fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
22501da177e4SLinus Torvalds }
22511da177e4SLinus Torvalds 
2252ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
22535176f91eSThomas Graf 	[RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
225486872cb5SThomas Graf 	[RTA_OIF]               = { .type = NLA_U32 },
2255ab364a6fSThomas Graf 	[RTA_IIF]		= { .type = NLA_U32 },
225686872cb5SThomas Graf 	[RTA_PRIORITY]          = { .type = NLA_U32 },
225786872cb5SThomas Graf 	[RTA_METRICS]           = { .type = NLA_NESTED },
225886872cb5SThomas Graf };
225986872cb5SThomas Graf 
226086872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
226186872cb5SThomas Graf 			      struct fib6_config *cfg)
22621da177e4SLinus Torvalds {
226386872cb5SThomas Graf 	struct rtmsg *rtm;
226486872cb5SThomas Graf 	struct nlattr *tb[RTA_MAX+1];
226586872cb5SThomas Graf 	int err;
22661da177e4SLinus Torvalds 
226786872cb5SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
226886872cb5SThomas Graf 	if (err < 0)
226986872cb5SThomas Graf 		goto errout;
22701da177e4SLinus Torvalds 
227186872cb5SThomas Graf 	err = -EINVAL;
227286872cb5SThomas Graf 	rtm = nlmsg_data(nlh);
227386872cb5SThomas Graf 	memset(cfg, 0, sizeof(*cfg));
227486872cb5SThomas Graf 
227586872cb5SThomas Graf 	cfg->fc_table = rtm->rtm_table;
227686872cb5SThomas Graf 	cfg->fc_dst_len = rtm->rtm_dst_len;
227786872cb5SThomas Graf 	cfg->fc_src_len = rtm->rtm_src_len;
227886872cb5SThomas Graf 	cfg->fc_flags = RTF_UP;
227986872cb5SThomas Graf 	cfg->fc_protocol = rtm->rtm_protocol;
228086872cb5SThomas Graf 
228186872cb5SThomas Graf 	if (rtm->rtm_type == RTN_UNREACHABLE)
228286872cb5SThomas Graf 		cfg->fc_flags |= RTF_REJECT;
228386872cb5SThomas Graf 
2284ab79ad14SMaciej Żenczykowski 	if (rtm->rtm_type == RTN_LOCAL)
2285ab79ad14SMaciej Żenczykowski 		cfg->fc_flags |= RTF_LOCAL;
2286ab79ad14SMaciej Żenczykowski 
228786872cb5SThomas Graf 	cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
228886872cb5SThomas Graf 	cfg->fc_nlinfo.nlh = nlh;
22893b1e0a65SYOSHIFUJI Hideaki 	cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
229086872cb5SThomas Graf 
229186872cb5SThomas Graf 	if (tb[RTA_GATEWAY]) {
229286872cb5SThomas Graf 		nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
229386872cb5SThomas Graf 		cfg->fc_flags |= RTF_GATEWAY;
22941da177e4SLinus Torvalds 	}
229586872cb5SThomas Graf 
229686872cb5SThomas Graf 	if (tb[RTA_DST]) {
229786872cb5SThomas Graf 		int plen = (rtm->rtm_dst_len + 7) >> 3;
229886872cb5SThomas Graf 
229986872cb5SThomas Graf 		if (nla_len(tb[RTA_DST]) < plen)
230086872cb5SThomas Graf 			goto errout;
230186872cb5SThomas Graf 
230286872cb5SThomas Graf 		nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
23031da177e4SLinus Torvalds 	}
230486872cb5SThomas Graf 
230586872cb5SThomas Graf 	if (tb[RTA_SRC]) {
230686872cb5SThomas Graf 		int plen = (rtm->rtm_src_len + 7) >> 3;
230786872cb5SThomas Graf 
230886872cb5SThomas Graf 		if (nla_len(tb[RTA_SRC]) < plen)
230986872cb5SThomas Graf 			goto errout;
231086872cb5SThomas Graf 
231186872cb5SThomas Graf 		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
23121da177e4SLinus Torvalds 	}
231386872cb5SThomas Graf 
2314c3968a85SDaniel Walter 	if (tb[RTA_PREFSRC])
2315c3968a85SDaniel Walter 		nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2316c3968a85SDaniel Walter 
231786872cb5SThomas Graf 	if (tb[RTA_OIF])
231886872cb5SThomas Graf 		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
231986872cb5SThomas Graf 
232086872cb5SThomas Graf 	if (tb[RTA_PRIORITY])
232186872cb5SThomas Graf 		cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
232286872cb5SThomas Graf 
232386872cb5SThomas Graf 	if (tb[RTA_METRICS]) {
232486872cb5SThomas Graf 		cfg->fc_mx = nla_data(tb[RTA_METRICS]);
232586872cb5SThomas Graf 		cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
23261da177e4SLinus Torvalds 	}
232786872cb5SThomas Graf 
232886872cb5SThomas Graf 	if (tb[RTA_TABLE])
232986872cb5SThomas Graf 		cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
233086872cb5SThomas Graf 
233186872cb5SThomas Graf 	err = 0;
233286872cb5SThomas Graf errout:
233386872cb5SThomas Graf 	return err;
23341da177e4SLinus Torvalds }
23351da177e4SLinus Torvalds 
2336c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
23371da177e4SLinus Torvalds {
233886872cb5SThomas Graf 	struct fib6_config cfg;
233986872cb5SThomas Graf 	int err;
23401da177e4SLinus Torvalds 
234186872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
234286872cb5SThomas Graf 	if (err < 0)
234386872cb5SThomas Graf 		return err;
234486872cb5SThomas Graf 
234586872cb5SThomas Graf 	return ip6_route_del(&cfg);
23461da177e4SLinus Torvalds }
23471da177e4SLinus Torvalds 
2348c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
23491da177e4SLinus Torvalds {
235086872cb5SThomas Graf 	struct fib6_config cfg;
235186872cb5SThomas Graf 	int err;
23521da177e4SLinus Torvalds 
235386872cb5SThomas Graf 	err = rtm_to_fib6_config(skb, nlh, &cfg);
235486872cb5SThomas Graf 	if (err < 0)
235586872cb5SThomas Graf 		return err;
235686872cb5SThomas Graf 
235786872cb5SThomas Graf 	return ip6_route_add(&cfg);
23581da177e4SLinus Torvalds }
23591da177e4SLinus Torvalds 
2360339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void)
2361339bf98fSThomas Graf {
2362339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct rtmsg))
2363339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_SRC */
2364339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_DST */
2365339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_GATEWAY */
2366339bf98fSThomas Graf 	       + nla_total_size(16) /* RTA_PREFSRC */
2367339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_TABLE */
2368339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_IIF */
2369339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_OIF */
2370339bf98fSThomas Graf 	       + nla_total_size(4) /* RTA_PRIORITY */
23716a2b9ce0SNoriaki TAKAMIYA 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
2372339bf98fSThomas Graf 	       + nla_total_size(sizeof(struct rta_cacheinfo));
2373339bf98fSThomas Graf }
2374339bf98fSThomas Graf 
2375191cd582SBrian Haley static int rt6_fill_node(struct net *net,
2376191cd582SBrian Haley 			 struct sk_buff *skb, struct rt6_info *rt,
23770d51aa80SJamal Hadi Salim 			 struct in6_addr *dst, struct in6_addr *src,
23780d51aa80SJamal Hadi Salim 			 int iif, int type, u32 pid, u32 seq,
23797bc570c8SYOSHIFUJI Hideaki 			 int prefix, int nowait, unsigned int flags)
23801da177e4SLinus Torvalds {
2381346f870bSDavid S. Miller 	const struct inet_peer *peer;
23821da177e4SLinus Torvalds 	struct rtmsg *rtm;
23831da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
2384e3703b3dSThomas Graf 	long expires;
23859e762a4aSPatrick McHardy 	u32 table;
2386f2c31e32SEric Dumazet 	struct neighbour *n;
2387346f870bSDavid S. Miller 	u32 ts, tsage;
23881da177e4SLinus Torvalds 
23891da177e4SLinus Torvalds 	if (prefix) {	/* user wants prefix routes only */
23901da177e4SLinus Torvalds 		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
23911da177e4SLinus Torvalds 			/* success since this is not a prefix route */
23921da177e4SLinus Torvalds 			return 1;
23931da177e4SLinus Torvalds 		}
23941da177e4SLinus Torvalds 	}
23951da177e4SLinus Torvalds 
23962d7202bfSThomas Graf 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
239738308473SDavid S. Miller 	if (!nlh)
239826932566SPatrick McHardy 		return -EMSGSIZE;
23992d7202bfSThomas Graf 
24002d7202bfSThomas Graf 	rtm = nlmsg_data(nlh);
24011da177e4SLinus Torvalds 	rtm->rtm_family = AF_INET6;
24021da177e4SLinus Torvalds 	rtm->rtm_dst_len = rt->rt6i_dst.plen;
24031da177e4SLinus Torvalds 	rtm->rtm_src_len = rt->rt6i_src.plen;
24041da177e4SLinus Torvalds 	rtm->rtm_tos = 0;
2405c71099acSThomas Graf 	if (rt->rt6i_table)
24069e762a4aSPatrick McHardy 		table = rt->rt6i_table->tb6_id;
2407c71099acSThomas Graf 	else
24089e762a4aSPatrick McHardy 		table = RT6_TABLE_UNSPEC;
24099e762a4aSPatrick McHardy 	rtm->rtm_table = table;
24102d7202bfSThomas Graf 	NLA_PUT_U32(skb, RTA_TABLE, table);
24111da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_REJECT)
24121da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNREACHABLE;
2413ab79ad14SMaciej Żenczykowski 	else if (rt->rt6i_flags & RTF_LOCAL)
2414ab79ad14SMaciej Żenczykowski 		rtm->rtm_type = RTN_LOCAL;
2415d1918542SDavid S. Miller 	else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
24161da177e4SLinus Torvalds 		rtm->rtm_type = RTN_LOCAL;
24171da177e4SLinus Torvalds 	else
24181da177e4SLinus Torvalds 		rtm->rtm_type = RTN_UNICAST;
24191da177e4SLinus Torvalds 	rtm->rtm_flags = 0;
24201da177e4SLinus Torvalds 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
24211da177e4SLinus Torvalds 	rtm->rtm_protocol = rt->rt6i_protocol;
24221da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_DYNAMIC)
24231da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_REDIRECT;
24241da177e4SLinus Torvalds 	else if (rt->rt6i_flags & RTF_ADDRCONF)
24251da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_KERNEL;
24261da177e4SLinus Torvalds 	else if (rt->rt6i_flags & RTF_DEFAULT)
24271da177e4SLinus Torvalds 		rtm->rtm_protocol = RTPROT_RA;
24281da177e4SLinus Torvalds 
24291da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_CACHE)
24301da177e4SLinus Torvalds 		rtm->rtm_flags |= RTM_F_CLONED;
24311da177e4SLinus Torvalds 
24321da177e4SLinus Torvalds 	if (dst) {
24332d7202bfSThomas Graf 		NLA_PUT(skb, RTA_DST, 16, dst);
24341da177e4SLinus Torvalds 		rtm->rtm_dst_len = 128;
24351da177e4SLinus Torvalds 	} else if (rtm->rtm_dst_len)
24362d7202bfSThomas Graf 		NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
24371da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
24381da177e4SLinus Torvalds 	if (src) {
24392d7202bfSThomas Graf 		NLA_PUT(skb, RTA_SRC, 16, src);
24401da177e4SLinus Torvalds 		rtm->rtm_src_len = 128;
24411da177e4SLinus Torvalds 	} else if (rtm->rtm_src_len)
24422d7202bfSThomas Graf 		NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
24431da177e4SLinus Torvalds #endif
24447bc570c8SYOSHIFUJI Hideaki 	if (iif) {
24457bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE
24467bc570c8SYOSHIFUJI Hideaki 		if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
24478229efdaSBenjamin Thery 			int err = ip6mr_get_route(net, skb, rtm, nowait);
24487bc570c8SYOSHIFUJI Hideaki 			if (err <= 0) {
24497bc570c8SYOSHIFUJI Hideaki 				if (!nowait) {
24507bc570c8SYOSHIFUJI Hideaki 					if (err == 0)
24517bc570c8SYOSHIFUJI Hideaki 						return 0;
24527bc570c8SYOSHIFUJI Hideaki 					goto nla_put_failure;
24537bc570c8SYOSHIFUJI Hideaki 				} else {
24547bc570c8SYOSHIFUJI Hideaki 					if (err == -EMSGSIZE)
24557bc570c8SYOSHIFUJI Hideaki 						goto nla_put_failure;
24567bc570c8SYOSHIFUJI Hideaki 				}
24577bc570c8SYOSHIFUJI Hideaki 			}
24587bc570c8SYOSHIFUJI Hideaki 		} else
24597bc570c8SYOSHIFUJI Hideaki #endif
24602d7202bfSThomas Graf 			NLA_PUT_U32(skb, RTA_IIF, iif);
24617bc570c8SYOSHIFUJI Hideaki 	} else if (dst) {
24621da177e4SLinus Torvalds 		struct in6_addr saddr_buf;
2463c3968a85SDaniel Walter 		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0)
2464c3968a85SDaniel Walter 			NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
2465c3968a85SDaniel Walter 	}
2466c3968a85SDaniel Walter 
2467c3968a85SDaniel Walter 	if (rt->rt6i_prefsrc.plen) {
2468c3968a85SDaniel Walter 		struct in6_addr saddr_buf;
24694e3fd7a0SAlexey Dobriyan 		saddr_buf = rt->rt6i_prefsrc.addr;
24702d7202bfSThomas Graf 		NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
24711da177e4SLinus Torvalds 	}
24722d7202bfSThomas Graf 
2473defb3519SDavid S. Miller 	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
24742d7202bfSThomas Graf 		goto nla_put_failure;
24752d7202bfSThomas Graf 
2476f2c31e32SEric Dumazet 	rcu_read_lock();
247727217455SDavid Miller 	n = dst_get_neighbour_noref(&rt->dst);
2478f2c31e32SEric Dumazet 	if (n)
2479f2c31e32SEric Dumazet 		NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
2480f2c31e32SEric Dumazet 	rcu_read_unlock();
24812d7202bfSThomas Graf 
2482d8d1f30bSChangli Gao 	if (rt->dst.dev)
2483d1918542SDavid S. Miller 		NLA_PUT_U32(skb, RTA_OIF, rt->dst.dev->ifindex);
24842d7202bfSThomas Graf 
24852d7202bfSThomas Graf 	NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
2486e3703b3dSThomas Graf 
248736e3deaeSYOSHIFUJI Hideaki 	if (!(rt->rt6i_flags & RTF_EXPIRES))
248836e3deaeSYOSHIFUJI Hideaki 		expires = 0;
2489d1918542SDavid S. Miller 	else if (rt->dst.expires - jiffies < INT_MAX)
2490d1918542SDavid S. Miller 		expires = rt->dst.expires - jiffies;
249136e3deaeSYOSHIFUJI Hideaki 	else
249236e3deaeSYOSHIFUJI Hideaki 		expires = INT_MAX;
249369cdf8f9SYOSHIFUJI Hideaki 
2494346f870bSDavid S. Miller 	peer = rt->rt6i_peer;
2495346f870bSDavid S. Miller 	ts = tsage = 0;
2496346f870bSDavid S. Miller 	if (peer && peer->tcp_ts_stamp) {
2497346f870bSDavid S. Miller 		ts = peer->tcp_ts;
2498346f870bSDavid S. Miller 		tsage = get_seconds() - peer->tcp_ts_stamp;
2499346f870bSDavid S. Miller 	}
2500346f870bSDavid S. Miller 
2501346f870bSDavid S. Miller 	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, ts, tsage,
2502d8d1f30bSChangli Gao 			       expires, rt->dst.error) < 0)
2503e3703b3dSThomas Graf 		goto nla_put_failure;
25041da177e4SLinus Torvalds 
25052d7202bfSThomas Graf 	return nlmsg_end(skb, nlh);
25062d7202bfSThomas Graf 
25072d7202bfSThomas Graf nla_put_failure:
250826932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
250926932566SPatrick McHardy 	return -EMSGSIZE;
25101da177e4SLinus Torvalds }
25111da177e4SLinus Torvalds 
25121b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg)
25131da177e4SLinus Torvalds {
25141da177e4SLinus Torvalds 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
25151da177e4SLinus Torvalds 	int prefix;
25161da177e4SLinus Torvalds 
25172d7202bfSThomas Graf 	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
25182d7202bfSThomas Graf 		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
25191da177e4SLinus Torvalds 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
25201da177e4SLinus Torvalds 	} else
25211da177e4SLinus Torvalds 		prefix = 0;
25221da177e4SLinus Torvalds 
2523191cd582SBrian Haley 	return rt6_fill_node(arg->net,
2524191cd582SBrian Haley 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
25251da177e4SLinus Torvalds 		     NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
25267bc570c8SYOSHIFUJI Hideaki 		     prefix, 0, NLM_F_MULTI);
25271da177e4SLinus Torvalds }
25281da177e4SLinus Torvalds 
2529c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
25301da177e4SLinus Torvalds {
25313b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(in_skb->sk);
2532ab364a6fSThomas Graf 	struct nlattr *tb[RTA_MAX+1];
25331da177e4SLinus Torvalds 	struct rt6_info *rt;
2534ab364a6fSThomas Graf 	struct sk_buff *skb;
2535ab364a6fSThomas Graf 	struct rtmsg *rtm;
25364c9483b2SDavid S. Miller 	struct flowi6 fl6;
2537ab364a6fSThomas Graf 	int err, iif = 0;
2538ab364a6fSThomas Graf 
2539ab364a6fSThomas Graf 	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2540ab364a6fSThomas Graf 	if (err < 0)
2541ab364a6fSThomas Graf 		goto errout;
2542ab364a6fSThomas Graf 
2543ab364a6fSThomas Graf 	err = -EINVAL;
25444c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2545ab364a6fSThomas Graf 
2546ab364a6fSThomas Graf 	if (tb[RTA_SRC]) {
2547ab364a6fSThomas Graf 		if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2548ab364a6fSThomas Graf 			goto errout;
2549ab364a6fSThomas Graf 
25504e3fd7a0SAlexey Dobriyan 		fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
2551ab364a6fSThomas Graf 	}
2552ab364a6fSThomas Graf 
2553ab364a6fSThomas Graf 	if (tb[RTA_DST]) {
2554ab364a6fSThomas Graf 		if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2555ab364a6fSThomas Graf 			goto errout;
2556ab364a6fSThomas Graf 
25574e3fd7a0SAlexey Dobriyan 		fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
2558ab364a6fSThomas Graf 	}
2559ab364a6fSThomas Graf 
2560ab364a6fSThomas Graf 	if (tb[RTA_IIF])
2561ab364a6fSThomas Graf 		iif = nla_get_u32(tb[RTA_IIF]);
2562ab364a6fSThomas Graf 
2563ab364a6fSThomas Graf 	if (tb[RTA_OIF])
25644c9483b2SDavid S. Miller 		fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]);
2565ab364a6fSThomas Graf 
2566ab364a6fSThomas Graf 	if (iif) {
2567ab364a6fSThomas Graf 		struct net_device *dev;
25685578689aSDaniel Lezcano 		dev = __dev_get_by_index(net, iif);
2569ab364a6fSThomas Graf 		if (!dev) {
2570ab364a6fSThomas Graf 			err = -ENODEV;
2571ab364a6fSThomas Graf 			goto errout;
2572ab364a6fSThomas Graf 		}
2573ab364a6fSThomas Graf 	}
25741da177e4SLinus Torvalds 
25751da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
257638308473SDavid S. Miller 	if (!skb) {
2577ab364a6fSThomas Graf 		err = -ENOBUFS;
2578ab364a6fSThomas Graf 		goto errout;
2579ab364a6fSThomas Graf 	}
25801da177e4SLinus Torvalds 
25811da177e4SLinus Torvalds 	/* Reserve room for dummy headers, this skb can pass
25821da177e4SLinus Torvalds 	   through good chunk of routing engine.
25831da177e4SLinus Torvalds 	 */
2584459a98edSArnaldo Carvalho de Melo 	skb_reset_mac_header(skb);
25851da177e4SLinus Torvalds 	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
25861da177e4SLinus Torvalds 
25874c9483b2SDavid S. Miller 	rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6);
2588d8d1f30bSChangli Gao 	skb_dst_set(skb, &rt->dst);
25891da177e4SLinus Torvalds 
25904c9483b2SDavid S. Miller 	err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
25911da177e4SLinus Torvalds 			    RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
25927bc570c8SYOSHIFUJI Hideaki 			    nlh->nlmsg_seq, 0, 0, 0);
25931da177e4SLinus Torvalds 	if (err < 0) {
2594ab364a6fSThomas Graf 		kfree_skb(skb);
2595ab364a6fSThomas Graf 		goto errout;
25961da177e4SLinus Torvalds 	}
25971da177e4SLinus Torvalds 
25985578689aSDaniel Lezcano 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
2599ab364a6fSThomas Graf errout:
26001da177e4SLinus Torvalds 	return err;
26011da177e4SLinus Torvalds }
26021da177e4SLinus Torvalds 
260386872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
26041da177e4SLinus Torvalds {
26051da177e4SLinus Torvalds 	struct sk_buff *skb;
26065578689aSDaniel Lezcano 	struct net *net = info->nl_net;
2607528c4cebSDenis V. Lunev 	u32 seq;
2608528c4cebSDenis V. Lunev 	int err;
26090d51aa80SJamal Hadi Salim 
2610528c4cebSDenis V. Lunev 	err = -ENOBUFS;
261138308473SDavid S. Miller 	seq = info->nlh ? info->nlh->nlmsg_seq : 0;
261286872cb5SThomas Graf 
2613339bf98fSThomas Graf 	skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
261438308473SDavid S. Miller 	if (!skb)
261521713ebcSThomas Graf 		goto errout;
26161da177e4SLinus Torvalds 
2617191cd582SBrian Haley 	err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
26187bc570c8SYOSHIFUJI Hideaki 				event, info->pid, seq, 0, 0, 0);
261926932566SPatrick McHardy 	if (err < 0) {
262026932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
262126932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
262226932566SPatrick McHardy 		kfree_skb(skb);
262326932566SPatrick McHardy 		goto errout;
262426932566SPatrick McHardy 	}
26251ce85fe4SPablo Neira Ayuso 	rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
26265578689aSDaniel Lezcano 		    info->nlh, gfp_any());
26271ce85fe4SPablo Neira Ayuso 	return;
262821713ebcSThomas Graf errout:
262921713ebcSThomas Graf 	if (err < 0)
26305578689aSDaniel Lezcano 		rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
26311da177e4SLinus Torvalds }
26321da177e4SLinus Torvalds 
26338ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this,
26348ed67789SDaniel Lezcano 				unsigned long event, void *data)
26358ed67789SDaniel Lezcano {
26368ed67789SDaniel Lezcano 	struct net_device *dev = (struct net_device *)data;
2637c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
26388ed67789SDaniel Lezcano 
26398ed67789SDaniel Lezcano 	if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
2640d8d1f30bSChangli Gao 		net->ipv6.ip6_null_entry->dst.dev = dev;
26418ed67789SDaniel Lezcano 		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
26428ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2643d8d1f30bSChangli Gao 		net->ipv6.ip6_prohibit_entry->dst.dev = dev;
26448ed67789SDaniel Lezcano 		net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
2645d8d1f30bSChangli Gao 		net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
26468ed67789SDaniel Lezcano 		net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
26478ed67789SDaniel Lezcano #endif
26488ed67789SDaniel Lezcano 	}
26498ed67789SDaniel Lezcano 
26508ed67789SDaniel Lezcano 	return NOTIFY_OK;
26518ed67789SDaniel Lezcano }
26528ed67789SDaniel Lezcano 
26531da177e4SLinus Torvalds /*
26541da177e4SLinus Torvalds  *	/proc
26551da177e4SLinus Torvalds  */
26561da177e4SLinus Torvalds 
26571da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
26581da177e4SLinus Torvalds 
26591da177e4SLinus Torvalds struct rt6_proc_arg
26601da177e4SLinus Torvalds {
26611da177e4SLinus Torvalds 	char *buffer;
26621da177e4SLinus Torvalds 	int offset;
26631da177e4SLinus Torvalds 	int length;
26641da177e4SLinus Torvalds 	int skip;
26651da177e4SLinus Torvalds 	int len;
26661da177e4SLinus Torvalds };
26671da177e4SLinus Torvalds 
26681da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg)
26691da177e4SLinus Torvalds {
267033120b30SAlexey Dobriyan 	struct seq_file *m = p_arg;
267169cce1d1SDavid S. Miller 	struct neighbour *n;
26721da177e4SLinus Torvalds 
26734b7a4274SHarvey Harrison 	seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
26741da177e4SLinus Torvalds 
26751da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES
26764b7a4274SHarvey Harrison 	seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
26771da177e4SLinus Torvalds #else
267833120b30SAlexey Dobriyan 	seq_puts(m, "00000000000000000000000000000000 00 ");
26791da177e4SLinus Torvalds #endif
2680f2c31e32SEric Dumazet 	rcu_read_lock();
268127217455SDavid Miller 	n = dst_get_neighbour_noref(&rt->dst);
268269cce1d1SDavid S. Miller 	if (n) {
268369cce1d1SDavid S. Miller 		seq_printf(m, "%pi6", n->primary_key);
26841da177e4SLinus Torvalds 	} else {
268533120b30SAlexey Dobriyan 		seq_puts(m, "00000000000000000000000000000000");
26861da177e4SLinus Torvalds 	}
2687f2c31e32SEric Dumazet 	rcu_read_unlock();
268833120b30SAlexey Dobriyan 	seq_printf(m, " %08x %08x %08x %08x %8s\n",
2689d8d1f30bSChangli Gao 		   rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2690d8d1f30bSChangli Gao 		   rt->dst.__use, rt->rt6i_flags,
2691d1918542SDavid S. Miller 		   rt->dst.dev ? rt->dst.dev->name : "");
26921da177e4SLinus Torvalds 	return 0;
26931da177e4SLinus Torvalds }
26941da177e4SLinus Torvalds 
269533120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v)
26961da177e4SLinus Torvalds {
2697f3db4851SDaniel Lezcano 	struct net *net = (struct net *)m->private;
269832b293a5SJosh Hunt 	fib6_clean_all_ro(net, rt6_info_route, 0, m);
269933120b30SAlexey Dobriyan 	return 0;
27001da177e4SLinus Torvalds }
27011da177e4SLinus Torvalds 
270233120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file)
270333120b30SAlexey Dobriyan {
2704de05c557SPavel Emelyanov 	return single_open_net(inode, file, ipv6_route_show);
2705f3db4851SDaniel Lezcano }
2706f3db4851SDaniel Lezcano 
270733120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = {
270833120b30SAlexey Dobriyan 	.owner		= THIS_MODULE,
270933120b30SAlexey Dobriyan 	.open		= ipv6_route_open,
271033120b30SAlexey Dobriyan 	.read		= seq_read,
271133120b30SAlexey Dobriyan 	.llseek		= seq_lseek,
2712b6fcbdb4SPavel Emelyanov 	.release	= single_release_net,
271333120b30SAlexey Dobriyan };
271433120b30SAlexey Dobriyan 
27151da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v)
27161da177e4SLinus Torvalds {
271769ddb805SDaniel Lezcano 	struct net *net = (struct net *)seq->private;
27181da177e4SLinus Torvalds 	seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
271969ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_nodes,
272069ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_route_nodes,
272169ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_alloc,
272269ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_entries,
272369ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_rt_cache,
2724fc66f95cSEric Dumazet 		   dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
272569ddb805SDaniel Lezcano 		   net->ipv6.rt6_stats->fib_discarded_routes);
27261da177e4SLinus Torvalds 
27271da177e4SLinus Torvalds 	return 0;
27281da177e4SLinus Torvalds }
27291da177e4SLinus Torvalds 
27301da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file)
27311da177e4SLinus Torvalds {
2732de05c557SPavel Emelyanov 	return single_open_net(inode, file, rt6_stats_seq_show);
273369ddb805SDaniel Lezcano }
273469ddb805SDaniel Lezcano 
27359a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = {
27361da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
27371da177e4SLinus Torvalds 	.open	 = rt6_stats_seq_open,
27381da177e4SLinus Torvalds 	.read	 = seq_read,
27391da177e4SLinus Torvalds 	.llseek	 = seq_lseek,
2740b6fcbdb4SPavel Emelyanov 	.release = single_release_net,
27411da177e4SLinus Torvalds };
27421da177e4SLinus Torvalds #endif	/* CONFIG_PROC_FS */
27431da177e4SLinus Torvalds 
27441da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
27451da177e4SLinus Torvalds 
27461da177e4SLinus Torvalds static
27478d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
27481da177e4SLinus Torvalds 			      void __user *buffer, size_t *lenp, loff_t *ppos)
27491da177e4SLinus Torvalds {
2750c486da34SLucian Adrian Grijincu 	struct net *net;
2751c486da34SLucian Adrian Grijincu 	int delay;
2752c486da34SLucian Adrian Grijincu 	if (!write)
2753c486da34SLucian Adrian Grijincu 		return -EINVAL;
2754c486da34SLucian Adrian Grijincu 
2755c486da34SLucian Adrian Grijincu 	net = (struct net *)ctl->extra1;
2756c486da34SLucian Adrian Grijincu 	delay = net->ipv6.sysctl.flush_delay;
27578d65af78SAlexey Dobriyan 	proc_dointvec(ctl, write, buffer, lenp, ppos);
27585b7c931dSDaniel Lezcano 	fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
27591da177e4SLinus Torvalds 	return 0;
27601da177e4SLinus Torvalds }
27611da177e4SLinus Torvalds 
2762760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = {
27631da177e4SLinus Torvalds 	{
27641da177e4SLinus Torvalds 		.procname	=	"flush",
27654990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.flush_delay,
27661da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
276789c8b3a1SDave Jones 		.mode		=	0200,
27686d9f239aSAlexey Dobriyan 		.proc_handler	=	ipv6_sysctl_rtcache_flush
27691da177e4SLinus Torvalds 	},
27701da177e4SLinus Torvalds 	{
27711da177e4SLinus Torvalds 		.procname	=	"gc_thresh",
27729a7ec3a9SDaniel Lezcano 		.data		=	&ip6_dst_ops_template.gc_thresh,
27731da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27741da177e4SLinus Torvalds 		.mode		=	0644,
27756d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
27761da177e4SLinus Torvalds 	},
27771da177e4SLinus Torvalds 	{
27781da177e4SLinus Torvalds 		.procname	=	"max_size",
27794990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
27801da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27811da177e4SLinus Torvalds 		.mode		=	0644,
27826d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec,
27831da177e4SLinus Torvalds 	},
27841da177e4SLinus Torvalds 	{
27851da177e4SLinus Torvalds 		.procname	=	"gc_min_interval",
27864990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
27871da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27881da177e4SLinus Torvalds 		.mode		=	0644,
27896d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
27901da177e4SLinus Torvalds 	},
27911da177e4SLinus Torvalds 	{
27921da177e4SLinus Torvalds 		.procname	=	"gc_timeout",
27934990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_timeout,
27941da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
27951da177e4SLinus Torvalds 		.mode		=	0644,
27966d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
27971da177e4SLinus Torvalds 	},
27981da177e4SLinus Torvalds 	{
27991da177e4SLinus Torvalds 		.procname	=	"gc_interval",
28004990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_interval,
28011da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28021da177e4SLinus Torvalds 		.mode		=	0644,
28036d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28041da177e4SLinus Torvalds 	},
28051da177e4SLinus Torvalds 	{
28061da177e4SLinus Torvalds 		.procname	=	"gc_elasticity",
28074990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
28081da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28091da177e4SLinus Torvalds 		.mode		=	0644,
2810f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
28111da177e4SLinus Torvalds 	},
28121da177e4SLinus Torvalds 	{
28131da177e4SLinus Torvalds 		.procname	=	"mtu_expires",
28144990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_mtu_expires,
28151da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28161da177e4SLinus Torvalds 		.mode		=	0644,
28176d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_jiffies,
28181da177e4SLinus Torvalds 	},
28191da177e4SLinus Torvalds 	{
28201da177e4SLinus Torvalds 		.procname	=	"min_adv_mss",
28214990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_min_advmss,
28221da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28231da177e4SLinus Torvalds 		.mode		=	0644,
2824f3d3f616SMin Zhang 		.proc_handler	=	proc_dointvec,
28251da177e4SLinus Torvalds 	},
28261da177e4SLinus Torvalds 	{
28271da177e4SLinus Torvalds 		.procname	=	"gc_min_interval_ms",
28284990509fSDaniel Lezcano 		.data		=	&init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
28291da177e4SLinus Torvalds 		.maxlen		=	sizeof(int),
28301da177e4SLinus Torvalds 		.mode		=	0644,
28316d9f239aSAlexey Dobriyan 		.proc_handler	=	proc_dointvec_ms_jiffies,
28321da177e4SLinus Torvalds 	},
2833f8572d8fSEric W. Biederman 	{ }
28341da177e4SLinus Torvalds };
28351da177e4SLinus Torvalds 
28362c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
2837760f2d01SDaniel Lezcano {
2838760f2d01SDaniel Lezcano 	struct ctl_table *table;
2839760f2d01SDaniel Lezcano 
2840760f2d01SDaniel Lezcano 	table = kmemdup(ipv6_route_table_template,
2841760f2d01SDaniel Lezcano 			sizeof(ipv6_route_table_template),
2842760f2d01SDaniel Lezcano 			GFP_KERNEL);
28435ee09105SYOSHIFUJI Hideaki 
28445ee09105SYOSHIFUJI Hideaki 	if (table) {
28455ee09105SYOSHIFUJI Hideaki 		table[0].data = &net->ipv6.sysctl.flush_delay;
2846c486da34SLucian Adrian Grijincu 		table[0].extra1 = net;
284786393e52SAlexey Dobriyan 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
28485ee09105SYOSHIFUJI Hideaki 		table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
28495ee09105SYOSHIFUJI Hideaki 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
28505ee09105SYOSHIFUJI Hideaki 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
28515ee09105SYOSHIFUJI Hideaki 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
28525ee09105SYOSHIFUJI Hideaki 		table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
28535ee09105SYOSHIFUJI Hideaki 		table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
28545ee09105SYOSHIFUJI Hideaki 		table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
28559c69fabeSAlexey Dobriyan 		table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
28565ee09105SYOSHIFUJI Hideaki 	}
28575ee09105SYOSHIFUJI Hideaki 
2858760f2d01SDaniel Lezcano 	return table;
2859760f2d01SDaniel Lezcano }
28601da177e4SLinus Torvalds #endif
28611da177e4SLinus Torvalds 
28622c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net)
2863cdb18761SDaniel Lezcano {
2864633d424bSPavel Emelyanov 	int ret = -ENOMEM;
28658ed67789SDaniel Lezcano 
286686393e52SAlexey Dobriyan 	memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
286786393e52SAlexey Dobriyan 	       sizeof(net->ipv6.ip6_dst_ops));
2868f2fc6a54SBenjamin Thery 
2869fc66f95cSEric Dumazet 	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2870fc66f95cSEric Dumazet 		goto out_ip6_dst_ops;
2871fc66f95cSEric Dumazet 
28728ed67789SDaniel Lezcano 	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
28738ed67789SDaniel Lezcano 					   sizeof(*net->ipv6.ip6_null_entry),
28748ed67789SDaniel Lezcano 					   GFP_KERNEL);
28758ed67789SDaniel Lezcano 	if (!net->ipv6.ip6_null_entry)
2876fc66f95cSEric Dumazet 		goto out_ip6_dst_entries;
2877d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.path =
28788ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_null_entry;
2879d8d1f30bSChangli Gao 	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
288062fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
288162fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
28828ed67789SDaniel Lezcano 
28838ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
28848ed67789SDaniel Lezcano 	net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
28858ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_prohibit_entry),
28868ed67789SDaniel Lezcano 					       GFP_KERNEL);
288768fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_prohibit_entry)
288868fffc67SPeter Zijlstra 		goto out_ip6_null_entry;
2889d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.path =
28908ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
2891d8d1f30bSChangli Gao 	net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
289262fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
289362fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
28948ed67789SDaniel Lezcano 
28958ed67789SDaniel Lezcano 	net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
28968ed67789SDaniel Lezcano 					       sizeof(*net->ipv6.ip6_blk_hole_entry),
28978ed67789SDaniel Lezcano 					       GFP_KERNEL);
289868fffc67SPeter Zijlstra 	if (!net->ipv6.ip6_blk_hole_entry)
289968fffc67SPeter Zijlstra 		goto out_ip6_prohibit_entry;
2900d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.path =
29018ed67789SDaniel Lezcano 		(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
2902d8d1f30bSChangli Gao 	net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
290362fa8a84SDavid S. Miller 	dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
290462fa8a84SDavid S. Miller 			 ip6_template_metrics, true);
29058ed67789SDaniel Lezcano #endif
29068ed67789SDaniel Lezcano 
2907b339a47cSPeter Zijlstra 	net->ipv6.sysctl.flush_delay = 0;
2908b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_max_size = 4096;
2909b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2910b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2911b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2912b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2913b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2914b339a47cSPeter Zijlstra 	net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2915b339a47cSPeter Zijlstra 
2916cdb18761SDaniel Lezcano #ifdef CONFIG_PROC_FS
2917cdb18761SDaniel Lezcano 	proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2918cdb18761SDaniel Lezcano 	proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2919cdb18761SDaniel Lezcano #endif
29206891a346SBenjamin Thery 	net->ipv6.ip6_rt_gc_expire = 30*HZ;
29216891a346SBenjamin Thery 
29228ed67789SDaniel Lezcano 	ret = 0;
29238ed67789SDaniel Lezcano out:
29248ed67789SDaniel Lezcano 	return ret;
2925f2fc6a54SBenjamin Thery 
292668fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES
292768fffc67SPeter Zijlstra out_ip6_prohibit_entry:
292868fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_prohibit_entry);
292968fffc67SPeter Zijlstra out_ip6_null_entry:
293068fffc67SPeter Zijlstra 	kfree(net->ipv6.ip6_null_entry);
293168fffc67SPeter Zijlstra #endif
2932fc66f95cSEric Dumazet out_ip6_dst_entries:
2933fc66f95cSEric Dumazet 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
2934f2fc6a54SBenjamin Thery out_ip6_dst_ops:
2935f2fc6a54SBenjamin Thery 	goto out;
2936cdb18761SDaniel Lezcano }
2937cdb18761SDaniel Lezcano 
29382c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net)
2939cdb18761SDaniel Lezcano {
2940cdb18761SDaniel Lezcano #ifdef CONFIG_PROC_FS
2941cdb18761SDaniel Lezcano 	proc_net_remove(net, "ipv6_route");
2942cdb18761SDaniel Lezcano 	proc_net_remove(net, "rt6_stats");
2943cdb18761SDaniel Lezcano #endif
29448ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_null_entry);
29458ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES
29468ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_prohibit_entry);
29478ed67789SDaniel Lezcano 	kfree(net->ipv6.ip6_blk_hole_entry);
29488ed67789SDaniel Lezcano #endif
294941bb78b4SXiaotian Feng 	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
2950cdb18761SDaniel Lezcano }
2951cdb18761SDaniel Lezcano 
2952cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = {
2953cdb18761SDaniel Lezcano 	.init = ip6_route_net_init,
2954cdb18761SDaniel Lezcano 	.exit = ip6_route_net_exit,
2955cdb18761SDaniel Lezcano };
2956cdb18761SDaniel Lezcano 
29578ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = {
29588ed67789SDaniel Lezcano 	.notifier_call = ip6_route_dev_notify,
29598ed67789SDaniel Lezcano 	.priority = 0,
29608ed67789SDaniel Lezcano };
29618ed67789SDaniel Lezcano 
2962433d49c3SDaniel Lezcano int __init ip6_route_init(void)
29631da177e4SLinus Torvalds {
2964433d49c3SDaniel Lezcano 	int ret;
2965433d49c3SDaniel Lezcano 
29669a7ec3a9SDaniel Lezcano 	ret = -ENOMEM;
29679a7ec3a9SDaniel Lezcano 	ip6_dst_ops_template.kmem_cachep =
29689a7ec3a9SDaniel Lezcano 		kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
29699a7ec3a9SDaniel Lezcano 				  SLAB_HWCACHE_ALIGN, NULL);
29709a7ec3a9SDaniel Lezcano 	if (!ip6_dst_ops_template.kmem_cachep)
2971c19a28e1SFernando Carrijo 		goto out;
297214e50e57SDavid S. Miller 
2973fc66f95cSEric Dumazet 	ret = dst_entries_init(&ip6_dst_blackhole_ops);
29748ed67789SDaniel Lezcano 	if (ret)
2975bdb3289fSDaniel Lezcano 		goto out_kmem_cache;
2976bdb3289fSDaniel Lezcano 
2977fc66f95cSEric Dumazet 	ret = register_pernet_subsys(&ip6_route_net_ops);
2978fc66f95cSEric Dumazet 	if (ret)
2979fc66f95cSEric Dumazet 		goto out_dst_entries;
2980fc66f95cSEric Dumazet 
29815dc121e9SArnaud Ebalard 	ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
29825dc121e9SArnaud Ebalard 
29838ed67789SDaniel Lezcano 	/* Registering of the loopback is done before this portion of code,
29848ed67789SDaniel Lezcano 	 * the loopback reference in rt6_info will not be taken, do it
29858ed67789SDaniel Lezcano 	 * manually for init_net */
2986d8d1f30bSChangli Gao 	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
29878ed67789SDaniel Lezcano 	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2988bdb3289fSDaniel Lezcano   #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2989d8d1f30bSChangli Gao 	init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
29908ed67789SDaniel Lezcano 	init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2991d8d1f30bSChangli Gao 	init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
29928ed67789SDaniel Lezcano 	init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2993bdb3289fSDaniel Lezcano   #endif
2994433d49c3SDaniel Lezcano 	ret = fib6_init();
2995433d49c3SDaniel Lezcano 	if (ret)
29968ed67789SDaniel Lezcano 		goto out_register_subsys;
2997433d49c3SDaniel Lezcano 
2998433d49c3SDaniel Lezcano 	ret = xfrm6_init();
2999433d49c3SDaniel Lezcano 	if (ret)
3000cdb18761SDaniel Lezcano 		goto out_fib6_init;
3001c35b7e72SDaniel Lezcano 
3002433d49c3SDaniel Lezcano 	ret = fib6_rules_init();
3003433d49c3SDaniel Lezcano 	if (ret)
3004433d49c3SDaniel Lezcano 		goto xfrm6_init;
30057e5449c2SDaniel Lezcano 
3006433d49c3SDaniel Lezcano 	ret = -ENOBUFS;
3007c7ac8679SGreg Rose 	if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3008c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3009c7ac8679SGreg Rose 	    __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
3010433d49c3SDaniel Lezcano 		goto fib6_rules_init;
3011433d49c3SDaniel Lezcano 
30128ed67789SDaniel Lezcano 	ret = register_netdevice_notifier(&ip6_route_dev_notifier);
3013cdb18761SDaniel Lezcano 	if (ret)
3014cdb18761SDaniel Lezcano 		goto fib6_rules_init;
30158ed67789SDaniel Lezcano 
3016433d49c3SDaniel Lezcano out:
3017433d49c3SDaniel Lezcano 	return ret;
3018433d49c3SDaniel Lezcano 
3019433d49c3SDaniel Lezcano fib6_rules_init:
3020433d49c3SDaniel Lezcano 	fib6_rules_cleanup();
3021433d49c3SDaniel Lezcano xfrm6_init:
3022433d49c3SDaniel Lezcano 	xfrm6_fini();
3023433d49c3SDaniel Lezcano out_fib6_init:
3024433d49c3SDaniel Lezcano 	fib6_gc_cleanup();
30258ed67789SDaniel Lezcano out_register_subsys:
30268ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
3027fc66f95cSEric Dumazet out_dst_entries:
3028fc66f95cSEric Dumazet 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3029433d49c3SDaniel Lezcano out_kmem_cache:
3030f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
3031433d49c3SDaniel Lezcano 	goto out;
30321da177e4SLinus Torvalds }
30331da177e4SLinus Torvalds 
30341da177e4SLinus Torvalds void ip6_route_cleanup(void)
30351da177e4SLinus Torvalds {
30368ed67789SDaniel Lezcano 	unregister_netdevice_notifier(&ip6_route_dev_notifier);
3037101367c2SThomas Graf 	fib6_rules_cleanup();
30381da177e4SLinus Torvalds 	xfrm6_fini();
30391da177e4SLinus Torvalds 	fib6_gc_cleanup();
30408ed67789SDaniel Lezcano 	unregister_pernet_subsys(&ip6_route_net_ops);
304141bb78b4SXiaotian Feng 	dst_entries_destroy(&ip6_dst_blackhole_ops);
3042f2fc6a54SBenjamin Thery 	kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
30431da177e4SLinus Torvalds }
3044