blob: ab48b02eb56a7c96c5723a658820ba1dec993d38 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Randy Dunlap4fc268d2006-01-11 12:17:47 -080027#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040029#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/types.h>
31#include <linux/times.h>
32#include <linux/socket.h>
33#include <linux/sockios.h>
34#include <linux/net.h>
35#include <linux/route.h>
36#include <linux/netdevice.h>
37#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090038#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/proc_fs.h>
42#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080043#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090044#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020045#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <net/snmp.h>
47#include <net/ipv6.h>
48#include <net/ip6_fib.h>
49#include <net/ip6_route.h>
50#include <net/ndisc.h>
51#include <net/addrconf.h>
52#include <net/tcp.h>
53#include <linux/rtnetlink.h>
54#include <net/dst.h>
55#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070056#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070057#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59#include <asm/uaccess.h>
60
61#ifdef CONFIG_SYSCTL
62#include <linux/sysctl.h>
63#endif
64
65/* Set to 3 to get tracing. */
66#define RT6_DEBUG 2
67
68#if RT6_DEBUG >= 3
69#define RDBG(x) printk x
70#define RT6_TRACE(x...) printk(KERN_DEBUG x)
71#else
72#define RDBG(x)
73#define RT6_TRACE(x...) do { ; } while (0)
74#endif
75
Eric Dumazet21efcfa2011-07-19 20:18:36 +000076static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort,
77 const struct in6_addr *dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080079static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000080static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081static struct dst_entry *ip6_negative_advice(struct dst_entry *);
82static void ip6_dst_destroy(struct dst_entry *);
83static void ip6_dst_ifdown(struct dst_entry *,
84 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080085static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
87static int ip6_pkt_discard(struct sk_buff *skb);
88static int ip6_pkt_discard_out(struct sk_buff *skb);
89static void ip6_link_failure(struct sk_buff *skb);
90static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
91
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080092#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080093static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000094 const struct in6_addr *prefix, int prefixlen,
95 const struct in6_addr *gwaddr, int ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080096 unsigned pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080097static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000098 const struct in6_addr *prefix, int prefixlen,
99 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800100#endif
101
David S. Miller06582542011-01-27 14:58:42 -0800102static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
103{
104 struct rt6_info *rt = (struct rt6_info *) dst;
105 struct inet_peer *peer;
106 u32 *p = NULL;
107
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000108 if (!(rt->dst.flags & DST_HOST))
109 return NULL;
110
David S. Miller06582542011-01-27 14:58:42 -0800111 if (!rt->rt6i_peer)
112 rt6_bind_peer(rt, 1);
113
114 peer = rt->rt6i_peer;
115 if (peer) {
116 u32 *old_p = __DST_METRICS_PTR(old);
117 unsigned long prev, new;
118
119 p = peer->metrics;
120 if (inet_metrics_new(peer))
121 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
122
123 new = (unsigned long) p;
124 prev = cmpxchg(&dst->_metrics, old, new);
125
126 if (prev != old) {
127 p = __DST_METRICS_PTR(prev);
128 if (prev & DST_METRICS_READ_ONLY)
129 p = NULL;
130 }
131 }
132 return p;
133}
134
David S. Millerd3aaeb32011-07-18 00:40:17 -0700135static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, const void *daddr)
136{
137 return __neigh_lookup_errno(&nd_tbl, daddr, dst->dev);
138}
139
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800140static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800142 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 .gc = ip6_dst_gc,
144 .gc_thresh = 1024,
145 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800146 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000147 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800148 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 .destroy = ip6_dst_destroy,
150 .ifdown = ip6_dst_ifdown,
151 .negative_advice = ip6_negative_advice,
152 .link_failure = ip6_link_failure,
153 .update_pmtu = ip6_rt_update_pmtu,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700154 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700155 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156};
157
Steffen Klassertebb762f2011-11-23 02:12:51 +0000158static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800159{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000160 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
161
162 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800163}
164
David S. Miller14e50e52007-05-24 18:17:54 -0700165static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
166{
167}
168
Held Bernhard0972ddb2011-04-24 22:07:32 +0000169static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
170 unsigned long old)
171{
172 return NULL;
173}
174
David S. Miller14e50e52007-05-24 18:17:54 -0700175static struct dst_ops ip6_dst_blackhole_ops = {
176 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800177 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700178 .destroy = ip6_dst_destroy,
179 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000180 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800181 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700182 .update_pmtu = ip6_rt_blackhole_update_pmtu,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000183 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700184 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700185};
186
David S. Miller62fa8a82011-01-26 20:51:05 -0800187static const u32 ip6_template_metrics[RTAX_MAX] = {
188 [RTAX_HOPLIMIT - 1] = 255,
189};
190
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800191static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700192 .dst = {
193 .__refcnt = ATOMIC_INIT(1),
194 .__use = 1,
195 .obsolete = -1,
196 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700197 .input = ip6_pkt_discard,
198 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 },
200 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700201 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 .rt6i_metric = ~(u32) 0,
203 .rt6i_ref = ATOMIC_INIT(1),
204};
205
Thomas Graf101367c2006-08-04 03:39:02 -0700206#ifdef CONFIG_IPV6_MULTIPLE_TABLES
207
David S. Miller6723ab52006-10-18 21:20:57 -0700208static int ip6_pkt_prohibit(struct sk_buff *skb);
209static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700210
Adrian Bunk280a34c2008-04-21 02:29:32 -0700211static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700212 .dst = {
213 .__refcnt = ATOMIC_INIT(1),
214 .__use = 1,
215 .obsolete = -1,
216 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700217 .input = ip6_pkt_prohibit,
218 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700219 },
220 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700221 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700222 .rt6i_metric = ~(u32) 0,
223 .rt6i_ref = ATOMIC_INIT(1),
224};
225
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800226static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700227 .dst = {
228 .__refcnt = ATOMIC_INIT(1),
229 .__use = 1,
230 .obsolete = -1,
231 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700232 .input = dst_discard,
233 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700234 },
235 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700236 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700237 .rt6i_metric = ~(u32) 0,
238 .rt6i_ref = ATOMIC_INIT(1),
239};
240
241#endif
242
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243/* allocate dst with ip6_dst_ops */
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700244static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops,
David S. Miller957c6652011-06-24 15:25:00 -0700245 struct net_device *dev,
246 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247{
David S. Miller957c6652011-06-24 15:25:00 -0700248 struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700249
David S. Miller38308472011-12-03 18:02:47 -0500250 if (rt)
Madalin Bucurfbe58182011-09-26 07:04:56 +0000251 memset(&rt->rt6i_table, 0,
David S. Miller38308472011-12-03 18:02:47 -0500252 sizeof(*rt) - sizeof(struct dst_entry));
David S. Millercf911662011-04-28 14:31:47 -0700253
254 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255}
256
257static void ip6_dst_destroy(struct dst_entry *dst)
258{
259 struct rt6_info *rt = (struct rt6_info *)dst;
260 struct inet6_dev *idev = rt->rt6i_idev;
David S. Millerb3419362010-11-30 12:27:11 -0800261 struct inet_peer *peer = rt->rt6i_peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000263 if (!(rt->dst.flags & DST_HOST))
264 dst_destroy_metrics_generic(dst);
265
David S. Miller38308472011-12-03 18:02:47 -0500266 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 rt->rt6i_idev = NULL;
268 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900269 }
David S. Millerb3419362010-11-30 12:27:11 -0800270 if (peer) {
David S. Millerb3419362010-11-30 12:27:11 -0800271 rt->rt6i_peer = NULL;
272 inet_putpeer(peer);
273 }
274}
275
David S. Miller6431cbc2011-02-07 20:38:06 -0800276static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
277
278static u32 rt6_peer_genid(void)
279{
280 return atomic_read(&__rt6_peer_genid);
281}
282
David S. Millerb3419362010-11-30 12:27:11 -0800283void rt6_bind_peer(struct rt6_info *rt, int create)
284{
285 struct inet_peer *peer;
286
David S. Millerb3419362010-11-30 12:27:11 -0800287 peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create);
288 if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL)
289 inet_putpeer(peer);
David S. Miller6431cbc2011-02-07 20:38:06 -0800290 else
291 rt->rt6i_peer_genid = rt6_peer_genid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292}
293
294static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
295 int how)
296{
297 struct rt6_info *rt = (struct rt6_info *)dst;
298 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800299 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900300 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
David S. Miller38308472011-12-03 18:02:47 -0500302 if (dev != loopback_dev && idev && idev->dev == dev) {
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800303 struct inet6_dev *loopback_idev =
304 in6_dev_get(loopback_dev);
David S. Miller38308472011-12-03 18:02:47 -0500305 if (loopback_idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 rt->rt6i_idev = loopback_idev;
307 in6_dev_put(idev);
308 }
309 }
310}
311
312static __inline__ int rt6_check_expired(const struct rt6_info *rt)
313{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000314 return (rt->rt6i_flags & RTF_EXPIRES) &&
315 time_after(jiffies, rt->rt6i_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316}
317
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000318static inline int rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700319{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000320 return ipv6_addr_type(daddr) &
321 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700322}
323
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700325 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 */
327
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800328static inline struct rt6_info *rt6_device_match(struct net *net,
329 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000330 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700332 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
334 struct rt6_info *local = NULL;
335 struct rt6_info *sprt;
336
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900337 if (!oif && ipv6_addr_any(saddr))
338 goto out;
339
Changli Gaod8d1f302010-06-10 23:31:35 -0700340 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900341 struct net_device *dev = sprt->rt6i_dev;
342
343 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 if (dev->ifindex == oif)
345 return sprt;
346 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500347 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700349 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900351 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 local->rt6i_idev->dev->ifindex == oif))
353 continue;
354 }
355 local = sprt;
356 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900357 } else {
358 if (ipv6_chk_addr(net, saddr, dev,
359 flags & RT6_LOOKUP_F_IFACE))
360 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900362 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900364 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 if (local)
366 return local;
367
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700368 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800369 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900371out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 return rt;
373}
374
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800375#ifdef CONFIG_IPV6_ROUTER_PREF
376static void rt6_probe(struct rt6_info *rt)
377{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000378 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800379 /*
380 * Okay, this does not seem to be appropriate
381 * for now, however, we need to check if it
382 * is really so; aka Router Reachability Probing.
383 *
384 * Router Reachability Probe MUST be rate-limited
385 * to no more than one per minute.
386 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000387 rcu_read_lock();
388 neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800389 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000390 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800391 read_lock_bh(&neigh->lock);
392 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800393 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800394 struct in6_addr mcaddr;
395 struct in6_addr *target;
396
397 neigh->updated = jiffies;
398 read_unlock_bh(&neigh->lock);
399
400 target = (struct in6_addr *)&neigh->primary_key;
401 addrconf_addr_solict_mult(target, &mcaddr);
402 ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000403 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800404 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000405 }
406out:
407 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800408}
409#else
410static inline void rt6_probe(struct rt6_info *rt)
411{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800412}
413#endif
414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800416 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700418static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800420 struct net_device *dev = rt->rt6i_dev;
David S. Miller161980f2007-04-06 11:42:27 -0700421 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800422 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700423 if ((dev->flags & IFF_LOOPBACK) &&
424 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
425 return 1;
426 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427}
428
Dave Jonesb6f99a22007-03-22 12:27:49 -0700429static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000431 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800432 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000433
434 rcu_read_lock();
435 neigh = dst_get_neighbour(&rt->dst);
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700436 if (rt->rt6i_flags & RTF_NONEXTHOP ||
437 !(rt->rt6i_flags & RTF_GATEWAY))
438 m = 1;
439 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800440 read_lock_bh(&neigh->lock);
441 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700442 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800443#ifdef CONFIG_IPV6_ROUTER_PREF
444 else if (neigh->nud_state & NUD_FAILED)
445 m = 0;
446#endif
447 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800448 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800449 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800450 } else
451 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000452 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800453 return m;
454}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800456static int rt6_score_route(struct rt6_info *rt, int oif,
457 int strict)
458{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700459 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900460
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700461 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700462 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800463 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800464#ifdef CONFIG_IPV6_ROUTER_PREF
465 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
466#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700467 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800468 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800469 return -1;
470 return m;
471}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
David S. Millerf11e6652007-03-24 20:36:25 -0700473static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
474 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800475{
David S. Millerf11e6652007-03-24 20:36:25 -0700476 int m;
477
478 if (rt6_check_expired(rt))
479 goto out;
480
481 m = rt6_score_route(rt, oif, strict);
482 if (m < 0)
483 goto out;
484
485 if (m > *mpri) {
486 if (strict & RT6_LOOKUP_F_REACHABLE)
487 rt6_probe(match);
488 *mpri = m;
489 match = rt;
490 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
491 rt6_probe(rt);
492 }
493
494out:
495 return match;
496}
497
498static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
499 struct rt6_info *rr_head,
500 u32 metric, int oif, int strict)
501{
502 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800503 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
David S. Millerf11e6652007-03-24 20:36:25 -0700505 match = NULL;
506 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700507 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700508 match = find_match(rt, oif, strict, &mpri, match);
509 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700510 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700511 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800512
David S. Millerf11e6652007-03-24 20:36:25 -0700513 return match;
514}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800515
David S. Millerf11e6652007-03-24 20:36:25 -0700516static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
517{
518 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800519 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
David S. Millerf11e6652007-03-24 20:36:25 -0700521 RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800522 __func__, fn->leaf, oif);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
David S. Millerf11e6652007-03-24 20:36:25 -0700524 rt0 = fn->rr_ptr;
525 if (!rt0)
526 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
David S. Millerf11e6652007-03-24 20:36:25 -0700528 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800530 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700531 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700532 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700533
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800534 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700535 if (!next || next->rt6i_metric != rt0->rt6i_metric)
536 next = fn->leaf;
537
538 if (next != rt0)
539 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 }
541
David S. Millerf11e6652007-03-24 20:36:25 -0700542 RT6_TRACE("%s() => %p\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800543 __func__, match);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900545 net = dev_net(rt0->rt6i_dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000546 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547}
548
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800549#ifdef CONFIG_IPV6_ROUTE_INFO
550int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000551 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800552{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900553 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800554 struct route_info *rinfo = (struct route_info *) opt;
555 struct in6_addr prefix_buf, *prefix;
556 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900557 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800558 struct rt6_info *rt;
559
560 if (len < sizeof(struct route_info)) {
561 return -EINVAL;
562 }
563
564 /* Sanity check for prefix_len and length */
565 if (rinfo->length > 3) {
566 return -EINVAL;
567 } else if (rinfo->prefix_len > 128) {
568 return -EINVAL;
569 } else if (rinfo->prefix_len > 64) {
570 if (rinfo->length < 2) {
571 return -EINVAL;
572 }
573 } else if (rinfo->prefix_len > 0) {
574 if (rinfo->length < 1) {
575 return -EINVAL;
576 }
577 }
578
579 pref = rinfo->route_pref;
580 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000581 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800582
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900583 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800584
585 if (rinfo->length == 3)
586 prefix = (struct in6_addr *)rinfo->prefix;
587 else {
588 /* this function is safe */
589 ipv6_addr_prefix(&prefix_buf,
590 (struct in6_addr *)rinfo->prefix,
591 rinfo->prefix_len);
592 prefix = &prefix_buf;
593 }
594
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800595 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
596 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800597
598 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700599 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800600 rt = NULL;
601 }
602
603 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800604 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800605 pref);
606 else if (rt)
607 rt->rt6i_flags = RTF_ROUTEINFO |
608 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
609
610 if (rt) {
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900611 if (!addrconf_finite_timeout(lifetime)) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800612 rt->rt6i_flags &= ~RTF_EXPIRES;
613 } else {
614 rt->rt6i_expires = jiffies + HZ * lifetime;
615 rt->rt6i_flags |= RTF_EXPIRES;
616 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700617 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800618 }
619 return 0;
620}
621#endif
622
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800623#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700624do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800625 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700626 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700627 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700628 if (fn->fn_flags & RTN_TL_ROOT) \
629 goto out; \
630 pn = fn->parent; \
631 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800632 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700633 else \
634 fn = pn; \
635 if (fn->fn_flags & RTN_RTINFO) \
636 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700637 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700638 } \
David S. Miller38308472011-12-03 18:02:47 -0500639} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700640
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800641static struct rt6_info *ip6_pol_route_lookup(struct net *net,
642 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500643 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644{
645 struct fib6_node *fn;
646 struct rt6_info *rt;
647
Thomas Grafc71099a2006-08-04 23:20:06 -0700648 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500649 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700650restart:
651 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500652 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
653 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700654out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700655 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700656 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700657 return rt;
658
659}
660
Florian Westphalea6e5742011-09-05 16:05:44 +0200661struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
662 int flags)
663{
664 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
665}
666EXPORT_SYMBOL_GPL(ip6_route_lookup);
667
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900668struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
669 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700670{
David S. Miller4c9483b2011-03-12 16:22:43 -0500671 struct flowi6 fl6 = {
672 .flowi6_oif = oif,
673 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700674 };
675 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700676 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700677
Thomas Grafadaa70b2006-10-13 15:01:03 -0700678 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500679 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700680 flags |= RT6_LOOKUP_F_HAS_SADDR;
681 }
682
David S. Miller4c9483b2011-03-12 16:22:43 -0500683 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700684 if (dst->error == 0)
685 return (struct rt6_info *) dst;
686
687 dst_release(dst);
688
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 return NULL;
690}
691
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900692EXPORT_SYMBOL(rt6_lookup);
693
Thomas Grafc71099a2006-08-04 23:20:06 -0700694/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 It takes new route entry, the addition fails by any reason the
696 route is freed. In any case, if caller does not hold it, it may
697 be destroyed.
698 */
699
Thomas Graf86872cb2006-08-22 00:01:08 -0700700static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701{
702 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700703 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
Thomas Grafc71099a2006-08-04 23:20:06 -0700705 table = rt->rt6i_table;
706 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700707 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700708 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
710 return err;
711}
712
Thomas Graf40e22e82006-08-22 00:00:45 -0700713int ip6_ins_rt(struct rt6_info *rt)
714{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800715 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900716 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800717 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800718 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700719}
720
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000721static struct rt6_info *rt6_alloc_cow(const struct rt6_info *ort,
722 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000723 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 struct rt6_info *rt;
726
727 /*
728 * Clone the route.
729 */
730
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000731 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
733 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800734 struct neighbour *neigh;
735 int attempts = !in_softirq();
736
David S. Miller38308472011-12-03 18:02:47 -0500737 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900738 if (rt->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000739 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900740 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000741 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900742 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
746#ifdef CONFIG_IPV6_SUBTREES
747 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000748 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 rt->rt6i_src.plen = 128;
750 }
751#endif
752
David S. Miller14deae42009-01-04 16:04:39 -0800753 retry:
David S. Miller04a6f442011-12-03 18:29:30 -0500754 neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway,
755 rt->rt6i_dev);
David S. Miller14deae42009-01-04 16:04:39 -0800756 if (IS_ERR(neigh)) {
757 struct net *net = dev_net(rt->rt6i_dev);
758 int saved_rt_min_interval =
759 net->ipv6.sysctl.ip6_rt_gc_min_interval;
760 int saved_rt_elasticity =
761 net->ipv6.sysctl.ip6_rt_gc_elasticity;
762
763 if (attempts-- > 0) {
764 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
765 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
766
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000767 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800768
769 net->ipv6.sysctl.ip6_rt_gc_elasticity =
770 saved_rt_elasticity;
771 net->ipv6.sysctl.ip6_rt_gc_min_interval =
772 saved_rt_min_interval;
773 goto retry;
774 }
775
776 if (net_ratelimit())
777 printk(KERN_WARNING
Ulrich Weber7e1b33e2010-09-27 15:02:18 -0700778 "ipv6: Neighbour table overflow.\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700779 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800780 return NULL;
781 }
David S. Miller69cce1d2011-07-17 23:09:49 -0700782 dst_set_neighbour(&rt->dst, neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800784 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800786 return rt;
787}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000789static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
790 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800791{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000792 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
793
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800794 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800795 rt->rt6i_flags |= RTF_CACHE;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000796 dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst)));
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800797 }
798 return rt;
799}
800
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800801static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500802 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803{
804 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800805 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700806 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800808 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700809 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700811 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
813relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700814 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800816restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500817 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
819restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700820 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800821
David S. Miller4c9483b2011-03-12 16:22:43 -0500822 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800823 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800824 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef042006-03-20 17:01:24 -0800825 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Changli Gaod8d1f302010-06-10 23:31:35 -0700827 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700828 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800829
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000830 if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500831 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800832 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500833 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800834 else
835 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800836
Changli Gaod8d1f302010-06-10 23:31:35 -0700837 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800838 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800839
Changli Gaod8d1f302010-06-10 23:31:35 -0700840 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800841 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700842 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800843 if (!err)
844 goto out2;
845 }
846
847 if (--attempts <= 0)
848 goto out2;
849
850 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700851 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800852 * released someone could insert this route. Relookup.
853 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700854 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800855 goto relookup;
856
857out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800858 if (reachable) {
859 reachable = 0;
860 goto restart_2;
861 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700862 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700863 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700865 rt->dst.lastuse = jiffies;
866 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700867
868 return rt;
869}
870
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800871static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500872 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700873{
David S. Miller4c9483b2011-03-12 16:22:43 -0500874 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700875}
876
Thomas Grafc71099a2006-08-04 23:20:06 -0700877void ip6_route_input(struct sk_buff *skb)
878{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000879 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900880 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700881 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500882 struct flowi6 fl6 = {
883 .flowi6_iif = skb->dev->ifindex,
884 .daddr = iph->daddr,
885 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500886 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500887 .flowi6_mark = skb->mark,
888 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700889 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700890
Thomas Goff1d6e55f2009-01-27 22:39:59 -0800891 if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG)
Thomas Grafadaa70b2006-10-13 15:01:03 -0700892 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700893
David S. Miller4c9483b2011-03-12 16:22:43 -0500894 skb_dst_set(skb, fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input));
Thomas Grafc71099a2006-08-04 23:20:06 -0700895}
896
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800897static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500898 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700899{
David S. Miller4c9483b2011-03-12 16:22:43 -0500900 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700901}
902
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700903struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500904 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700905{
906 int flags = 0;
907
David S. Miller4c9483b2011-03-12 16:22:43 -0500908 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700909 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700910
David S. Miller4c9483b2011-03-12 16:22:43 -0500911 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700912 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000913 else if (sk)
914 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700915
David S. Miller4c9483b2011-03-12 16:22:43 -0500916 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917}
918
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900919EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
David S. Miller2774c132011-03-01 14:59:04 -0800921struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700922{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700923 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700924 struct dst_entry *new = NULL;
925
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700926 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700927 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700928 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
929
Changli Gaod8d1f302010-06-10 23:31:35 -0700930 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700931
David S. Miller14e50e52007-05-24 18:17:54 -0700932 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800933 new->input = dst_discard;
934 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700935
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000936 if (dst_metrics_read_only(&ort->dst))
937 new->_metrics = ort->dst._metrics;
938 else
939 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700940 rt->rt6i_idev = ort->rt6i_idev;
941 if (rt->rt6i_idev)
942 in6_dev_hold(rt->rt6i_idev);
943 rt->rt6i_expires = 0;
944
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000945 rt->rt6i_gateway = ort->rt6i_gateway;
David S. Miller14e50e52007-05-24 18:17:54 -0700946 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
947 rt->rt6i_metric = 0;
948
949 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
950#ifdef CONFIG_IPV6_SUBTREES
951 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
952#endif
953
954 dst_free(new);
955 }
956
David S. Miller69ead7a2011-03-01 14:45:33 -0800957 dst_release(dst_orig);
958 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -0700959}
David S. Miller14e50e52007-05-24 18:17:54 -0700960
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961/*
962 * Destination cache support functions
963 */
964
965static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
966{
967 struct rt6_info *rt;
968
969 rt = (struct rt6_info *) dst;
970
David S. Miller6431cbc2011-02-07 20:38:06 -0800971 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
972 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
973 if (!rt->rt6i_peer)
974 rt6_bind_peer(rt, 0);
975 rt->rt6i_peer_genid = rt6_peer_genid();
976 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -0800978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 return NULL;
980}
981
982static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
983{
984 struct rt6_info *rt = (struct rt6_info *) dst;
985
986 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000987 if (rt->rt6i_flags & RTF_CACHE) {
988 if (rt6_check_expired(rt)) {
989 ip6_del_rt(rt);
990 dst = NULL;
991 }
992 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000994 dst = NULL;
995 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000997 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998}
999
1000static void ip6_link_failure(struct sk_buff *skb)
1001{
1002 struct rt6_info *rt;
1003
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001004 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Eric Dumazetadf30902009-06-02 05:19:30 +00001006 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 if (rt) {
David S. Miller38308472011-12-03 18:02:47 -05001008 if (rt->rt6i_flags & RTF_CACHE) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001009 dst_set_expires(&rt->dst, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 rt->rt6i_flags |= RTF_EXPIRES;
1011 } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
1012 rt->rt6i_node->fn_sernum = -1;
1013 }
1014}
1015
1016static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1017{
1018 struct rt6_info *rt6 = (struct rt6_info*)dst;
1019
1020 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
1021 rt6->rt6i_flags |= RTF_MODIFIED;
1022 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001023 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001025 features |= RTAX_FEATURE_ALLFRAG;
1026 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 }
David S. Millerdefb3512010-12-08 21:16:57 -08001028 dst_metric_set(dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 }
1030}
1031
David S. Miller0dbaee32010-12-13 12:52:14 -08001032static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033{
David S. Miller0dbaee32010-12-13 12:52:14 -08001034 struct net_device *dev = dst->dev;
1035 unsigned int mtu = dst_mtu(dst);
1036 struct net *net = dev_net(dev);
1037
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1039
Daniel Lezcano55786892008-03-04 13:47:47 -08001040 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1041 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042
1043 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001044 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1045 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1046 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 * rely only on pmtu discovery"
1048 */
1049 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1050 mtu = IPV6_MAXPLEN;
1051 return mtu;
1052}
1053
Steffen Klassertebb762f2011-11-23 02:12:51 +00001054static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001055{
David S. Millerd33e4552010-12-14 13:01:14 -08001056 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001057 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1058
1059 if (mtu)
1060 return mtu;
1061
1062 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001063
1064 rcu_read_lock();
1065 idev = __in6_dev_get(dst->dev);
1066 if (idev)
1067 mtu = idev->cnf.mtu6;
1068 rcu_read_unlock();
1069
1070 return mtu;
1071}
1072
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001073static struct dst_entry *icmp6_dst_gc_list;
1074static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001075
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001076struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001078 const struct in6_addr *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
1080 struct rt6_info *rt;
1081 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001082 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083
David S. Miller38308472011-12-03 18:02:47 -05001084 if (unlikely(!idev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 return NULL;
1086
David S. Miller957c6652011-06-24 15:25:00 -07001087 rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05001088 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 in6_dev_put(idev);
1090 goto out;
1091 }
1092
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 if (neigh)
1094 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001095 else {
David S. Miller04a6f442011-12-03 18:29:30 -05001096 neigh = __neigh_lookup_errno(&nd_tbl, addr, dev);
David S. Miller14deae42009-01-04 16:04:39 -08001097 if (IS_ERR(neigh))
1098 neigh = NULL;
1099 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001101 rt->dst.flags |= DST_HOST;
1102 rt->dst.output = ip6_output;
David S. Miller69cce1d2011-07-17 23:09:49 -07001103 dst_set_neighbour(&rt->dst, neigh);
Changli Gaod8d1f302010-06-10 23:31:35 -07001104 atomic_set(&rt->dst.__refcnt, 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001105 rt->rt6i_dst.addr = *addr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001106 rt->rt6i_dst.plen = 128;
1107 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001108 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001110 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001111 rt->dst.next = icmp6_dst_gc_list;
1112 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001113 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
Daniel Lezcano55786892008-03-04 13:47:47 -08001115 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
1117out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001118 return &rt->dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119}
1120
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001121int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001123 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001124 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001126 spin_lock_bh(&icmp6_dst_lock);
1127 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001128
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 while ((dst = *pprev) != NULL) {
1130 if (!atomic_read(&dst->__refcnt)) {
1131 *pprev = dst->next;
1132 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 } else {
1134 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001135 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 }
1137 }
1138
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001139 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001140
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001141 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142}
1143
David S. Miller1e493d12008-09-10 17:27:15 -07001144static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1145 void *arg)
1146{
1147 struct dst_entry *dst, **pprev;
1148
1149 spin_lock_bh(&icmp6_dst_lock);
1150 pprev = &icmp6_dst_gc_list;
1151 while ((dst = *pprev) != NULL) {
1152 struct rt6_info *rt = (struct rt6_info *) dst;
1153 if (func(rt, arg)) {
1154 *pprev = dst->next;
1155 dst_free(dst);
1156 } else {
1157 pprev = &dst->next;
1158 }
1159 }
1160 spin_unlock_bh(&icmp6_dst_lock);
1161}
1162
Daniel Lezcano569d3642008-01-18 03:56:57 -08001163static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001166 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001167 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1168 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1169 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1170 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1171 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001172 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173
Eric Dumazetfc66f952010-10-08 06:37:34 +00001174 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001175 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001176 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 goto out;
1178
Benjamin Thery6891a342008-03-04 13:49:47 -08001179 net->ipv6.ip6_rt_gc_expire++;
1180 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1181 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001182 entries = dst_entries_get_slow(ops);
1183 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001184 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001186 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001187 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188}
1189
1190/* Clean host part of a prefix. Not necessary in radix tree,
1191 but results in cleaner routing tables.
1192
1193 Remove it only when all the things will work!
1194 */
1195
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001196int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197{
David S. Miller5170ae82010-12-12 21:35:57 -08001198 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001199 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001200 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001201 struct inet6_dev *idev;
1202
1203 rcu_read_lock();
1204 idev = __in6_dev_get(dev);
1205 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001206 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001207 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001208 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001209 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 }
1211 return hoplimit;
1212}
David S. Millerabbf46a2010-12-12 21:14:46 -08001213EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
1215/*
1216 *
1217 */
1218
Thomas Graf86872cb2006-08-22 00:01:08 -07001219int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220{
1221 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001222 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 struct rt6_info *rt = NULL;
1224 struct net_device *dev = NULL;
1225 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001226 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 int addr_type;
1228
Thomas Graf86872cb2006-08-22 00:01:08 -07001229 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 return -EINVAL;
1231#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001232 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 return -EINVAL;
1234#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001235 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001237 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 if (!dev)
1239 goto out;
1240 idev = in6_dev_get(dev);
1241 if (!idev)
1242 goto out;
1243 }
1244
Thomas Graf86872cb2006-08-22 00:01:08 -07001245 if (cfg->fc_metric == 0)
1246 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
Matti Vaittinend71314b2011-11-14 00:14:49 +00001248 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001249 if (cfg->fc_nlinfo.nlh &&
1250 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001251 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001252 if (!table) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001253 printk(KERN_WARNING "IPv6: NLM_F_CREATE should be specified when creating new route\n");
1254 table = fib6_new_table(net, cfg->fc_table);
1255 }
1256 } else {
1257 table = fib6_new_table(net, cfg->fc_table);
1258 }
David S. Miller38308472011-12-03 18:02:47 -05001259
1260 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001261 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001262
David S. Miller957c6652011-06-24 15:25:00 -07001263 rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL, DST_NOCOUNT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264
David S. Miller38308472011-12-03 18:02:47 -05001265 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 err = -ENOMEM;
1267 goto out;
1268 }
1269
Changli Gaod8d1f302010-06-10 23:31:35 -07001270 rt->dst.obsolete = -1;
YOSHIFUJI Hideaki6f704992008-05-19 16:56:11 -07001271 rt->rt6i_expires = (cfg->fc_flags & RTF_EXPIRES) ?
1272 jiffies + clock_t_to_jiffies(cfg->fc_expires) :
1273 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Thomas Graf86872cb2006-08-22 00:01:08 -07001275 if (cfg->fc_protocol == RTPROT_UNSPEC)
1276 cfg->fc_protocol = RTPROT_BOOT;
1277 rt->rt6i_protocol = cfg->fc_protocol;
1278
1279 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
1281 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001282 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001283 else if (cfg->fc_flags & RTF_LOCAL)
1284 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001286 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
Changli Gaod8d1f302010-06-10 23:31:35 -07001288 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289
Thomas Graf86872cb2006-08-22 00:01:08 -07001290 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1291 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001293 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001295 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1296 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1297 if (!metrics) {
1298 err = -ENOMEM;
1299 goto out;
1300 }
1301 dst_init_metrics(&rt->dst, metrics, 0);
1302 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001304 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1305 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306#endif
1307
Thomas Graf86872cb2006-08-22 00:01:08 -07001308 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
1310 /* We cannot add true routes via loopback here,
1311 they would result in kernel looping; promote them to reject routes
1312 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001313 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001314 (dev && (dev->flags & IFF_LOOPBACK) &&
1315 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1316 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001318 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 if (dev) {
1320 dev_put(dev);
1321 in6_dev_put(idev);
1322 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001323 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 dev_hold(dev);
1325 idev = in6_dev_get(dev);
1326 if (!idev) {
1327 err = -ENODEV;
1328 goto out;
1329 }
1330 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001331 rt->dst.output = ip6_pkt_discard_out;
1332 rt->dst.input = ip6_pkt_discard;
1333 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1335 goto install_route;
1336 }
1337
Thomas Graf86872cb2006-08-22 00:01:08 -07001338 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001339 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 int gwa_type;
1341
Thomas Graf86872cb2006-08-22 00:01:08 -07001342 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001343 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 gwa_type = ipv6_addr_type(gw_addr);
1345
1346 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1347 struct rt6_info *grt;
1348
1349 /* IPv6 strictly inhibits using not link-local
1350 addresses as nexthop address.
1351 Otherwise, router will not able to send redirects.
1352 It is very good, but in some (rare!) circumstances
1353 (SIT, PtP, NBMA NOARP links) it is handy to allow
1354 some exceptions. --ANK
1355 */
1356 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001357 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 goto out;
1359
Daniel Lezcano55786892008-03-04 13:47:47 -08001360 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
1362 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001363 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 goto out;
1365 if (dev) {
1366 if (dev != grt->rt6i_dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001367 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 goto out;
1369 }
1370 } else {
1371 dev = grt->rt6i_dev;
1372 idev = grt->rt6i_idev;
1373 dev_hold(dev);
1374 in6_dev_hold(grt->rt6i_idev);
1375 }
David S. Miller38308472011-12-03 18:02:47 -05001376 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001378 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
1380 if (err)
1381 goto out;
1382 }
1383 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001384 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 goto out;
1386 }
1387
1388 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001389 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 goto out;
1391
Daniel Walterc3968a82011-04-13 21:10:57 +00001392 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1393 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1394 err = -EINVAL;
1395 goto out;
1396 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001397 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001398 rt->rt6i_prefsrc.plen = 128;
1399 } else
1400 rt->rt6i_prefsrc.plen = 0;
1401
Thomas Graf86872cb2006-08-22 00:01:08 -07001402 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller69cce1d2011-07-17 23:09:49 -07001403 struct neighbour *n = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);
1404 if (IS_ERR(n)) {
1405 err = PTR_ERR(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 goto out;
1407 }
David S. Miller69cce1d2011-07-17 23:09:49 -07001408 dst_set_neighbour(&rt->dst, n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 }
1410
Thomas Graf86872cb2006-08-22 00:01:08 -07001411 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
1413install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001414 if (cfg->fc_mx) {
1415 struct nlattr *nla;
1416 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
Thomas Graf86872cb2006-08-22 00:01:08 -07001418 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001419 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001420
1421 if (type) {
1422 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 err = -EINVAL;
1424 goto out;
1425 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001426
David S. Millerdefb3512010-12-08 21:16:57 -08001427 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 }
1430 }
1431
Changli Gaod8d1f302010-06-10 23:31:35 -07001432 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001434 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001435
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001436 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001437
Thomas Graf86872cb2006-08-22 00:01:08 -07001438 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
1440out:
1441 if (dev)
1442 dev_put(dev);
1443 if (idev)
1444 in6_dev_put(idev);
1445 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001446 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 return err;
1448}
1449
Thomas Graf86872cb2006-08-22 00:01:08 -07001450static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451{
1452 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001453 struct fib6_table *table;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001454 struct net *net = dev_net(rt->rt6i_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001456 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001457 return -ENOENT;
1458
Thomas Grafc71099a2006-08-04 23:20:06 -07001459 table = rt->rt6i_table;
1460 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461
Thomas Graf86872cb2006-08-22 00:01:08 -07001462 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001463 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
Thomas Grafc71099a2006-08-04 23:20:06 -07001465 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466
1467 return err;
1468}
1469
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001470int ip6_del_rt(struct rt6_info *rt)
1471{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001472 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001473 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001474 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001475 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001476}
1477
Thomas Graf86872cb2006-08-22 00:01:08 -07001478static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479{
Thomas Grafc71099a2006-08-04 23:20:06 -07001480 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 struct fib6_node *fn;
1482 struct rt6_info *rt;
1483 int err = -ESRCH;
1484
Daniel Lezcano55786892008-03-04 13:47:47 -08001485 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001486 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001487 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
Thomas Grafc71099a2006-08-04 23:20:06 -07001489 read_lock_bh(&table->tb6_lock);
1490
1491 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001492 &cfg->fc_dst, cfg->fc_dst_len,
1493 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001494
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001496 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001497 if (cfg->fc_ifindex &&
David S. Miller38308472011-12-03 18:02:47 -05001498 (!rt->rt6i_dev ||
Thomas Graf86872cb2006-08-22 00:01:08 -07001499 rt->rt6i_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001501 if (cfg->fc_flags & RTF_GATEWAY &&
1502 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001504 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001506 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001507 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
Thomas Graf86872cb2006-08-22 00:01:08 -07001509 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 }
1511 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001512 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
1514 return err;
1515}
1516
1517/*
1518 * Handle redirects
1519 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001520struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001521 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001522 struct in6_addr gateway;
1523};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001525static struct rt6_info *__ip6_route_redirect(struct net *net,
1526 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001527 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001528 int flags)
1529{
David S. Miller4c9483b2011-03-12 16:22:43 -05001530 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001531 struct rt6_info *rt;
1532 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001533
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001535 * Get the "current" route for this destination and
1536 * check if the redirect has come from approriate router.
1537 *
1538 * RFC 2461 specifies that redirects should only be
1539 * accepted if they come from the nexthop to the target.
1540 * Due to the way the routes are chosen, this notion
1541 * is a bit fuzzy and one might need to check all possible
1542 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
Thomas Grafc71099a2006-08-04 23:20:06 -07001545 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001546 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001547restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001548 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001549 /*
1550 * Current route is on-link; redirect is always invalid.
1551 *
1552 * Seems, previous statement is not true. It could
1553 * be node, which looks for us as on-link (f.e. proxy ndisc)
1554 * But then router serving it might decide, that we should
1555 * know truth 8)8) --ANK (980726).
1556 */
1557 if (rt6_check_expired(rt))
1558 continue;
1559 if (!(rt->rt6i_flags & RTF_GATEWAY))
1560 continue;
David S. Miller4c9483b2011-03-12 16:22:43 -05001561 if (fl6->flowi6_oif != rt->rt6i_dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001562 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001563 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001564 continue;
1565 break;
1566 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001567
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001568 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001569 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001570 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001571out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001572 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001573
1574 read_unlock_bh(&table->tb6_lock);
1575
1576 return rt;
1577};
1578
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001579static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1580 const struct in6_addr *src,
1581 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001582 struct net_device *dev)
1583{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001584 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001585 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001586 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001587 .fl6 = {
1588 .flowi6_oif = dev->ifindex,
1589 .daddr = *dest,
1590 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001591 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001592 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001593
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001594 rdfl.gateway = *gateway;
Brian Haley86c36ce2009-10-07 13:58:01 -07001595
Thomas Grafadaa70b2006-10-13 15:01:03 -07001596 if (rt6_need_strict(dest))
1597 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001598
David S. Miller4c9483b2011-03-12 16:22:43 -05001599 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001600 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001601}
1602
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001603void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
1604 const struct in6_addr *saddr,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001605 struct neighbour *neigh, u8 *lladdr, int on_link)
1606{
1607 struct rt6_info *rt, *nrt = NULL;
1608 struct netevent_redirect netevent;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001609 struct net *net = dev_net(neigh->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001610
1611 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1612
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001613 if (rt == net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 if (net_ratelimit())
1615 printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
1616 "for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001617 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 }
1619
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 /*
1621 * We have finally decided to accept it.
1622 */
1623
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001624 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1626 NEIGH_UPDATE_F_OVERRIDE|
1627 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1628 NEIGH_UPDATE_F_ISROUTER))
1629 );
1630
1631 /*
1632 * Redirect received -> path was valid.
1633 * Look, redirects are sent only in response to data packets,
1634 * so that this nexthop apparently is reachable. --ANK
1635 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001636 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
1638 /* Duplicate redirect: silently ignore. */
Eric Dumazetf2c31e32011-07-29 19:00:53 +00001639 if (neigh == dst_get_neighbour_raw(&rt->dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 goto out;
1641
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001642 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001643 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 goto out;
1645
1646 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1647 if (on_link)
1648 nrt->rt6i_flags &= ~RTF_GATEWAY;
1649
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001650 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller69cce1d2011-07-17 23:09:49 -07001651 dst_set_neighbour(&nrt->dst, neigh_clone(neigh));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
Thomas Graf40e22e82006-08-22 00:00:45 -07001653 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 goto out;
1655
Changli Gaod8d1f302010-06-10 23:31:35 -07001656 netevent.old = &rt->dst;
1657 netevent.new = &nrt->dst;
Tom Tucker8d717402006-07-30 20:43:36 -07001658 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1659
David S. Miller38308472011-12-03 18:02:47 -05001660 if (rt->rt6i_flags & RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001661 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 return;
1663 }
1664
1665out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001666 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667}
1668
1669/*
1670 * Handle ICMP "packet too big" messages
1671 * i.e. Path MTU discovery
1672 */
1673
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001674static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr,
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001675 struct net *net, u32 pmtu, int ifindex)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676{
1677 struct rt6_info *rt, *nrt;
1678 int allfrag = 0;
Andrey Vagind3052b52010-12-11 15:20:11 +00001679again:
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001680 rt = rt6_lookup(net, daddr, saddr, ifindex, 0);
David S. Miller38308472011-12-03 18:02:47 -05001681 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 return;
1683
Andrey Vagind3052b52010-12-11 15:20:11 +00001684 if (rt6_check_expired(rt)) {
1685 ip6_del_rt(rt);
1686 goto again;
1687 }
1688
Changli Gaod8d1f302010-06-10 23:31:35 -07001689 if (pmtu >= dst_mtu(&rt->dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 goto out;
1691
1692 if (pmtu < IPV6_MIN_MTU) {
1693 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001694 * According to RFC2460, PMTU is set to the IPv6 Minimum Link
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 * MTU (1280) and a fragment header should always be included
1696 * after a node receiving Too Big message reporting PMTU is
1697 * less than the IPv6 Minimum Link MTU.
1698 */
1699 pmtu = IPV6_MIN_MTU;
1700 allfrag = 1;
1701 }
1702
1703 /* New mtu received -> path was valid.
1704 They are sent only in response to data packets,
1705 so that this nexthop apparently is reachable. --ANK
1706 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001707 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708
1709 /* Host route. If it is static, it would be better
1710 not to override it, but add new one, so that
1711 when cache entry will expire old pmtu
1712 would return automatically.
1713 */
1714 if (rt->rt6i_flags & RTF_CACHE) {
David S. Millerdefb3512010-12-08 21:16:57 -08001715 dst_metric_set(&rt->dst, RTAX_MTU, pmtu);
1716 if (allfrag) {
1717 u32 features = dst_metric(&rt->dst, RTAX_FEATURES);
1718 features |= RTAX_FEATURE_ALLFRAG;
1719 dst_metric_set(&rt->dst, RTAX_FEATURES, features);
1720 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001721 dst_set_expires(&rt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
1723 goto out;
1724 }
1725
1726 /* Network route.
1727 Two cases are possible:
1728 1. It is connected route. Action: COW
1729 2. It is gatewayed route or NONEXTHOP route. Action: clone it.
1730 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +00001731 if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001732 nrt = rt6_alloc_cow(rt, daddr, saddr);
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001733 else
1734 nrt = rt6_alloc_clone(rt, daddr);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001735
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001736 if (nrt) {
David S. Millerdefb3512010-12-08 21:16:57 -08001737 dst_metric_set(&nrt->dst, RTAX_MTU, pmtu);
1738 if (allfrag) {
1739 u32 features = dst_metric(&nrt->dst, RTAX_FEATURES);
1740 features |= RTAX_FEATURE_ALLFRAG;
1741 dst_metric_set(&nrt->dst, RTAX_FEATURES, features);
1742 }
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001743
1744 /* According to RFC 1981, detecting PMTU increase shouldn't be
1745 * happened within 5 mins, the recommended timer is 10 mins.
1746 * Here this route expiration time is set to ip6_rt_mtu_expires
1747 * which is 10 mins. After 10 mins the decreased pmtu is expired
1748 * and detecting PMTU increase will be automatically happened.
1749 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001750 dst_set_expires(&nrt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001751 nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
1752
Thomas Graf40e22e82006-08-22 00:00:45 -07001753 ip6_ins_rt(nrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001756 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757}
1758
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001759void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr,
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001760 struct net_device *dev, u32 pmtu)
1761{
1762 struct net *net = dev_net(dev);
1763
1764 /*
1765 * RFC 1981 states that a node "MUST reduce the size of the packets it
1766 * is sending along the path" that caused the Packet Too Big message.
1767 * Since it's not possible in the general case to determine which
1768 * interface was used to send the original packet, we update the MTU
1769 * on the interface that will be used to send future packets. We also
1770 * update the MTU on the interface that received the Packet Too Big in
1771 * case the original packet was forced out that interface with
1772 * SO_BINDTODEVICE or similar. This is the next best thing to the
1773 * correct behaviour, which would be to update the MTU on all
1774 * interfaces.
1775 */
1776 rt6_do_pmtu_disc(daddr, saddr, net, pmtu, 0);
1777 rt6_do_pmtu_disc(daddr, saddr, net, pmtu, dev->ifindex);
1778}
1779
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780/*
1781 * Misc support functions
1782 */
1783
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001784static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort,
1785 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001787 struct net *net = dev_net(ort->rt6i_dev);
David S. Miller5c1e6aa2011-04-28 14:13:38 -07001788 struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
David S. Miller957c6652011-06-24 15:25:00 -07001789 ort->dst.dev, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
1791 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001792 rt->dst.input = ort->dst.input;
1793 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001794 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001796 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001797 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001798 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001799 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 rt->rt6i_idev = ort->rt6i_idev;
1801 if (rt->rt6i_idev)
1802 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001803 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 rt->rt6i_expires = 0;
1805
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001806 rt->rt6i_gateway = ort->rt6i_gateway;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
1808 rt->rt6i_metric = 0;
1809
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810#ifdef CONFIG_IPV6_SUBTREES
1811 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1812#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001813 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001814 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 }
1816 return rt;
1817}
1818
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001819#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001820static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001821 const struct in6_addr *prefix, int prefixlen,
1822 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001823{
1824 struct fib6_node *fn;
1825 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001826 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001827
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001828 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001829 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001830 return NULL;
1831
1832 write_lock_bh(&table->tb6_lock);
1833 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001834 if (!fn)
1835 goto out;
1836
Changli Gaod8d1f302010-06-10 23:31:35 -07001837 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001838 if (rt->rt6i_dev->ifindex != ifindex)
1839 continue;
1840 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1841 continue;
1842 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1843 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001844 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001845 break;
1846 }
1847out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001848 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001849 return rt;
1850}
1851
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001852static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001853 const struct in6_addr *prefix, int prefixlen,
1854 const struct in6_addr *gwaddr, int ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001855 unsigned pref)
1856{
Thomas Graf86872cb2006-08-22 00:01:08 -07001857 struct fib6_config cfg = {
1858 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001859 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001860 .fc_ifindex = ifindex,
1861 .fc_dst_len = prefixlen,
1862 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1863 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001864 .fc_nlinfo.pid = 0,
1865 .fc_nlinfo.nlh = NULL,
1866 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001867 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001868
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001869 cfg.fc_dst = *prefix;
1870 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001871
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001872 /* We should treat it as a default route if prefix length is 0. */
1873 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001874 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001875
Thomas Graf86872cb2006-08-22 00:01:08 -07001876 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001877
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001878 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001879}
1880#endif
1881
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001882struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001883{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001885 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001887 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001888 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001889 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890
Thomas Grafc71099a2006-08-04 23:20:06 -07001891 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001892 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 if (dev == rt->rt6i_dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001894 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1896 break;
1897 }
1898 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001899 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001900 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 return rt;
1902}
1903
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001904struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001905 struct net_device *dev,
1906 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907{
Thomas Graf86872cb2006-08-22 00:01:08 -07001908 struct fib6_config cfg = {
1909 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001910 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001911 .fc_ifindex = dev->ifindex,
1912 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1913 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001914 .fc_nlinfo.pid = 0,
1915 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001916 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001917 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001919 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920
Thomas Graf86872cb2006-08-22 00:01:08 -07001921 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 return rt6_get_dflt_router(gwaddr, dev);
1924}
1925
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001926void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927{
1928 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001929 struct fib6_table *table;
1930
1931 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001932 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001933 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001934 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935
1936restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001937 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001938 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001940 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001941 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001942 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 goto restart;
1944 }
1945 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001946 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947}
1948
Daniel Lezcano55786892008-03-04 13:47:47 -08001949static void rtmsg_to_fib6_config(struct net *net,
1950 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001951 struct fib6_config *cfg)
1952{
1953 memset(cfg, 0, sizeof(*cfg));
1954
1955 cfg->fc_table = RT6_TABLE_MAIN;
1956 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1957 cfg->fc_metric = rtmsg->rtmsg_metric;
1958 cfg->fc_expires = rtmsg->rtmsg_info;
1959 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1960 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1961 cfg->fc_flags = rtmsg->rtmsg_flags;
1962
Daniel Lezcano55786892008-03-04 13:47:47 -08001963 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001964
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001965 cfg->fc_dst = rtmsg->rtmsg_dst;
1966 cfg->fc_src = rtmsg->rtmsg_src;
1967 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001968}
1969
Daniel Lezcano55786892008-03-04 13:47:47 -08001970int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971{
Thomas Graf86872cb2006-08-22 00:01:08 -07001972 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 struct in6_rtmsg rtmsg;
1974 int err;
1975
1976 switch(cmd) {
1977 case SIOCADDRT: /* Add a route */
1978 case SIOCDELRT: /* Delete a route */
1979 if (!capable(CAP_NET_ADMIN))
1980 return -EPERM;
1981 err = copy_from_user(&rtmsg, arg,
1982 sizeof(struct in6_rtmsg));
1983 if (err)
1984 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001985
Daniel Lezcano55786892008-03-04 13:47:47 -08001986 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001987
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 rtnl_lock();
1989 switch (cmd) {
1990 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001991 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 break;
1993 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001994 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 break;
1996 default:
1997 err = -EINVAL;
1998 }
1999 rtnl_unlock();
2000
2001 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002002 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003
2004 return -EINVAL;
2005}
2006
2007/*
2008 * Drop the packet on the floor
2009 */
2010
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002011static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002013 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002014 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002015 switch (ipstats_mib_noroutes) {
2016 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002017 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002018 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002019 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2020 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002021 break;
2022 }
2023 /* FALLTHROUGH */
2024 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002025 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2026 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002027 break;
2028 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002029 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 kfree_skb(skb);
2031 return 0;
2032}
2033
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002034static int ip6_pkt_discard(struct sk_buff *skb)
2035{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002036 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002037}
2038
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002039static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040{
Eric Dumazetadf30902009-06-02 05:19:30 +00002041 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002042 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043}
2044
David S. Miller6723ab52006-10-18 21:20:57 -07002045#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2046
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002047static int ip6_pkt_prohibit(struct sk_buff *skb)
2048{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002049 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002050}
2051
2052static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2053{
Eric Dumazetadf30902009-06-02 05:19:30 +00002054 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002055 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002056}
2057
David S. Miller6723ab52006-10-18 21:20:57 -07002058#endif
2059
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060/*
2061 * Allocate a dst for local (unicast / anycast) address.
2062 */
2063
2064struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2065 const struct in6_addr *addr,
2066 int anycast)
2067{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002068 struct net *net = dev_net(idev->dev);
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002069 struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
David S. Miller957c6652011-06-24 15:25:00 -07002070 net->loopback_dev, 0);
David S. Miller14deae42009-01-04 16:04:39 -08002071 struct neighbour *neigh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072
David S. Miller38308472011-12-03 18:02:47 -05002073 if (!rt) {
Ben Greear40385652010-11-08 12:33:48 +00002074 if (net_ratelimit())
2075 pr_warning("IPv6: Maximum number of routes reached,"
2076 " consider increasing route/max_size.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002078 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 in6_dev_hold(idev);
2081
David S. Miller11d53b42011-06-24 15:23:34 -07002082 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002083 rt->dst.input = ip6_input;
2084 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002086 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
2088 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002089 if (anycast)
2090 rt->rt6i_flags |= RTF_ANYCAST;
2091 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller04a6f442011-12-03 18:29:30 -05002093 neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, rt->rt6i_dev);
David S. Miller14deae42009-01-04 16:04:39 -08002094 if (IS_ERR(neigh)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002095 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -08002096
David S. Miller29546a62011-03-03 12:10:37 -08002097 return ERR_CAST(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 }
David S. Miller69cce1d2011-07-17 23:09:49 -07002099 dst_set_neighbour(&rt->dst, neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002101 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002103 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104
Changli Gaod8d1f302010-06-10 23:31:35 -07002105 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106
2107 return rt;
2108}
2109
Daniel Walterc3968a82011-04-13 21:10:57 +00002110int ip6_route_get_saddr(struct net *net,
2111 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002112 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002113 unsigned int prefs,
2114 struct in6_addr *saddr)
2115{
2116 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2117 int err = 0;
2118 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002119 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002120 else
2121 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2122 daddr, prefs, saddr);
2123 return err;
2124}
2125
2126/* remove deleted ip from prefsrc entries */
2127struct arg_dev_net_ip {
2128 struct net_device *dev;
2129 struct net *net;
2130 struct in6_addr *addr;
2131};
2132
2133static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2134{
2135 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2136 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2137 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2138
David S. Miller38308472011-12-03 18:02:47 -05002139 if (((void *)rt->rt6i_dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002140 rt != net->ipv6.ip6_null_entry &&
2141 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2142 /* remove prefsrc entry */
2143 rt->rt6i_prefsrc.plen = 0;
2144 }
2145 return 0;
2146}
2147
2148void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2149{
2150 struct net *net = dev_net(ifp->idev->dev);
2151 struct arg_dev_net_ip adni = {
2152 .dev = ifp->idev->dev,
2153 .net = net,
2154 .addr = &ifp->addr,
2155 };
2156 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2157}
2158
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002159struct arg_dev_net {
2160 struct net_device *dev;
2161 struct net *net;
2162};
2163
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164static int fib6_ifdown(struct rt6_info *rt, void *arg)
2165{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002166 const struct arg_dev_net *adn = arg;
2167 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002168
David S. Miller38308472011-12-03 18:02:47 -05002169 if ((rt->rt6i_dev == dev || !dev) &&
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002170 rt != adn->net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 RT6_TRACE("deleted by ifdown %p\n", rt);
2172 return -1;
2173 }
2174 return 0;
2175}
2176
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002177void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002179 struct arg_dev_net adn = {
2180 .dev = dev,
2181 .net = net,
2182 };
2183
2184 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002185 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186}
2187
2188struct rt6_mtu_change_arg
2189{
2190 struct net_device *dev;
2191 unsigned mtu;
2192};
2193
2194static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2195{
2196 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2197 struct inet6_dev *idev;
2198
2199 /* In IPv6 pmtu discovery is not optional,
2200 so that RTAX_MTU lock cannot disable it.
2201 We still use this lock to block changes
2202 caused by addrconf/ndisc.
2203 */
2204
2205 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002206 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 return 0;
2208
2209 /* For administrative MTU increase, there is no way to discover
2210 IPv6 PMTU increase, so PMTU increase should be updated here.
2211 Since RFC 1981 doesn't include administrative MTU increase
2212 update PMTU increase is a MUST. (i.e. jumbo frame)
2213 */
2214 /*
2215 If new MTU is less than route PMTU, this new MTU will be the
2216 lowest MTU in the path, update the route PMTU to reflect PMTU
2217 decreases; if new MTU is greater than route PMTU, and the
2218 old MTU is the lowest MTU in the path, update the route PMTU
2219 to reflect the increase. In this case if the other nodes' MTU
2220 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2221 PMTU discouvery.
2222 */
2223 if (rt->rt6i_dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002224 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2225 (dst_mtu(&rt->dst) >= arg->mtu ||
2226 (dst_mtu(&rt->dst) < arg->mtu &&
2227 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002228 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002229 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 return 0;
2231}
2232
2233void rt6_mtu_change(struct net_device *dev, unsigned mtu)
2234{
Thomas Grafc71099a2006-08-04 23:20:06 -07002235 struct rt6_mtu_change_arg arg = {
2236 .dev = dev,
2237 .mtu = mtu,
2238 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002240 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241}
2242
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002243static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002244 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002245 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002246 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002247 [RTA_PRIORITY] = { .type = NLA_U32 },
2248 [RTA_METRICS] = { .type = NLA_NESTED },
2249};
2250
2251static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2252 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253{
Thomas Graf86872cb2006-08-22 00:01:08 -07002254 struct rtmsg *rtm;
2255 struct nlattr *tb[RTA_MAX+1];
2256 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257
Thomas Graf86872cb2006-08-22 00:01:08 -07002258 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2259 if (err < 0)
2260 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261
Thomas Graf86872cb2006-08-22 00:01:08 -07002262 err = -EINVAL;
2263 rtm = nlmsg_data(nlh);
2264 memset(cfg, 0, sizeof(*cfg));
2265
2266 cfg->fc_table = rtm->rtm_table;
2267 cfg->fc_dst_len = rtm->rtm_dst_len;
2268 cfg->fc_src_len = rtm->rtm_src_len;
2269 cfg->fc_flags = RTF_UP;
2270 cfg->fc_protocol = rtm->rtm_protocol;
2271
2272 if (rtm->rtm_type == RTN_UNREACHABLE)
2273 cfg->fc_flags |= RTF_REJECT;
2274
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002275 if (rtm->rtm_type == RTN_LOCAL)
2276 cfg->fc_flags |= RTF_LOCAL;
2277
Thomas Graf86872cb2006-08-22 00:01:08 -07002278 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2279 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002280 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002281
2282 if (tb[RTA_GATEWAY]) {
2283 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2284 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002286
2287 if (tb[RTA_DST]) {
2288 int plen = (rtm->rtm_dst_len + 7) >> 3;
2289
2290 if (nla_len(tb[RTA_DST]) < plen)
2291 goto errout;
2292
2293 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002295
2296 if (tb[RTA_SRC]) {
2297 int plen = (rtm->rtm_src_len + 7) >> 3;
2298
2299 if (nla_len(tb[RTA_SRC]) < plen)
2300 goto errout;
2301
2302 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002304
Daniel Walterc3968a82011-04-13 21:10:57 +00002305 if (tb[RTA_PREFSRC])
2306 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2307
Thomas Graf86872cb2006-08-22 00:01:08 -07002308 if (tb[RTA_OIF])
2309 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2310
2311 if (tb[RTA_PRIORITY])
2312 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2313
2314 if (tb[RTA_METRICS]) {
2315 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2316 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002318
2319 if (tb[RTA_TABLE])
2320 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2321
2322 err = 0;
2323errout:
2324 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325}
2326
Thomas Grafc127ea22007-03-22 11:58:32 -07002327static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328{
Thomas Graf86872cb2006-08-22 00:01:08 -07002329 struct fib6_config cfg;
2330 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331
Thomas Graf86872cb2006-08-22 00:01:08 -07002332 err = rtm_to_fib6_config(skb, nlh, &cfg);
2333 if (err < 0)
2334 return err;
2335
2336 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337}
2338
Thomas Grafc127ea22007-03-22 11:58:32 -07002339static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340{
Thomas Graf86872cb2006-08-22 00:01:08 -07002341 struct fib6_config cfg;
2342 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343
Thomas Graf86872cb2006-08-22 00:01:08 -07002344 err = rtm_to_fib6_config(skb, nlh, &cfg);
2345 if (err < 0)
2346 return err;
2347
2348 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349}
2350
Thomas Graf339bf982006-11-10 14:10:15 -08002351static inline size_t rt6_nlmsg_size(void)
2352{
2353 return NLMSG_ALIGN(sizeof(struct rtmsg))
2354 + nla_total_size(16) /* RTA_SRC */
2355 + nla_total_size(16) /* RTA_DST */
2356 + nla_total_size(16) /* RTA_GATEWAY */
2357 + nla_total_size(16) /* RTA_PREFSRC */
2358 + nla_total_size(4) /* RTA_TABLE */
2359 + nla_total_size(4) /* RTA_IIF */
2360 + nla_total_size(4) /* RTA_OIF */
2361 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002362 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002363 + nla_total_size(sizeof(struct rta_cacheinfo));
2364}
2365
Brian Haley191cd582008-08-14 15:33:21 -07002366static int rt6_fill_node(struct net *net,
2367 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002368 struct in6_addr *dst, struct in6_addr *src,
2369 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002370 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371{
2372 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002373 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002374 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002375 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002376 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
2378 if (prefix) { /* user wants prefix routes only */
2379 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2380 /* success since this is not a prefix route */
2381 return 1;
2382 }
2383 }
2384
Thomas Graf2d7202b2006-08-22 00:01:27 -07002385 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002386 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002387 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002388
2389 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 rtm->rtm_family = AF_INET6;
2391 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2392 rtm->rtm_src_len = rt->rt6i_src.plen;
2393 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002394 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002395 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002396 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002397 table = RT6_TABLE_UNSPEC;
2398 rtm->rtm_table = table;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002399 NLA_PUT_U32(skb, RTA_TABLE, table);
David S. Miller38308472011-12-03 18:02:47 -05002400 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002402 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002403 rtm->rtm_type = RTN_LOCAL;
David S. Miller38308472011-12-03 18:02:47 -05002404 else if (rt->rt6i_dev && (rt->rt6i_dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 rtm->rtm_type = RTN_LOCAL;
2406 else
2407 rtm->rtm_type = RTN_UNICAST;
2408 rtm->rtm_flags = 0;
2409 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2410 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002411 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412 rtm->rtm_protocol = RTPROT_REDIRECT;
2413 else if (rt->rt6i_flags & RTF_ADDRCONF)
2414 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002415 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416 rtm->rtm_protocol = RTPROT_RA;
2417
David S. Miller38308472011-12-03 18:02:47 -05002418 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419 rtm->rtm_flags |= RTM_F_CLONED;
2420
2421 if (dst) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002422 NLA_PUT(skb, RTA_DST, 16, dst);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002423 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424 } else if (rtm->rtm_dst_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002425 NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426#ifdef CONFIG_IPV6_SUBTREES
2427 if (src) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002428 NLA_PUT(skb, RTA_SRC, 16, src);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002429 rtm->rtm_src_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430 } else if (rtm->rtm_src_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002431 NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002433 if (iif) {
2434#ifdef CONFIG_IPV6_MROUTE
2435 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002436 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002437 if (err <= 0) {
2438 if (!nowait) {
2439 if (err == 0)
2440 return 0;
2441 goto nla_put_failure;
2442 } else {
2443 if (err == -EMSGSIZE)
2444 goto nla_put_failure;
2445 }
2446 }
2447 } else
2448#endif
2449 NLA_PUT_U32(skb, RTA_IIF, iif);
2450 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 struct in6_addr saddr_buf;
Daniel Walterc3968a82011-04-13 21:10:57 +00002452 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002453 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002455
Daniel Walterc3968a82011-04-13 21:10:57 +00002456 if (rt->rt6i_prefsrc.plen) {
2457 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002458 saddr_buf = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002459 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
2460 }
2461
David S. Millerdefb3512010-12-08 21:16:57 -08002462 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002463 goto nla_put_failure;
2464
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002465 rcu_read_lock();
2466 n = dst_get_neighbour(&rt->dst);
2467 if (n)
2468 NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
2469 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002470
Changli Gaod8d1f302010-06-10 23:31:35 -07002471 if (rt->dst.dev)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002472 NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
2473
2474 NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
Thomas Grafe3703b32006-11-27 09:27:07 -08002475
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002476 if (!(rt->rt6i_flags & RTF_EXPIRES))
2477 expires = 0;
2478 else if (rt->rt6i_expires - jiffies < INT_MAX)
2479 expires = rt->rt6i_expires - jiffies;
2480 else
2481 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002482
Changli Gaod8d1f302010-06-10 23:31:35 -07002483 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0,
2484 expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002485 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486
Thomas Graf2d7202b2006-08-22 00:01:27 -07002487 return nlmsg_end(skb, nlh);
2488
2489nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002490 nlmsg_cancel(skb, nlh);
2491 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492}
2493
Patrick McHardy1b43af52006-08-10 23:11:17 -07002494int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495{
2496 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2497 int prefix;
2498
Thomas Graf2d7202b2006-08-22 00:01:27 -07002499 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2500 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2502 } else
2503 prefix = 0;
2504
Brian Haley191cd582008-08-14 15:33:21 -07002505 return rt6_fill_node(arg->net,
2506 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002508 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509}
2510
Thomas Grafc127ea22007-03-22 11:58:32 -07002511static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002513 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002514 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002516 struct sk_buff *skb;
2517 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002518 struct flowi6 fl6;
Thomas Grafab364a62006-08-22 00:01:47 -07002519 int err, iif = 0;
2520
2521 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2522 if (err < 0)
2523 goto errout;
2524
2525 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002526 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002527
2528 if (tb[RTA_SRC]) {
2529 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2530 goto errout;
2531
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002532 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002533 }
2534
2535 if (tb[RTA_DST]) {
2536 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2537 goto errout;
2538
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002539 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002540 }
2541
2542 if (tb[RTA_IIF])
2543 iif = nla_get_u32(tb[RTA_IIF]);
2544
2545 if (tb[RTA_OIF])
David S. Miller4c9483b2011-03-12 16:22:43 -05002546 fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002547
2548 if (iif) {
2549 struct net_device *dev;
Daniel Lezcano55786892008-03-04 13:47:47 -08002550 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002551 if (!dev) {
2552 err = -ENODEV;
2553 goto errout;
2554 }
2555 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556
2557 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002558 if (!skb) {
Thomas Grafab364a62006-08-22 00:01:47 -07002559 err = -ENOBUFS;
2560 goto errout;
2561 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562
2563 /* Reserve room for dummy headers, this skb can pass
2564 through good chunk of routing engine.
2565 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002566 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2568
David S. Miller4c9483b2011-03-12 16:22:43 -05002569 rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6);
Changli Gaod8d1f302010-06-10 23:31:35 -07002570 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571
David S. Miller4c9483b2011-03-12 16:22:43 -05002572 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002574 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002576 kfree_skb(skb);
2577 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 }
2579
Daniel Lezcano55786892008-03-04 13:47:47 -08002580 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002581errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583}
2584
Thomas Graf86872cb2006-08-22 00:01:08 -07002585void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586{
2587 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002588 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002589 u32 seq;
2590 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002592 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002593 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002594
Thomas Graf339bf982006-11-10 14:10:15 -08002595 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002596 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002597 goto errout;
2598
Brian Haley191cd582008-08-14 15:33:21 -07002599 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002600 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002601 if (err < 0) {
2602 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2603 WARN_ON(err == -EMSGSIZE);
2604 kfree_skb(skb);
2605 goto errout;
2606 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002607 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2608 info->nlh, gfp_any());
2609 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002610errout:
2611 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002612 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613}
2614
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002615static int ip6_route_dev_notify(struct notifier_block *this,
2616 unsigned long event, void *data)
2617{
2618 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002619 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002620
2621 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002622 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002623 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2624#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002625 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002626 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002627 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002628 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2629#endif
2630 }
2631
2632 return NOTIFY_OK;
2633}
2634
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635/*
2636 * /proc
2637 */
2638
2639#ifdef CONFIG_PROC_FS
2640
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641struct rt6_proc_arg
2642{
2643 char *buffer;
2644 int offset;
2645 int length;
2646 int skip;
2647 int len;
2648};
2649
2650static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2651{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002652 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002653 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002655 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656
2657#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002658 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002660 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002662 rcu_read_lock();
David S. Miller69cce1d2011-07-17 23:09:49 -07002663 n = dst_get_neighbour(&rt->dst);
2664 if (n) {
2665 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002667 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002669 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002670 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002671 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2672 rt->dst.__use, rt->rt6i_flags,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002673 rt->rt6i_dev ? rt->rt6i_dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002674 return 0;
2675}
2676
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002677static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002679 struct net *net = (struct net *)m->private;
2680 fib6_clean_all(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002681 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682}
2683
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002684static int ipv6_route_open(struct inode *inode, struct file *file)
2685{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002686 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002687}
2688
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002689static const struct file_operations ipv6_route_proc_fops = {
2690 .owner = THIS_MODULE,
2691 .open = ipv6_route_open,
2692 .read = seq_read,
2693 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002694 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002695};
2696
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2698{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002699 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002701 net->ipv6.rt6_stats->fib_nodes,
2702 net->ipv6.rt6_stats->fib_route_nodes,
2703 net->ipv6.rt6_stats->fib_rt_alloc,
2704 net->ipv6.rt6_stats->fib_rt_entries,
2705 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002706 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002707 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708
2709 return 0;
2710}
2711
2712static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2713{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002714 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002715}
2716
Arjan van de Ven9a321442007-02-12 00:55:35 -08002717static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718 .owner = THIS_MODULE,
2719 .open = rt6_stats_seq_open,
2720 .read = seq_read,
2721 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002722 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723};
2724#endif /* CONFIG_PROC_FS */
2725
2726#ifdef CONFIG_SYSCTL
2727
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002729int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 void __user *buffer, size_t *lenp, loff_t *ppos)
2731{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002732 struct net *net;
2733 int delay;
2734 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002736
2737 net = (struct net *)ctl->extra1;
2738 delay = net->ipv6.sysctl.flush_delay;
2739 proc_dointvec(ctl, write, buffer, lenp, ppos);
2740 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2741 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742}
2743
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002744ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002745 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002747 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748 .maxlen = sizeof(int),
Dave Jones89c8b3a2005-04-28 12:11:49 -07002749 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002750 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 },
2752 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002754 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755 .maxlen = sizeof(int),
2756 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002757 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 },
2759 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002761 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 .maxlen = sizeof(int),
2763 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002764 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 },
2766 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002768 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 .maxlen = sizeof(int),
2770 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002771 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 },
2773 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002775 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 .maxlen = sizeof(int),
2777 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002778 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 },
2780 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002782 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 .maxlen = sizeof(int),
2784 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002785 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 },
2787 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002789 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 .maxlen = sizeof(int),
2791 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002792 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 },
2794 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002796 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 .maxlen = sizeof(int),
2798 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002799 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 },
2801 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002803 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 .maxlen = sizeof(int),
2805 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002806 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 },
2808 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002810 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 .maxlen = sizeof(int),
2812 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002813 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002815 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816};
2817
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002818struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002819{
2820 struct ctl_table *table;
2821
2822 table = kmemdup(ipv6_route_table_template,
2823 sizeof(ipv6_route_table_template),
2824 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002825
2826 if (table) {
2827 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002828 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002829 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002830 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2831 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2832 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2833 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2834 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2835 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2836 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002837 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002838 }
2839
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002840 return table;
2841}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842#endif
2843
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002844static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002845{
Pavel Emelyanov633d4242008-04-21 14:25:23 -07002846 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002847
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002848 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2849 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002850
Eric Dumazetfc66f952010-10-08 06:37:34 +00002851 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2852 goto out_ip6_dst_ops;
2853
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002854 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2855 sizeof(*net->ipv6.ip6_null_entry),
2856 GFP_KERNEL);
2857 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002858 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002859 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002860 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002861 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002862 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2863 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002864
2865#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2866 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2867 sizeof(*net->ipv6.ip6_prohibit_entry),
2868 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002869 if (!net->ipv6.ip6_prohibit_entry)
2870 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002871 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002872 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002873 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002874 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2875 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002876
2877 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2878 sizeof(*net->ipv6.ip6_blk_hole_entry),
2879 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002880 if (!net->ipv6.ip6_blk_hole_entry)
2881 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002882 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002883 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002884 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002885 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2886 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002887#endif
2888
Peter Zijlstrab339a472008-10-07 14:15:00 -07002889 net->ipv6.sysctl.flush_delay = 0;
2890 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2891 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2892 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2893 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2894 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2895 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2896 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2897
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002898#ifdef CONFIG_PROC_FS
2899 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2900 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2901#endif
Benjamin Thery6891a342008-03-04 13:49:47 -08002902 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2903
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002904 ret = 0;
2905out:
2906 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002907
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002908#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2909out_ip6_prohibit_entry:
2910 kfree(net->ipv6.ip6_prohibit_entry);
2911out_ip6_null_entry:
2912 kfree(net->ipv6.ip6_null_entry);
2913#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002914out_ip6_dst_entries:
2915 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002916out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002917 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002918}
2919
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002920static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002921{
2922#ifdef CONFIG_PROC_FS
2923 proc_net_remove(net, "ipv6_route");
2924 proc_net_remove(net, "rt6_stats");
2925#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002926 kfree(net->ipv6.ip6_null_entry);
2927#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2928 kfree(net->ipv6.ip6_prohibit_entry);
2929 kfree(net->ipv6.ip6_blk_hole_entry);
2930#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002931 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002932}
2933
2934static struct pernet_operations ip6_route_net_ops = {
2935 .init = ip6_route_net_init,
2936 .exit = ip6_route_net_exit,
2937};
2938
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002939static struct notifier_block ip6_route_dev_notifier = {
2940 .notifier_call = ip6_route_dev_notify,
2941 .priority = 0,
2942};
2943
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002944int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002946 int ret;
2947
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002948 ret = -ENOMEM;
2949 ip6_dst_ops_template.kmem_cachep =
2950 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
2951 SLAB_HWCACHE_ALIGN, NULL);
2952 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08002953 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07002954
Eric Dumazetfc66f952010-10-08 06:37:34 +00002955 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002956 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002957 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002958
Eric Dumazetfc66f952010-10-08 06:37:34 +00002959 ret = register_pernet_subsys(&ip6_route_net_ops);
2960 if (ret)
2961 goto out_dst_entries;
2962
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07002963 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
2964
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002965 /* Registering of the loopback is done before this portion of code,
2966 * the loopback reference in rt6_info will not be taken, do it
2967 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07002968 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002969 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2970 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002971 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002972 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002973 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002974 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2975 #endif
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002976 ret = fib6_init();
2977 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002978 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002979
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002980 ret = xfrm6_init();
2981 if (ret)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002982 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08002983
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002984 ret = fib6_rules_init();
2985 if (ret)
2986 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08002987
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002988 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00002989 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
2990 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
2991 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002992 goto fib6_rules_init;
2993
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002994 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002995 if (ret)
2996 goto fib6_rules_init;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002997
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002998out:
2999 return ret;
3000
3001fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003002 fib6_rules_cleanup();
3003xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003004 xfrm6_fini();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003005out_fib6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003006 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003007out_register_subsys:
3008 unregister_pernet_subsys(&ip6_route_net_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003009out_dst_entries:
3010 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003011out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003012 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003013 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003014}
3015
3016void ip6_route_cleanup(void)
3017{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003018 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Graf101367c2006-08-04 03:39:02 -07003019 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003022 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003023 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003024 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003025}