blob: 545b1526c1434e0b6186316912602f7e37376a15 [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
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020047#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <net/snmp.h>
49#include <net/ipv6.h>
50#include <net/ip6_fib.h>
51#include <net/ip6_route.h>
52#include <net/ndisc.h>
53#include <net/addrconf.h>
54#include <net/tcp.h>
55#include <linux/rtnetlink.h>
56#include <net/dst.h>
57#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070058#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070059#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61#include <asm/uaccess.h>
62
63#ifdef CONFIG_SYSCTL
64#include <linux/sysctl.h>
65#endif
66
Gao feng1716a962012-04-06 00:13:10 +000067static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +000068 const struct in6_addr *dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080070static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000071static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072static struct dst_entry *ip6_negative_advice(struct dst_entry *);
73static void ip6_dst_destroy(struct dst_entry *);
74static void ip6_dst_ifdown(struct dst_entry *,
75 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080076static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78static int ip6_pkt_discard(struct sk_buff *skb);
79static int ip6_pkt_discard_out(struct sk_buff *skb);
80static void ip6_link_failure(struct sk_buff *skb);
81static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
David S. Miller6e157b62012-07-12 00:05:02 -070082static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080084#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080085static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000086 const struct in6_addr *prefix, int prefixlen,
87 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +000088 unsigned int pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080089static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000090 const struct in6_addr *prefix, int prefixlen,
91 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080092#endif
93
David S. Miller06582542011-01-27 14:58:42 -080094static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
95{
96 struct rt6_info *rt = (struct rt6_info *) dst;
97 struct inet_peer *peer;
98 u32 *p = NULL;
99
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000100 if (!(rt->dst.flags & DST_HOST))
101 return NULL;
102
David S. Millerfbfe95a2012-06-08 23:24:18 -0700103 peer = rt6_get_peer_create(rt);
David S. Miller06582542011-01-27 14:58:42 -0800104 if (peer) {
105 u32 *old_p = __DST_METRICS_PTR(old);
106 unsigned long prev, new;
107
108 p = peer->metrics;
109 if (inet_metrics_new(peer))
110 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
111
112 new = (unsigned long) p;
113 prev = cmpxchg(&dst->_metrics, old, new);
114
115 if (prev != old) {
116 p = __DST_METRICS_PTR(prev);
117 if (prev & DST_METRICS_READ_ONLY)
118 p = NULL;
119 }
120 }
121 return p;
122}
123
David S. Millerf894cbf2012-07-02 21:52:24 -0700124static inline const void *choose_neigh_daddr(struct rt6_info *rt,
125 struct sk_buff *skb,
126 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500127{
128 struct in6_addr *p = &rt->rt6i_gateway;
129
David S. Millera7563f32012-01-26 16:29:16 -0500130 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500131 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700132 else if (skb)
133 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500134 return daddr;
135}
136
David S. Millerf894cbf2012-07-02 21:52:24 -0700137static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
138 struct sk_buff *skb,
139 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700140{
David S. Miller39232972012-01-26 15:22:32 -0500141 struct rt6_info *rt = (struct rt6_info *) dst;
142 struct neighbour *n;
143
David S. Millerf894cbf2012-07-02 21:52:24 -0700144 daddr = choose_neigh_daddr(rt, skb, daddr);
David S. Miller39232972012-01-26 15:22:32 -0500145 n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500146 if (n)
147 return n;
148 return neigh_create(&nd_tbl, daddr, dst->dev);
149}
150
David S. Miller8ade06c2011-12-29 18:51:57 -0500151static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
David S. Millerf83c7792011-12-28 15:41:23 -0500152{
David S. Miller8ade06c2011-12-29 18:51:57 -0500153 struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
154 if (!n) {
155 n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
156 if (IS_ERR(n))
157 return PTR_ERR(n);
158 }
David S. Miller97cac082012-07-02 22:43:47 -0700159 rt->n = n;
David S. Millerf83c7792011-12-28 15:41:23 -0500160
161 return 0;
David S. Millerd3aaeb32011-07-18 00:40:17 -0700162}
163
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800164static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 .family = AF_INET6,
Harvey Harrison09640e632009-02-01 00:45:17 -0800166 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 .gc = ip6_dst_gc,
168 .gc_thresh = 1024,
169 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800170 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000171 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800172 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .destroy = ip6_dst_destroy,
174 .ifdown = ip6_dst_ifdown,
175 .negative_advice = ip6_negative_advice,
176 .link_failure = ip6_link_failure,
177 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700178 .redirect = rt6_do_redirect,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700179 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700180 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181};
182
Steffen Klassertebb762f2011-11-23 02:12:51 +0000183static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800184{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000185 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
186
187 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800188}
189
David S. Miller14e50e52007-05-24 18:17:54 -0700190static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
191{
192}
193
Held Bernhard0972ddb2011-04-24 22:07:32 +0000194static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
195 unsigned long old)
196{
197 return NULL;
198}
199
David S. Miller14e50e52007-05-24 18:17:54 -0700200static struct dst_ops ip6_dst_blackhole_ops = {
201 .family = AF_INET6,
Harvey Harrison09640e632009-02-01 00:45:17 -0800202 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700203 .destroy = ip6_dst_destroy,
204 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000205 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800206 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700207 .update_pmtu = ip6_rt_blackhole_update_pmtu,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000208 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700209 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700210};
211
David S. Miller62fa8a82011-01-26 20:51:05 -0800212static const u32 ip6_template_metrics[RTAX_MAX] = {
213 [RTAX_HOPLIMIT - 1] = 255,
214};
215
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800216static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700217 .dst = {
218 .__refcnt = ATOMIC_INIT(1),
219 .__use = 1,
220 .obsolete = -1,
221 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700222 .input = ip6_pkt_discard,
223 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 },
225 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700226 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .rt6i_metric = ~(u32) 0,
228 .rt6i_ref = ATOMIC_INIT(1),
229};
230
Thomas Graf101367c2006-08-04 03:39:02 -0700231#ifdef CONFIG_IPV6_MULTIPLE_TABLES
232
David S. Miller6723ab52006-10-18 21:20:57 -0700233static int ip6_pkt_prohibit(struct sk_buff *skb);
234static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700235
Adrian Bunk280a34c2008-04-21 02:29:32 -0700236static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700237 .dst = {
238 .__refcnt = ATOMIC_INIT(1),
239 .__use = 1,
240 .obsolete = -1,
241 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700242 .input = ip6_pkt_prohibit,
243 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700244 },
245 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700246 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700247 .rt6i_metric = ~(u32) 0,
248 .rt6i_ref = ATOMIC_INIT(1),
249};
250
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800251static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700252 .dst = {
253 .__refcnt = ATOMIC_INIT(1),
254 .__use = 1,
255 .obsolete = -1,
256 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700257 .input = dst_discard,
258 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700259 },
260 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700261 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700262 .rt6i_metric = ~(u32) 0,
263 .rt6i_ref = ATOMIC_INIT(1),
264};
265
266#endif
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700269static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700270 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700271 int flags,
272 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
David S. Miller97bab732012-06-09 22:36:36 -0700274 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
275 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700276
David S. Miller97bab732012-06-09 22:36:36 -0700277 if (rt) {
Steffen Klasserta2de86f2012-07-05 03:18:28 +0000278 memset(&rt->n, 0,
David S. Miller38308472011-12-03 18:02:47 -0500279 sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller8b96d222012-06-11 02:01:56 -0700280 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
David S. Miller97bab732012-06-09 22:36:36 -0700281 }
David S. Millercf911662011-04-28 14:31:47 -0700282 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283}
284
285static void ip6_dst_destroy(struct dst_entry *dst)
286{
287 struct rt6_info *rt = (struct rt6_info *)dst;
288 struct inet6_dev *idev = rt->rt6i_idev;
289
David S. Miller97cac082012-07-02 22:43:47 -0700290 if (rt->n)
291 neigh_release(rt->n);
292
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000293 if (!(rt->dst.flags & DST_HOST))
294 dst_destroy_metrics_generic(dst);
295
David S. Miller38308472011-12-03 18:02:47 -0500296 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 rt->rt6i_idev = NULL;
298 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900299 }
Gao feng1716a962012-04-06 00:13:10 +0000300
301 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
302 dst_release(dst->from);
303
David S. Miller97bab732012-06-09 22:36:36 -0700304 if (rt6_has_peer(rt)) {
305 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800306 inet_putpeer(peer);
307 }
308}
309
David S. Miller6431cbc2011-02-07 20:38:06 -0800310static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
311
312static u32 rt6_peer_genid(void)
313{
314 return atomic_read(&__rt6_peer_genid);
315}
316
David S. Millerb3419362010-11-30 12:27:11 -0800317void rt6_bind_peer(struct rt6_info *rt, int create)
318{
David S. Miller97bab732012-06-09 22:36:36 -0700319 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800320 struct inet_peer *peer;
321
David S. Miller97bab732012-06-09 22:36:36 -0700322 base = inetpeer_base_ptr(rt->_rt6i_peer);
323 if (!base)
324 return;
325
326 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700327 if (peer) {
328 if (!rt6_set_peer(rt, peer))
329 inet_putpeer(peer);
330 else
331 rt->rt6i_peer_genid = rt6_peer_genid();
332 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
335static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
336 int how)
337{
338 struct rt6_info *rt = (struct rt6_info *)dst;
339 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800340 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900341 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
David S. Miller97cac082012-07-02 22:43:47 -0700343 if (dev != loopback_dev) {
344 if (idev && idev->dev == dev) {
345 struct inet6_dev *loopback_idev =
346 in6_dev_get(loopback_dev);
347 if (loopback_idev) {
348 rt->rt6i_idev = loopback_idev;
349 in6_dev_put(idev);
350 }
351 }
352 if (rt->n && rt->n->dev == dev) {
353 rt->n->dev = loopback_dev;
354 dev_hold(loopback_dev);
355 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 }
357 }
358}
359
Eric Dumazeta50feda2012-05-18 18:57:34 +0000360static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
Gao feng1716a962012-04-06 00:13:10 +0000362 struct rt6_info *ort = NULL;
363
364 if (rt->rt6i_flags & RTF_EXPIRES) {
365 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000366 return true;
Gao feng1716a962012-04-06 00:13:10 +0000367 } else if (rt->dst.from) {
368 ort = (struct rt6_info *) rt->dst.from;
369 return (ort->rt6i_flags & RTF_EXPIRES) &&
370 time_after(jiffies, ort->dst.expires);
371 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000372 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373}
374
Eric Dumazeta50feda2012-05-18 18:57:34 +0000375static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700376{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000377 return ipv6_addr_type(daddr) &
378 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700379}
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700382 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 */
384
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800385static inline struct rt6_info *rt6_device_match(struct net *net,
386 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000387 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700389 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
391 struct rt6_info *local = NULL;
392 struct rt6_info *sprt;
393
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900394 if (!oif && ipv6_addr_any(saddr))
395 goto out;
396
Changli Gaod8d1f302010-06-10 23:31:35 -0700397 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500398 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900399
400 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 if (dev->ifindex == oif)
402 return sprt;
403 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500404 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700406 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900408 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 local->rt6i_idev->dev->ifindex == oif))
410 continue;
411 }
412 local = sprt;
413 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900414 } else {
415 if (ipv6_chk_addr(net, saddr, dev,
416 flags & RT6_LOOKUP_F_IFACE))
417 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900419 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900421 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 if (local)
423 return local;
424
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700425 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800426 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900428out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 return rt;
430}
431
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800432#ifdef CONFIG_IPV6_ROUTER_PREF
433static void rt6_probe(struct rt6_info *rt)
434{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000435 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800436 /*
437 * Okay, this does not seem to be appropriate
438 * for now, however, we need to check if it
439 * is really so; aka Router Reachability Probing.
440 *
441 * Router Reachability Probe MUST be rate-limited
442 * to no more than one per minute.
443 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000444 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700445 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800446 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000447 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800448 read_lock_bh(&neigh->lock);
449 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e163562006-03-20 17:05:47 -0800450 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800451 struct in6_addr mcaddr;
452 struct in6_addr *target;
453
454 neigh->updated = jiffies;
455 read_unlock_bh(&neigh->lock);
456
457 target = (struct in6_addr *)&neigh->primary_key;
458 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500459 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000460 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800461 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000462 }
463out:
464 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800465}
466#else
467static inline void rt6_probe(struct rt6_info *rt)
468{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800469}
470#endif
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800473 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700475static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
David S. Millerd1918542011-12-28 20:19:20 -0500477 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700478 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800479 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700480 if ((dev->flags & IFF_LOOPBACK) &&
481 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
482 return 1;
483 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
Dave Jonesb6f99a22007-03-22 12:27:49 -0700486static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000488 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800489 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000490
491 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700492 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700493 if (rt->rt6i_flags & RTF_NONEXTHOP ||
494 !(rt->rt6i_flags & RTF_GATEWAY))
495 m = 1;
496 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800497 read_lock_bh(&neigh->lock);
498 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700499 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800500#ifdef CONFIG_IPV6_ROUTER_PREF
501 else if (neigh->nud_state & NUD_FAILED)
502 m = 0;
503#endif
504 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800505 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800506 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800507 } else
508 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000509 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800510 return m;
511}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800513static int rt6_score_route(struct rt6_info *rt, int oif,
514 int strict)
515{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700516 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900517
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700518 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700519 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800520 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800521#ifdef CONFIG_IPV6_ROUTER_PREF
522 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
523#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700524 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800525 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800526 return -1;
527 return m;
528}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
David S. Millerf11e6652007-03-24 20:36:25 -0700530static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
531 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800532{
David S. Millerf11e6652007-03-24 20:36:25 -0700533 int m;
534
535 if (rt6_check_expired(rt))
536 goto out;
537
538 m = rt6_score_route(rt, oif, strict);
539 if (m < 0)
540 goto out;
541
542 if (m > *mpri) {
543 if (strict & RT6_LOOKUP_F_REACHABLE)
544 rt6_probe(match);
545 *mpri = m;
546 match = rt;
547 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
548 rt6_probe(rt);
549 }
550
551out:
552 return match;
553}
554
555static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
556 struct rt6_info *rr_head,
557 u32 metric, int oif, int strict)
558{
559 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800560 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
David S. Millerf11e6652007-03-24 20:36:25 -0700562 match = NULL;
563 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700564 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700565 match = find_match(rt, oif, strict, &mpri, match);
566 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700567 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700568 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800569
David S. Millerf11e6652007-03-24 20:36:25 -0700570 return match;
571}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800572
David S. Millerf11e6652007-03-24 20:36:25 -0700573static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
574{
575 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800576 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
David S. Millerf11e6652007-03-24 20:36:25 -0700578 rt0 = fn->rr_ptr;
579 if (!rt0)
580 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
David S. Millerf11e6652007-03-24 20:36:25 -0700582 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800584 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700585 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700586 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700587
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800588 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700589 if (!next || next->rt6i_metric != rt0->rt6i_metric)
590 next = fn->leaf;
591
592 if (next != rt0)
593 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 }
595
David S. Millerd1918542011-12-28 20:19:20 -0500596 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000597 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598}
599
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800600#ifdef CONFIG_IPV6_ROUTE_INFO
601int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000602 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800603{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900604 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800605 struct route_info *rinfo = (struct route_info *) opt;
606 struct in6_addr prefix_buf, *prefix;
607 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900608 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800609 struct rt6_info *rt;
610
611 if (len < sizeof(struct route_info)) {
612 return -EINVAL;
613 }
614
615 /* Sanity check for prefix_len and length */
616 if (rinfo->length > 3) {
617 return -EINVAL;
618 } else if (rinfo->prefix_len > 128) {
619 return -EINVAL;
620 } else if (rinfo->prefix_len > 64) {
621 if (rinfo->length < 2) {
622 return -EINVAL;
623 }
624 } else if (rinfo->prefix_len > 0) {
625 if (rinfo->length < 1) {
626 return -EINVAL;
627 }
628 }
629
630 pref = rinfo->route_pref;
631 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000632 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800633
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900634 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800635
636 if (rinfo->length == 3)
637 prefix = (struct in6_addr *)rinfo->prefix;
638 else {
639 /* this function is safe */
640 ipv6_addr_prefix(&prefix_buf,
641 (struct in6_addr *)rinfo->prefix,
642 rinfo->prefix_len);
643 prefix = &prefix_buf;
644 }
645
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800646 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
647 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800648
649 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700650 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800651 rt = NULL;
652 }
653
654 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800655 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800656 pref);
657 else if (rt)
658 rt->rt6i_flags = RTF_ROUTEINFO |
659 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
660
661 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000662 if (!addrconf_finite_timeout(lifetime))
663 rt6_clean_expires(rt);
664 else
665 rt6_set_expires(rt, jiffies + HZ * lifetime);
666
Changli Gaod8d1f302010-06-10 23:31:35 -0700667 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800668 }
669 return 0;
670}
671#endif
672
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800673#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700674do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800675 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700676 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700677 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700678 if (fn->fn_flags & RTN_TL_ROOT) \
679 goto out; \
680 pn = fn->parent; \
681 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800682 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700683 else \
684 fn = pn; \
685 if (fn->fn_flags & RTN_RTINFO) \
686 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700687 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700688 } \
David S. Miller38308472011-12-03 18:02:47 -0500689} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700690
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800691static struct rt6_info *ip6_pol_route_lookup(struct net *net,
692 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500693 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694{
695 struct fib6_node *fn;
696 struct rt6_info *rt;
697
Thomas Grafc71099a2006-08-04 23:20:06 -0700698 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500699 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700700restart:
701 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500702 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
703 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700704out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700705 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700706 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700707 return rt;
708
709}
710
Florian Westphalea6e5742011-09-05 16:05:44 +0200711struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
712 int flags)
713{
714 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
715}
716EXPORT_SYMBOL_GPL(ip6_route_lookup);
717
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900718struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
719 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700720{
David S. Miller4c9483b2011-03-12 16:22:43 -0500721 struct flowi6 fl6 = {
722 .flowi6_oif = oif,
723 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700724 };
725 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700726 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700727
Thomas Grafadaa70b2006-10-13 15:01:03 -0700728 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500729 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700730 flags |= RT6_LOOKUP_F_HAS_SADDR;
731 }
732
David S. Miller4c9483b2011-03-12 16:22:43 -0500733 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700734 if (dst->error == 0)
735 return (struct rt6_info *) dst;
736
737 dst_release(dst);
738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 return NULL;
740}
741
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900742EXPORT_SYMBOL(rt6_lookup);
743
Thomas Grafc71099a2006-08-04 23:20:06 -0700744/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 It takes new route entry, the addition fails by any reason the
746 route is freed. In any case, if caller does not hold it, it may
747 be destroyed.
748 */
749
Thomas Graf86872cb2006-08-22 00:01:08 -0700750static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
752 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700753 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Thomas Grafc71099a2006-08-04 23:20:06 -0700755 table = rt->rt6i_table;
756 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700757 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700758 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
760 return err;
761}
762
Thomas Graf40e22e82006-08-22 00:00:45 -0700763int ip6_ins_rt(struct rt6_info *rt)
764{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800765 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500766 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800767 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800768 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700769}
770
Gao feng1716a962012-04-06 00:13:10 +0000771static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000772 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000773 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 struct rt6_info *rt;
776
777 /*
778 * Clone the route.
779 */
780
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000781 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800784 int attempts = !in_softirq();
785
David S. Miller38308472011-12-03 18:02:47 -0500786 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500787 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000788 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900789 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000790 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900791 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795#ifdef CONFIG_IPV6_SUBTREES
796 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000797 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 rt->rt6i_src.plen = 128;
799 }
800#endif
801
David S. Miller14deae42009-01-04 16:04:39 -0800802 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500803 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500804 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800805 int saved_rt_min_interval =
806 net->ipv6.sysctl.ip6_rt_gc_min_interval;
807 int saved_rt_elasticity =
808 net->ipv6.sysctl.ip6_rt_gc_elasticity;
809
810 if (attempts-- > 0) {
811 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
812 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
813
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000814 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800815
816 net->ipv6.sysctl.ip6_rt_gc_elasticity =
817 saved_rt_elasticity;
818 net->ipv6.sysctl.ip6_rt_gc_min_interval =
819 saved_rt_min_interval;
820 goto retry;
821 }
822
Joe Perchesf3213832012-05-15 14:11:53 +0000823 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700824 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800825 return NULL;
826 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800827 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800829 return rt;
830}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000832static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
833 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800834{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000835 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
836
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800837 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800838 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700839 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800840 }
841 return rt;
842}
843
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800844static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500845 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846{
847 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800848 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700849 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800851 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700852 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700854 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
856relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700857 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800859restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500860 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
862restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700863 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800864
David S. Miller4c9483b2011-03-12 16:22:43 -0500865 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800866 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800867 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800868 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
Changli Gaod8d1f302010-06-10 23:31:35 -0700870 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700871 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800872
David S. Miller97cac082012-07-02 22:43:47 -0700873 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500874 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800875 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500876 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800877 else
878 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800879
Changli Gaod8d1f302010-06-10 23:31:35 -0700880 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800881 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800882
Changli Gaod8d1f302010-06-10 23:31:35 -0700883 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800884 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700885 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800886 if (!err)
887 goto out2;
888 }
889
890 if (--attempts <= 0)
891 goto out2;
892
893 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700894 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800895 * released someone could insert this route. Relookup.
896 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700897 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800898 goto relookup;
899
900out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800901 if (reachable) {
902 reachable = 0;
903 goto restart_2;
904 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700905 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700906 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700908 rt->dst.lastuse = jiffies;
909 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700910
911 return rt;
912}
913
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800914static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500915 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700916{
David S. Miller4c9483b2011-03-12 16:22:43 -0500917 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700918}
919
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000920static struct dst_entry *ip6_route_input_lookup(struct net *net,
921 struct net_device *dev,
922 struct flowi6 *fl6, int flags)
923{
924 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
925 flags |= RT6_LOOKUP_F_IFACE;
926
927 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
928}
929
Thomas Grafc71099a2006-08-04 23:20:06 -0700930void ip6_route_input(struct sk_buff *skb)
931{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000932 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900933 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700934 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500935 struct flowi6 fl6 = {
936 .flowi6_iif = skb->dev->ifindex,
937 .daddr = iph->daddr,
938 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500939 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500940 .flowi6_mark = skb->mark,
941 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700942 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700943
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000944 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700945}
946
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800947static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500948 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700949{
David S. Miller4c9483b2011-03-12 16:22:43 -0500950 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700951}
952
Florian Westphal9c7a4f9c2011-03-22 19:17:36 -0700953struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500954 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700955{
956 int flags = 0;
957
David McCullough4dc27d1c2012-06-25 15:42:26 +0000958 fl6->flowi6_iif = net->loopback_dev->ifindex;
959
David S. Miller4c9483b2011-03-12 16:22:43 -0500960 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700961 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700962
David S. Miller4c9483b2011-03-12 16:22:43 -0500963 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700964 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000965 else if (sk)
966 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700967
David S. Miller4c9483b2011-03-12 16:22:43 -0500968 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969}
970
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900971EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
David S. Miller2774c132011-03-01 14:59:04 -0800973struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700974{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700975 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700976 struct dst_entry *new = NULL;
977
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700978 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700979 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700980 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller97bab732012-06-09 22:36:36 -0700981 rt6_init_peer(rt, net->ipv6.peers);
David S. Millercf911662011-04-28 14:31:47 -0700982
Changli Gaod8d1f302010-06-10 23:31:35 -0700983 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700984
David S. Miller14e50e52007-05-24 18:17:54 -0700985 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800986 new->input = dst_discard;
987 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700988
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000989 if (dst_metrics_read_only(&ort->dst))
990 new->_metrics = ort->dst._metrics;
991 else
992 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700993 rt->rt6i_idev = ort->rt6i_idev;
994 if (rt->rt6i_idev)
995 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -0700996
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000997 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +0000998 rt->rt6i_flags = ort->rt6i_flags;
999 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001000 rt->rt6i_metric = 0;
1001
1002 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1003#ifdef CONFIG_IPV6_SUBTREES
1004 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1005#endif
1006
1007 dst_free(new);
1008 }
1009
David S. Miller69ead7a2011-03-01 14:45:33 -08001010 dst_release(dst_orig);
1011 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001012}
David S. Miller14e50e52007-05-24 18:17:54 -07001013
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014/*
1015 * Destination cache support functions
1016 */
1017
1018static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1019{
1020 struct rt6_info *rt;
1021
1022 rt = (struct rt6_info *) dst;
1023
David S. Miller6431cbc2011-02-07 20:38:06 -08001024 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1025 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001026 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001027 rt6_bind_peer(rt, 0);
1028 rt->rt6i_peer_genid = rt6_peer_genid();
1029 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001031 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 return NULL;
1033}
1034
1035static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1036{
1037 struct rt6_info *rt = (struct rt6_info *) dst;
1038
1039 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001040 if (rt->rt6i_flags & RTF_CACHE) {
1041 if (rt6_check_expired(rt)) {
1042 ip6_del_rt(rt);
1043 dst = NULL;
1044 }
1045 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001047 dst = NULL;
1048 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001050 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051}
1052
1053static void ip6_link_failure(struct sk_buff *skb)
1054{
1055 struct rt6_info *rt;
1056
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001057 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Eric Dumazetadf30902009-06-02 05:19:30 +00001059 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001061 if (rt->rt6i_flags & RTF_CACHE)
1062 rt6_update_expires(rt, 0);
1063 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 rt->rt6i_node->fn_sernum = -1;
1065 }
1066}
1067
1068static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1069{
1070 struct rt6_info *rt6 = (struct rt6_info*)dst;
1071
David S. Miller81aded22012-06-15 14:54:11 -07001072 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001074 struct net *net = dev_net(dst->dev);
1075
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 rt6->rt6i_flags |= RTF_MODIFIED;
1077 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001078 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001080 features |= RTAX_FEATURE_ALLFRAG;
1081 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 }
David S. Millerdefb3512010-12-08 21:16:57 -08001083 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001084 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 }
1086}
1087
David S. Miller42ae66c2012-06-15 20:01:57 -07001088void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1089 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001090{
1091 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1092 struct dst_entry *dst;
1093 struct flowi6 fl6;
1094
1095 memset(&fl6, 0, sizeof(fl6));
1096 fl6.flowi6_oif = oif;
1097 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001098 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001099 fl6.daddr = iph->daddr;
1100 fl6.saddr = iph->saddr;
1101 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1102
1103 dst = ip6_route_output(net, NULL, &fl6);
1104 if (!dst->error)
1105 ip6_rt_update_pmtu(dst, ntohl(mtu));
1106 dst_release(dst);
1107}
1108EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1109
1110void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1111{
1112 ip6_update_pmtu(skb, sock_net(sk), mtu,
1113 sk->sk_bound_dev_if, sk->sk_mark);
1114}
1115EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1116
David S. Miller0dbaee32010-12-13 12:52:14 -08001117static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118{
David S. Miller0dbaee32010-12-13 12:52:14 -08001119 struct net_device *dev = dst->dev;
1120 unsigned int mtu = dst_mtu(dst);
1121 struct net *net = dev_net(dev);
1122
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1124
Daniel Lezcano55786892008-03-04 13:47:47 -08001125 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1126 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127
1128 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001129 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1130 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1131 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 * rely only on pmtu discovery"
1133 */
1134 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1135 mtu = IPV6_MAXPLEN;
1136 return mtu;
1137}
1138
Steffen Klassertebb762f2011-11-23 02:12:51 +00001139static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001140{
David S. Millerd33e4552010-12-14 13:01:14 -08001141 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001142 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1143
1144 if (mtu)
1145 return mtu;
1146
1147 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001148
1149 rcu_read_lock();
1150 idev = __in6_dev_get(dst->dev);
1151 if (idev)
1152 mtu = idev->cnf.mtu6;
1153 rcu_read_unlock();
1154
1155 return mtu;
1156}
1157
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001158static struct dst_entry *icmp6_dst_gc_list;
1159static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001160
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001161struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001163 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164{
David S. Miller87a11572011-12-06 17:04:13 -05001165 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 struct rt6_info *rt;
1167 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001168 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
David S. Miller38308472011-12-03 18:02:47 -05001170 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001171 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
David S. Miller8b96d222012-06-11 02:01:56 -07001173 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001174 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001176 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 goto out;
1178 }
1179
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 if (neigh)
1181 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001182 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001183 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001184 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001185 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001186 dst_free(&rt->dst);
1187 return ERR_CAST(neigh);
1188 }
David S. Miller14deae42009-01-04 16:04:39 -08001189 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001191 rt->dst.flags |= DST_HOST;
1192 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001193 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001194 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001195 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001196 rt->rt6i_dst.plen = 128;
1197 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001198 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001200 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001201 rt->dst.next = icmp6_dst_gc_list;
1202 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001203 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
Daniel Lezcano55786892008-03-04 13:47:47 -08001205 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
David S. Miller87a11572011-12-06 17:04:13 -05001207 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209out:
David S. Miller87a11572011-12-06 17:04:13 -05001210 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211}
1212
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001213int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214{
Hagen Paul Pfeifere9476e952011-02-25 05:45:19 +00001215 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001216 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001218 spin_lock_bh(&icmp6_dst_lock);
1219 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001220
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 while ((dst = *pprev) != NULL) {
1222 if (!atomic_read(&dst->__refcnt)) {
1223 *pprev = dst->next;
1224 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 } else {
1226 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001227 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 }
1229 }
1230
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001231 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001232
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001233 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234}
1235
David S. Miller1e493d12008-09-10 17:27:15 -07001236static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1237 void *arg)
1238{
1239 struct dst_entry *dst, **pprev;
1240
1241 spin_lock_bh(&icmp6_dst_lock);
1242 pprev = &icmp6_dst_gc_list;
1243 while ((dst = *pprev) != NULL) {
1244 struct rt6_info *rt = (struct rt6_info *) dst;
1245 if (func(rt, arg)) {
1246 *pprev = dst->next;
1247 dst_free(dst);
1248 } else {
1249 pprev = &dst->next;
1250 }
1251 }
1252 spin_unlock_bh(&icmp6_dst_lock);
1253}
1254
Daniel Lezcano569d3642008-01-18 03:56:57 -08001255static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001258 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001259 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1260 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1261 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1262 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1263 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001264 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
Eric Dumazetfc66f952010-10-08 06:37:34 +00001266 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001267 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001268 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 goto out;
1270
Benjamin Thery6891a342008-03-04 13:49:47 -08001271 net->ipv6.ip6_rt_gc_expire++;
1272 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1273 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001274 entries = dst_entries_get_slow(ops);
1275 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001276 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001278 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001279 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280}
1281
1282/* Clean host part of a prefix. Not necessary in radix tree,
1283 but results in cleaner routing tables.
1284
1285 Remove it only when all the things will work!
1286 */
1287
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001288int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289{
David S. Miller5170ae82010-12-12 21:35:57 -08001290 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001291 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001292 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001293 struct inet6_dev *idev;
1294
1295 rcu_read_lock();
1296 idev = __in6_dev_get(dev);
1297 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001298 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001299 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001300 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001301 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 }
1303 return hoplimit;
1304}
David S. Millerabbf46a2010-12-12 21:14:46 -08001305EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
1307/*
1308 *
1309 */
1310
Thomas Graf86872cb2006-08-22 00:01:08 -07001311int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312{
1313 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001314 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 struct rt6_info *rt = NULL;
1316 struct net_device *dev = NULL;
1317 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001318 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 int addr_type;
1320
Thomas Graf86872cb2006-08-22 00:01:08 -07001321 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 return -EINVAL;
1323#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001324 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 return -EINVAL;
1326#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001327 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001329 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 if (!dev)
1331 goto out;
1332 idev = in6_dev_get(dev);
1333 if (!idev)
1334 goto out;
1335 }
1336
Thomas Graf86872cb2006-08-22 00:01:08 -07001337 if (cfg->fc_metric == 0)
1338 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
Matti Vaittinend71314b2011-11-14 00:14:49 +00001340 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001341 if (cfg->fc_nlinfo.nlh &&
1342 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001343 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001344 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001345 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001346 table = fib6_new_table(net, cfg->fc_table);
1347 }
1348 } else {
1349 table = fib6_new_table(net, cfg->fc_table);
1350 }
David S. Miller38308472011-12-03 18:02:47 -05001351
1352 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001353 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001354
David S. Miller8b96d222012-06-11 02:01:56 -07001355 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
David S. Miller38308472011-12-03 18:02:47 -05001357 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 err = -ENOMEM;
1359 goto out;
1360 }
1361
Changli Gaod8d1f302010-06-10 23:31:35 -07001362 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001363
1364 if (cfg->fc_flags & RTF_EXPIRES)
1365 rt6_set_expires(rt, jiffies +
1366 clock_t_to_jiffies(cfg->fc_expires));
1367 else
1368 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Thomas Graf86872cb2006-08-22 00:01:08 -07001370 if (cfg->fc_protocol == RTPROT_UNSPEC)
1371 cfg->fc_protocol = RTPROT_BOOT;
1372 rt->rt6i_protocol = cfg->fc_protocol;
1373
1374 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
1376 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001377 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001378 else if (cfg->fc_flags & RTF_LOCAL)
1379 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001381 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Changli Gaod8d1f302010-06-10 23:31:35 -07001383 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
Thomas Graf86872cb2006-08-22 00:01:08 -07001385 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1386 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001388 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001390 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1391 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1392 if (!metrics) {
1393 err = -ENOMEM;
1394 goto out;
1395 }
1396 dst_init_metrics(&rt->dst, metrics, 0);
1397 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001399 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1400 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401#endif
1402
Thomas Graf86872cb2006-08-22 00:01:08 -07001403 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
1405 /* We cannot add true routes via loopback here,
1406 they would result in kernel looping; promote them to reject routes
1407 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001408 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001409 (dev && (dev->flags & IFF_LOOPBACK) &&
1410 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1411 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001413 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 if (dev) {
1415 dev_put(dev);
1416 in6_dev_put(idev);
1417 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001418 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 dev_hold(dev);
1420 idev = in6_dev_get(dev);
1421 if (!idev) {
1422 err = -ENODEV;
1423 goto out;
1424 }
1425 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001426 rt->dst.output = ip6_pkt_discard_out;
1427 rt->dst.input = ip6_pkt_discard;
1428 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1430 goto install_route;
1431 }
1432
Thomas Graf86872cb2006-08-22 00:01:08 -07001433 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001434 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 int gwa_type;
1436
Thomas Graf86872cb2006-08-22 00:01:08 -07001437 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001438 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 gwa_type = ipv6_addr_type(gw_addr);
1440
1441 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1442 struct rt6_info *grt;
1443
1444 /* IPv6 strictly inhibits using not link-local
1445 addresses as nexthop address.
1446 Otherwise, router will not able to send redirects.
1447 It is very good, but in some (rare!) circumstances
1448 (SIT, PtP, NBMA NOARP links) it is handy to allow
1449 some exceptions. --ANK
1450 */
1451 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001452 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 goto out;
1454
Daniel Lezcano55786892008-03-04 13:47:47 -08001455 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456
1457 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001458 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 goto out;
1460 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001461 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001462 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 goto out;
1464 }
1465 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001466 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 idev = grt->rt6i_idev;
1468 dev_hold(dev);
1469 in6_dev_hold(grt->rt6i_idev);
1470 }
David S. Miller38308472011-12-03 18:02:47 -05001471 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001473 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
1475 if (err)
1476 goto out;
1477 }
1478 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001479 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 goto out;
1481 }
1482
1483 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001484 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 goto out;
1486
Daniel Walterc3968a82011-04-13 21:10:57 +00001487 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1488 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1489 err = -EINVAL;
1490 goto out;
1491 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001492 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001493 rt->rt6i_prefsrc.plen = 128;
1494 } else
1495 rt->rt6i_prefsrc.plen = 0;
1496
Thomas Graf86872cb2006-08-22 00:01:08 -07001497 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001498 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001499 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 }
1502
Thomas Graf86872cb2006-08-22 00:01:08 -07001503 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001506 if (cfg->fc_mx) {
1507 struct nlattr *nla;
1508 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509
Thomas Graf86872cb2006-08-22 00:01:08 -07001510 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001511 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001512
1513 if (type) {
1514 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 err = -EINVAL;
1516 goto out;
1517 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001518
David S. Millerdefb3512010-12-08 21:16:57 -08001519 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 }
1522 }
1523
Changli Gaod8d1f302010-06-10 23:31:35 -07001524 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001526 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001527
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001528 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001529
Thomas Graf86872cb2006-08-22 00:01:08 -07001530 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
1532out:
1533 if (dev)
1534 dev_put(dev);
1535 if (idev)
1536 in6_dev_put(idev);
1537 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001538 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 return err;
1540}
1541
Thomas Graf86872cb2006-08-22 00:01:08 -07001542static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543{
1544 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001545 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001546 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001548 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001549 return -ENOENT;
1550
Thomas Grafc71099a2006-08-04 23:20:06 -07001551 table = rt->rt6i_table;
1552 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Thomas Graf86872cb2006-08-22 00:01:08 -07001554 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001555 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Thomas Grafc71099a2006-08-04 23:20:06 -07001557 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
1559 return err;
1560}
1561
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001562int ip6_del_rt(struct rt6_info *rt)
1563{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001564 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001565 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001566 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001567 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001568}
1569
Thomas Graf86872cb2006-08-22 00:01:08 -07001570static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571{
Thomas Grafc71099a2006-08-04 23:20:06 -07001572 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 struct fib6_node *fn;
1574 struct rt6_info *rt;
1575 int err = -ESRCH;
1576
Daniel Lezcano55786892008-03-04 13:47:47 -08001577 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001578 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001579 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
Thomas Grafc71099a2006-08-04 23:20:06 -07001581 read_lock_bh(&table->tb6_lock);
1582
1583 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001584 &cfg->fc_dst, cfg->fc_dst_len,
1585 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001586
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001588 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001589 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001590 (!rt->dst.dev ||
1591 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001593 if (cfg->fc_flags & RTF_GATEWAY &&
1594 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001596 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001598 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001599 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600
Thomas Graf86872cb2006-08-22 00:01:08 -07001601 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 }
1603 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001604 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
1606 return err;
1607}
1608
1609/*
1610 * Handle redirects
1611 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001612struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001613 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001614 struct in6_addr gateway;
1615};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001617static struct rt6_info *__ip6_route_redirect(struct net *net,
1618 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001619 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001620 int flags)
1621{
David S. Miller4c9483b2011-03-12 16:22:43 -05001622 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001623 struct rt6_info *rt;
1624 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001625
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001627 * Get the "current" route for this destination and
1628 * check if the redirect has come from approriate router.
1629 *
1630 * RFC 2461 specifies that redirects should only be
1631 * accepted if they come from the nexthop to the target.
1632 * Due to the way the routes are chosen, this notion
1633 * is a bit fuzzy and one might need to check all possible
1634 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636
Thomas Grafc71099a2006-08-04 23:20:06 -07001637 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001638 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001639restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001640 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001641 /*
1642 * Current route is on-link; redirect is always invalid.
1643 *
1644 * Seems, previous statement is not true. It could
1645 * be node, which looks for us as on-link (f.e. proxy ndisc)
1646 * But then router serving it might decide, that we should
1647 * know truth 8)8) --ANK (980726).
1648 */
1649 if (rt6_check_expired(rt))
1650 continue;
1651 if (!(rt->rt6i_flags & RTF_GATEWAY))
1652 continue;
David S. Millerd1918542011-12-28 20:19:20 -05001653 if (fl6->flowi6_oif != rt->dst.dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001654 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001655 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001656 continue;
1657 break;
1658 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001659
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001660 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001661 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001662 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001663out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001664 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001665
1666 read_unlock_bh(&table->tb6_lock);
1667
1668 return rt;
1669};
1670
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001671static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1672 const struct in6_addr *src,
1673 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001674 struct net_device *dev)
1675{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001676 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001677 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001678 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001679 .fl6 = {
1680 .flowi6_oif = dev->ifindex,
1681 .daddr = *dest,
1682 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001683 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001684 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001685
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001686 rdfl.gateway = *gateway;
Brian Haley86c36ce2009-10-07 13:58:01 -07001687
Thomas Grafadaa70b2006-10-13 15:01:03 -07001688 if (rt6_need_strict(dest))
1689 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001690
David S. Miller4c9483b2011-03-12 16:22:43 -05001691 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001692 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001693}
1694
David S. Miller6e157b62012-07-12 00:05:02 -07001695static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001696{
David S. Millere8599ff2012-07-11 23:43:53 -07001697 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001698 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001699 struct rt6_info *rt, *nrt = NULL;
1700 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001701 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001702 const struct in6_addr *dest;
1703 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001704 struct inet6_dev *in6_dev;
1705 struct neighbour *neigh;
1706 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001707 int optlen, on_link;
1708 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001709
1710 optlen = skb->tail - skb->transport_header;
1711 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1712
1713 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001714 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001715 return;
1716 }
1717
1718 icmph = icmp6_hdr(skb);
1719 target = (const struct in6_addr *) (icmph + 1);
1720 dest = target + 1;
1721
1722 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001723 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001724 return;
1725 }
1726
David S. Miller6e157b62012-07-12 00:05:02 -07001727 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001728 if (ipv6_addr_equal(dest, target)) {
1729 on_link = 1;
1730 } else if (ipv6_addr_type(target) !=
1731 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001732 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001733 return;
1734 }
1735
1736 in6_dev = __in6_dev_get(skb->dev);
1737 if (!in6_dev)
1738 return;
1739 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1740 return;
1741
1742 /* RFC2461 8.1:
1743 * The IP source address of the Redirect MUST be the same as the current
1744 * first-hop router for the specified ICMP Destination Address.
1745 */
1746
1747 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1748 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1749 return;
1750 }
David S. Miller6e157b62012-07-12 00:05:02 -07001751
1752 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001753 if (ndopts.nd_opts_tgt_lladdr) {
1754 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1755 skb->dev);
1756 if (!lladdr) {
1757 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1758 return;
1759 }
1760 }
1761
David S. Miller6e157b62012-07-12 00:05:02 -07001762 rt = (struct rt6_info *) dst;
1763 if (rt == net->ipv6.ip6_null_entry) {
1764 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1765 return;
1766 }
1767
1768 /* Redirect received -> path was valid.
1769 * Look, redirects are sent only in response to data packets,
1770 * so that this nexthop apparently is reachable. --ANK
1771 */
1772 dst_confirm(&rt->dst);
1773
David S. Millere8599ff2012-07-11 23:43:53 -07001774 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1775 if (!neigh)
1776 return;
1777
David S. Miller6e157b62012-07-12 00:05:02 -07001778 /* Duplicate redirect: silently ignore. */
1779 old_neigh = rt->n;
1780 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001781 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 /*
1784 * We have finally decided to accept it.
1785 */
1786
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001787 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1789 NEIGH_UPDATE_F_OVERRIDE|
1790 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1791 NEIGH_UPDATE_F_ISROUTER))
1792 );
1793
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001794 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001795 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 goto out;
1797
1798 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1799 if (on_link)
1800 nrt->rt6i_flags &= ~RTF_GATEWAY;
1801
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001802 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001803 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Thomas Graf40e22e82006-08-22 00:00:45 -07001805 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 goto out;
1807
Changli Gaod8d1f302010-06-10 23:31:35 -07001808 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001809 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001810 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001811 netevent.new_neigh = neigh;
1812 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001813 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1814
David S. Miller38308472011-12-03 18:02:47 -05001815 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001816 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001817 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 }
1819
1820out:
David S. Millere8599ff2012-07-11 23:43:53 -07001821 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001822}
1823
1824void rt6_redirect(struct sk_buff *skb)
1825{
1826 const struct in6_addr *target;
1827 const struct in6_addr *dest;
1828 const struct in6_addr *src;
1829 const struct in6_addr *saddr;
1830 struct icmp6hdr *icmph;
1831 struct rt6_info *rt;
1832
1833 icmph = icmp6_hdr(skb);
1834 target = (const struct in6_addr *) (icmph + 1);
1835 dest = target + 1;
1836
1837 src = &ipv6_hdr(skb)->daddr;
1838 saddr = &ipv6_hdr(skb)->saddr;
1839
1840 rt = ip6_route_redirect(dest, src, saddr, skb->dev);
1841 rt6_do_redirect(&rt->dst, skb);
Changli Gaod8d1f302010-06-10 23:31:35 -07001842 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843}
1844
1845/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 * Misc support functions
1847 */
1848
Gao feng1716a962012-04-06 00:13:10 +00001849static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001850 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851{
David S. Millerd1918542011-12-28 20:19:20 -05001852 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001853 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1854 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855
1856 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001857 rt->dst.input = ort->dst.input;
1858 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001859 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001861 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001862 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001863 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001864 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 rt->rt6i_idev = ort->rt6i_idev;
1866 if (rt->rt6i_idev)
1867 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001868 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001870 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001871 rt->rt6i_flags = ort->rt6i_flags;
1872 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1873 (RTF_DEFAULT | RTF_ADDRCONF))
1874 rt6_set_from(rt, ort);
1875 else
1876 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 rt->rt6i_metric = 0;
1878
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879#ifdef CONFIG_IPV6_SUBTREES
1880 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1881#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001882 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001883 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 }
1885 return rt;
1886}
1887
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001888#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001889static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001890 const struct in6_addr *prefix, int prefixlen,
1891 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001892{
1893 struct fib6_node *fn;
1894 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001895 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001896
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001897 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001898 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001899 return NULL;
1900
1901 write_lock_bh(&table->tb6_lock);
1902 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001903 if (!fn)
1904 goto out;
1905
Changli Gaod8d1f302010-06-10 23:31:35 -07001906 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001907 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001908 continue;
1909 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1910 continue;
1911 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1912 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001913 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001914 break;
1915 }
1916out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001917 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001918 return rt;
1919}
1920
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001921static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001922 const struct in6_addr *prefix, int prefixlen,
1923 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001924 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001925{
Thomas Graf86872cb2006-08-22 00:01:08 -07001926 struct fib6_config cfg = {
1927 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001928 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001929 .fc_ifindex = ifindex,
1930 .fc_dst_len = prefixlen,
1931 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1932 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001933 .fc_nlinfo.pid = 0,
1934 .fc_nlinfo.nlh = NULL,
1935 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001936 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001937
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001938 cfg.fc_dst = *prefix;
1939 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001940
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001941 /* We should treat it as a default route if prefix length is 0. */
1942 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001943 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001944
Thomas Graf86872cb2006-08-22 00:01:08 -07001945 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001946
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001947 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001948}
1949#endif
1950
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001951struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001952{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001954 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001956 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001957 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001958 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
Thomas Grafc71099a2006-08-04 23:20:06 -07001960 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001961 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001962 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001963 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1965 break;
1966 }
1967 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001968 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001969 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 return rt;
1971}
1972
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001973struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001974 struct net_device *dev,
1975 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976{
Thomas Graf86872cb2006-08-22 00:01:08 -07001977 struct fib6_config cfg = {
1978 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001979 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001980 .fc_ifindex = dev->ifindex,
1981 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1982 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001983 .fc_nlinfo.pid = 0,
1984 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001985 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001986 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001988 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989
Thomas Graf86872cb2006-08-22 00:01:08 -07001990 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 return rt6_get_dflt_router(gwaddr, dev);
1993}
1994
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001995void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996{
1997 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001998 struct fib6_table *table;
1999
2000 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08002001 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05002002 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07002003 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004
2005restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07002006 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07002007 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002009 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07002010 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07002011 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 goto restart;
2013 }
2014 }
Thomas Grafc71099a2006-08-04 23:20:06 -07002015 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016}
2017
Daniel Lezcano55786892008-03-04 13:47:47 -08002018static void rtmsg_to_fib6_config(struct net *net,
2019 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07002020 struct fib6_config *cfg)
2021{
2022 memset(cfg, 0, sizeof(*cfg));
2023
2024 cfg->fc_table = RT6_TABLE_MAIN;
2025 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
2026 cfg->fc_metric = rtmsg->rtmsg_metric;
2027 cfg->fc_expires = rtmsg->rtmsg_info;
2028 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
2029 cfg->fc_src_len = rtmsg->rtmsg_src_len;
2030 cfg->fc_flags = rtmsg->rtmsg_flags;
2031
Daniel Lezcano55786892008-03-04 13:47:47 -08002032 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08002033
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002034 cfg->fc_dst = rtmsg->rtmsg_dst;
2035 cfg->fc_src = rtmsg->rtmsg_src;
2036 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07002037}
2038
Daniel Lezcano55786892008-03-04 13:47:47 -08002039int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040{
Thomas Graf86872cb2006-08-22 00:01:08 -07002041 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 struct in6_rtmsg rtmsg;
2043 int err;
2044
2045 switch(cmd) {
2046 case SIOCADDRT: /* Add a route */
2047 case SIOCDELRT: /* Delete a route */
2048 if (!capable(CAP_NET_ADMIN))
2049 return -EPERM;
2050 err = copy_from_user(&rtmsg, arg,
2051 sizeof(struct in6_rtmsg));
2052 if (err)
2053 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07002054
Daniel Lezcano55786892008-03-04 13:47:47 -08002055 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07002056
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 rtnl_lock();
2058 switch (cmd) {
2059 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002060 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 break;
2062 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002063 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 break;
2065 default:
2066 err = -EINVAL;
2067 }
2068 rtnl_unlock();
2069
2070 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002071 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072
2073 return -EINVAL;
2074}
2075
2076/*
2077 * Drop the packet on the floor
2078 */
2079
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002080static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002082 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002083 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002084 switch (ipstats_mib_noroutes) {
2085 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002086 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002087 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002088 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2089 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002090 break;
2091 }
2092 /* FALLTHROUGH */
2093 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002094 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2095 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002096 break;
2097 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002098 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 kfree_skb(skb);
2100 return 0;
2101}
2102
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002103static int ip6_pkt_discard(struct sk_buff *skb)
2104{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002105 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002106}
2107
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002108static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109{
Eric Dumazetadf30902009-06-02 05:19:30 +00002110 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002111 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112}
2113
David S. Miller6723ab52006-10-18 21:20:57 -07002114#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2115
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002116static int ip6_pkt_prohibit(struct sk_buff *skb)
2117{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002118 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002119}
2120
2121static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2122{
Eric Dumazetadf30902009-06-02 05:19:30 +00002123 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002124 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002125}
2126
David S. Miller6723ab52006-10-18 21:20:57 -07002127#endif
2128
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129/*
2130 * Allocate a dst for local (unicast / anycast) address.
2131 */
2132
2133struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2134 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002135 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002137 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002138 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002139 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140
David S. Miller38308472011-12-03 18:02:47 -05002141 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002142 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002144 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 in6_dev_hold(idev);
2147
David S. Miller11d53b42011-06-24 15:23:34 -07002148 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002149 rt->dst.input = ip6_input;
2150 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002152 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153
2154 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002155 if (anycast)
2156 rt->rt6i_flags |= RTF_ANYCAST;
2157 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002159 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002160 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002161 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002162 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 }
2164
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002165 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002167 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168
Changli Gaod8d1f302010-06-10 23:31:35 -07002169 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170
2171 return rt;
2172}
2173
Daniel Walterc3968a82011-04-13 21:10:57 +00002174int ip6_route_get_saddr(struct net *net,
2175 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002176 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002177 unsigned int prefs,
2178 struct in6_addr *saddr)
2179{
2180 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2181 int err = 0;
2182 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002183 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002184 else
2185 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2186 daddr, prefs, saddr);
2187 return err;
2188}
2189
2190/* remove deleted ip from prefsrc entries */
2191struct arg_dev_net_ip {
2192 struct net_device *dev;
2193 struct net *net;
2194 struct in6_addr *addr;
2195};
2196
2197static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2198{
2199 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2200 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2201 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2202
David S. Millerd1918542011-12-28 20:19:20 -05002203 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002204 rt != net->ipv6.ip6_null_entry &&
2205 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2206 /* remove prefsrc entry */
2207 rt->rt6i_prefsrc.plen = 0;
2208 }
2209 return 0;
2210}
2211
2212void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2213{
2214 struct net *net = dev_net(ifp->idev->dev);
2215 struct arg_dev_net_ip adni = {
2216 .dev = ifp->idev->dev,
2217 .net = net,
2218 .addr = &ifp->addr,
2219 };
2220 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2221}
2222
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002223struct arg_dev_net {
2224 struct net_device *dev;
2225 struct net *net;
2226};
2227
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228static int fib6_ifdown(struct rt6_info *rt, void *arg)
2229{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002230 const struct arg_dev_net *adn = arg;
2231 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002232
David S. Millerd1918542011-12-28 20:19:20 -05002233 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002234 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002236
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 return 0;
2238}
2239
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002240void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002242 struct arg_dev_net adn = {
2243 .dev = dev,
2244 .net = net,
2245 };
2246
2247 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002248 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249}
2250
Eric Dumazet95c96172012-04-15 05:58:06 +00002251struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002253 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254};
2255
2256static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2257{
2258 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2259 struct inet6_dev *idev;
2260
2261 /* In IPv6 pmtu discovery is not optional,
2262 so that RTAX_MTU lock cannot disable it.
2263 We still use this lock to block changes
2264 caused by addrconf/ndisc.
2265 */
2266
2267 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002268 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 return 0;
2270
2271 /* For administrative MTU increase, there is no way to discover
2272 IPv6 PMTU increase, so PMTU increase should be updated here.
2273 Since RFC 1981 doesn't include administrative MTU increase
2274 update PMTU increase is a MUST. (i.e. jumbo frame)
2275 */
2276 /*
2277 If new MTU is less than route PMTU, this new MTU will be the
2278 lowest MTU in the path, update the route PMTU to reflect PMTU
2279 decreases; if new MTU is greater than route PMTU, and the
2280 old MTU is the lowest MTU in the path, update the route PMTU
2281 to reflect the increase. In this case if the other nodes' MTU
2282 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2283 PMTU discouvery.
2284 */
David S. Millerd1918542011-12-28 20:19:20 -05002285 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002286 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2287 (dst_mtu(&rt->dst) >= arg->mtu ||
2288 (dst_mtu(&rt->dst) < arg->mtu &&
2289 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002290 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002291 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 return 0;
2293}
2294
Eric Dumazet95c96172012-04-15 05:58:06 +00002295void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296{
Thomas Grafc71099a2006-08-04 23:20:06 -07002297 struct rt6_mtu_change_arg arg = {
2298 .dev = dev,
2299 .mtu = mtu,
2300 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002302 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303}
2304
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002305static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002306 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002307 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002308 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002309 [RTA_PRIORITY] = { .type = NLA_U32 },
2310 [RTA_METRICS] = { .type = NLA_NESTED },
2311};
2312
2313static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2314 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315{
Thomas Graf86872cb2006-08-22 00:01:08 -07002316 struct rtmsg *rtm;
2317 struct nlattr *tb[RTA_MAX+1];
2318 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
Thomas Graf86872cb2006-08-22 00:01:08 -07002320 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2321 if (err < 0)
2322 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323
Thomas Graf86872cb2006-08-22 00:01:08 -07002324 err = -EINVAL;
2325 rtm = nlmsg_data(nlh);
2326 memset(cfg, 0, sizeof(*cfg));
2327
2328 cfg->fc_table = rtm->rtm_table;
2329 cfg->fc_dst_len = rtm->rtm_dst_len;
2330 cfg->fc_src_len = rtm->rtm_src_len;
2331 cfg->fc_flags = RTF_UP;
2332 cfg->fc_protocol = rtm->rtm_protocol;
2333
2334 if (rtm->rtm_type == RTN_UNREACHABLE)
2335 cfg->fc_flags |= RTF_REJECT;
2336
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002337 if (rtm->rtm_type == RTN_LOCAL)
2338 cfg->fc_flags |= RTF_LOCAL;
2339
Thomas Graf86872cb2006-08-22 00:01:08 -07002340 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2341 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002342 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002343
2344 if (tb[RTA_GATEWAY]) {
2345 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2346 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002348
2349 if (tb[RTA_DST]) {
2350 int plen = (rtm->rtm_dst_len + 7) >> 3;
2351
2352 if (nla_len(tb[RTA_DST]) < plen)
2353 goto errout;
2354
2355 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002357
2358 if (tb[RTA_SRC]) {
2359 int plen = (rtm->rtm_src_len + 7) >> 3;
2360
2361 if (nla_len(tb[RTA_SRC]) < plen)
2362 goto errout;
2363
2364 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002366
Daniel Walterc3968a82011-04-13 21:10:57 +00002367 if (tb[RTA_PREFSRC])
2368 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2369
Thomas Graf86872cb2006-08-22 00:01:08 -07002370 if (tb[RTA_OIF])
2371 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2372
2373 if (tb[RTA_PRIORITY])
2374 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2375
2376 if (tb[RTA_METRICS]) {
2377 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2378 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002380
2381 if (tb[RTA_TABLE])
2382 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2383
2384 err = 0;
2385errout:
2386 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387}
2388
Thomas Grafc127ea22007-03-22 11:58:32 -07002389static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390{
Thomas Graf86872cb2006-08-22 00:01:08 -07002391 struct fib6_config cfg;
2392 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393
Thomas Graf86872cb2006-08-22 00:01:08 -07002394 err = rtm_to_fib6_config(skb, nlh, &cfg);
2395 if (err < 0)
2396 return err;
2397
2398 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399}
2400
Thomas Grafc127ea22007-03-22 11:58:32 -07002401static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402{
Thomas Graf86872cb2006-08-22 00:01:08 -07002403 struct fib6_config cfg;
2404 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405
Thomas Graf86872cb2006-08-22 00:01:08 -07002406 err = rtm_to_fib6_config(skb, nlh, &cfg);
2407 if (err < 0)
2408 return err;
2409
2410 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411}
2412
Thomas Graf339bf982006-11-10 14:10:15 -08002413static inline size_t rt6_nlmsg_size(void)
2414{
2415 return NLMSG_ALIGN(sizeof(struct rtmsg))
2416 + nla_total_size(16) /* RTA_SRC */
2417 + nla_total_size(16) /* RTA_DST */
2418 + nla_total_size(16) /* RTA_GATEWAY */
2419 + nla_total_size(16) /* RTA_PREFSRC */
2420 + nla_total_size(4) /* RTA_TABLE */
2421 + nla_total_size(4) /* RTA_IIF */
2422 + nla_total_size(4) /* RTA_OIF */
2423 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002424 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002425 + nla_total_size(sizeof(struct rta_cacheinfo));
2426}
2427
Brian Haley191cd582008-08-14 15:33:21 -07002428static int rt6_fill_node(struct net *net,
2429 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002430 struct in6_addr *dst, struct in6_addr *src,
2431 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002432 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433{
2434 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002435 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002436 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002437 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002438 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439
2440 if (prefix) { /* user wants prefix routes only */
2441 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2442 /* success since this is not a prefix route */
2443 return 1;
2444 }
2445 }
2446
Thomas Graf2d7202b2006-08-22 00:01:27 -07002447 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002448 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002449 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002450
2451 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 rtm->rtm_family = AF_INET6;
2453 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2454 rtm->rtm_src_len = rt->rt6i_src.plen;
2455 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002456 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002457 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002458 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002459 table = RT6_TABLE_UNSPEC;
2460 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002461 if (nla_put_u32(skb, RTA_TABLE, table))
2462 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002463 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002465 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002466 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002467 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468 rtm->rtm_type = RTN_LOCAL;
2469 else
2470 rtm->rtm_type = RTN_UNICAST;
2471 rtm->rtm_flags = 0;
2472 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2473 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002474 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475 rtm->rtm_protocol = RTPROT_REDIRECT;
2476 else if (rt->rt6i_flags & RTF_ADDRCONF)
2477 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002478 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479 rtm->rtm_protocol = RTPROT_RA;
2480
David S. Miller38308472011-12-03 18:02:47 -05002481 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 rtm->rtm_flags |= RTM_F_CLONED;
2483
2484 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002485 if (nla_put(skb, RTA_DST, 16, dst))
2486 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002487 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002489 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2490 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491#ifdef CONFIG_IPV6_SUBTREES
2492 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002493 if (nla_put(skb, RTA_SRC, 16, src))
2494 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002495 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002496 } else if (rtm->rtm_src_len &&
2497 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2498 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002500 if (iif) {
2501#ifdef CONFIG_IPV6_MROUTE
2502 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002503 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002504 if (err <= 0) {
2505 if (!nowait) {
2506 if (err == 0)
2507 return 0;
2508 goto nla_put_failure;
2509 } else {
2510 if (err == -EMSGSIZE)
2511 goto nla_put_failure;
2512 }
2513 }
2514 } else
2515#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002516 if (nla_put_u32(skb, RTA_IIF, iif))
2517 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002518 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002520 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2521 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2522 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002524
Daniel Walterc3968a82011-04-13 21:10:57 +00002525 if (rt->rt6i_prefsrc.plen) {
2526 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002527 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002528 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2529 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002530 }
2531
David S. Millerdefb3512010-12-08 21:16:57 -08002532 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002533 goto nla_put_failure;
2534
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002535 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002536 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002537 if (n) {
2538 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2539 rcu_read_unlock();
2540 goto nla_put_failure;
2541 }
2542 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002543 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002544
David S. Millerc78679e2012-04-01 20:27:33 -04002545 if (rt->dst.dev &&
2546 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2547 goto nla_put_failure;
2548 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2549 goto nla_put_failure;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002550 if (!(rt->rt6i_flags & RTF_EXPIRES))
2551 expires = 0;
David S. Millerd1918542011-12-28 20:19:20 -05002552 else if (rt->dst.expires - jiffies < INT_MAX)
2553 expires = rt->dst.expires - jiffies;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002554 else
2555 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002556
David S. Miller87a50692012-07-10 05:06:14 -07002557 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002558 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559
Thomas Graf2d7202b2006-08-22 00:01:27 -07002560 return nlmsg_end(skb, nlh);
2561
2562nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002563 nlmsg_cancel(skb, nlh);
2564 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565}
2566
Patrick McHardy1b43af52006-08-10 23:11:17 -07002567int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568{
2569 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2570 int prefix;
2571
Thomas Graf2d7202b2006-08-22 00:01:27 -07002572 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2573 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2575 } else
2576 prefix = 0;
2577
Brian Haley191cd582008-08-14 15:33:21 -07002578 return rt6_fill_node(arg->net,
2579 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002581 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582}
2583
Thomas Grafc127ea22007-03-22 11:58:32 -07002584static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002586 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002587 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002589 struct sk_buff *skb;
2590 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002591 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002592 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002593
2594 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2595 if (err < 0)
2596 goto errout;
2597
2598 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002599 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002600
2601 if (tb[RTA_SRC]) {
2602 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2603 goto errout;
2604
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002605 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002606 }
2607
2608 if (tb[RTA_DST]) {
2609 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2610 goto errout;
2611
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002612 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002613 }
2614
2615 if (tb[RTA_IIF])
2616 iif = nla_get_u32(tb[RTA_IIF]);
2617
2618 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002619 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002620
2621 if (iif) {
2622 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002623 int flags = 0;
2624
Daniel Lezcano55786892008-03-04 13:47:47 -08002625 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002626 if (!dev) {
2627 err = -ENODEV;
2628 goto errout;
2629 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002630
2631 fl6.flowi6_iif = iif;
2632
2633 if (!ipv6_addr_any(&fl6.saddr))
2634 flags |= RT6_LOOKUP_F_HAS_SADDR;
2635
2636 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2637 flags);
2638 } else {
2639 fl6.flowi6_oif = oif;
2640
2641 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002642 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643
2644 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002645 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002646 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002647 err = -ENOBUFS;
2648 goto errout;
2649 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650
2651 /* Reserve room for dummy headers, this skb can pass
2652 through good chunk of routing engine.
2653 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002654 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2656
Changli Gaod8d1f302010-06-10 23:31:35 -07002657 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658
David S. Miller4c9483b2011-03-12 16:22:43 -05002659 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002661 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002663 kfree_skb(skb);
2664 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 }
2666
Daniel Lezcano55786892008-03-04 13:47:47 -08002667 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002668errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670}
2671
Thomas Graf86872cb2006-08-22 00:01:08 -07002672void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673{
2674 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002675 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002676 u32 seq;
2677 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002679 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002680 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002681
Thomas Graf339bf982006-11-10 14:10:15 -08002682 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002683 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002684 goto errout;
2685
Brian Haley191cd582008-08-14 15:33:21 -07002686 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002687 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002688 if (err < 0) {
2689 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2690 WARN_ON(err == -EMSGSIZE);
2691 kfree_skb(skb);
2692 goto errout;
2693 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002694 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2695 info->nlh, gfp_any());
2696 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002697errout:
2698 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002699 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700}
2701
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002702static int ip6_route_dev_notify(struct notifier_block *this,
2703 unsigned long event, void *data)
2704{
2705 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002706 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002707
2708 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002709 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002710 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2711#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002712 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002713 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002714 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002715 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2716#endif
2717 }
2718
2719 return NOTIFY_OK;
2720}
2721
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722/*
2723 * /proc
2724 */
2725
2726#ifdef CONFIG_PROC_FS
2727
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728struct rt6_proc_arg
2729{
2730 char *buffer;
2731 int offset;
2732 int length;
2733 int skip;
2734 int len;
2735};
2736
2737static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2738{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002739 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002740 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002742 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743
2744#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002745 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002747 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002749 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002750 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002751 if (n) {
2752 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002754 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002756 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002757 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002758 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2759 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002760 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 return 0;
2762}
2763
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002764static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002766 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002767 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002768 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769}
2770
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002771static int ipv6_route_open(struct inode *inode, struct file *file)
2772{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002773 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002774}
2775
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002776static const struct file_operations ipv6_route_proc_fops = {
2777 .owner = THIS_MODULE,
2778 .open = ipv6_route_open,
2779 .read = seq_read,
2780 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002781 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002782};
2783
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2785{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002786 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002788 net->ipv6.rt6_stats->fib_nodes,
2789 net->ipv6.rt6_stats->fib_route_nodes,
2790 net->ipv6.rt6_stats->fib_rt_alloc,
2791 net->ipv6.rt6_stats->fib_rt_entries,
2792 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002793 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002794 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795
2796 return 0;
2797}
2798
2799static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2800{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002801 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002802}
2803
Arjan van de Ven9a321442007-02-12 00:55:35 -08002804static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 .owner = THIS_MODULE,
2806 .open = rt6_stats_seq_open,
2807 .read = seq_read,
2808 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002809 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810};
2811#endif /* CONFIG_PROC_FS */
2812
2813#ifdef CONFIG_SYSCTL
2814
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002816int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817 void __user *buffer, size_t *lenp, loff_t *ppos)
2818{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002819 struct net *net;
2820 int delay;
2821 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002823
2824 net = (struct net *)ctl->extra1;
2825 delay = net->ipv6.sysctl.flush_delay;
2826 proc_dointvec(ctl, write, buffer, lenp, ppos);
2827 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2828 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829}
2830
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002831ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002832 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002834 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002836 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002837 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 },
2839 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002841 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 .maxlen = sizeof(int),
2843 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002844 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 },
2846 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002848 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 .maxlen = sizeof(int),
2850 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002851 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 },
2853 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002855 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856 .maxlen = sizeof(int),
2857 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002858 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 },
2860 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002862 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 .maxlen = sizeof(int),
2864 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002865 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 },
2867 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002869 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870 .maxlen = sizeof(int),
2871 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002872 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 },
2874 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002876 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877 .maxlen = sizeof(int),
2878 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002879 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 },
2881 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002883 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884 .maxlen = sizeof(int),
2885 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002886 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887 },
2888 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002890 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891 .maxlen = sizeof(int),
2892 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002893 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 },
2895 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002897 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898 .maxlen = sizeof(int),
2899 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002900 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002902 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903};
2904
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002905struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002906{
2907 struct ctl_table *table;
2908
2909 table = kmemdup(ipv6_route_table_template,
2910 sizeof(ipv6_route_table_template),
2911 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002912
2913 if (table) {
2914 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002915 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002916 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002917 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2918 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2919 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2920 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2921 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2922 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2923 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002924 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002925 }
2926
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002927 return table;
2928}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002929#endif
2930
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002931static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002932{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002933 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002934
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002935 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2936 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002937
Eric Dumazetfc66f952010-10-08 06:37:34 +00002938 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2939 goto out_ip6_dst_ops;
2940
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002941 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2942 sizeof(*net->ipv6.ip6_null_entry),
2943 GFP_KERNEL);
2944 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002945 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002946 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002947 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002948 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002949 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2950 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002951
2952#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2953 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2954 sizeof(*net->ipv6.ip6_prohibit_entry),
2955 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002956 if (!net->ipv6.ip6_prohibit_entry)
2957 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002958 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002959 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002960 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002961 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2962 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002963
2964 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2965 sizeof(*net->ipv6.ip6_blk_hole_entry),
2966 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002967 if (!net->ipv6.ip6_blk_hole_entry)
2968 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002969 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002970 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002971 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002972 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2973 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002974#endif
2975
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002976 net->ipv6.sysctl.flush_delay = 0;
2977 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2978 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2979 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2980 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2981 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2982 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2983 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2984
Benjamin Thery6891a342008-03-04 13:49:47 -08002985 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2986
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002987 ret = 0;
2988out:
2989 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002990
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002991#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2992out_ip6_prohibit_entry:
2993 kfree(net->ipv6.ip6_prohibit_entry);
2994out_ip6_null_entry:
2995 kfree(net->ipv6.ip6_null_entry);
2996#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002997out_ip6_dst_entries:
2998 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002999out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003000 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003001}
3002
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00003003static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003004{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003005 kfree(net->ipv6.ip6_null_entry);
3006#ifdef CONFIG_IPV6_MULTIPLE_TABLES
3007 kfree(net->ipv6.ip6_prohibit_entry);
3008 kfree(net->ipv6.ip6_blk_hole_entry);
3009#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003010 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003011}
3012
Thomas Grafd1896342012-06-18 12:08:33 +00003013static int __net_init ip6_route_net_init_late(struct net *net)
3014{
3015#ifdef CONFIG_PROC_FS
3016 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
3017 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
3018#endif
3019 return 0;
3020}
3021
3022static void __net_exit ip6_route_net_exit_late(struct net *net)
3023{
3024#ifdef CONFIG_PROC_FS
3025 proc_net_remove(net, "ipv6_route");
3026 proc_net_remove(net, "rt6_stats");
3027#endif
3028}
3029
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003030static struct pernet_operations ip6_route_net_ops = {
3031 .init = ip6_route_net_init,
3032 .exit = ip6_route_net_exit,
3033};
3034
David S. Millerc3426b42012-06-09 16:27:05 -07003035static int __net_init ipv6_inetpeer_init(struct net *net)
3036{
3037 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3038
3039 if (!bp)
3040 return -ENOMEM;
3041 inet_peer_base_init(bp);
3042 net->ipv6.peers = bp;
3043 return 0;
3044}
3045
3046static void __net_exit ipv6_inetpeer_exit(struct net *net)
3047{
3048 struct inet_peer_base *bp = net->ipv6.peers;
3049
3050 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07003051 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07003052 kfree(bp);
3053}
3054
David S. Miller2b823f72012-06-09 19:00:16 -07003055static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003056 .init = ipv6_inetpeer_init,
3057 .exit = ipv6_inetpeer_exit,
3058};
3059
Thomas Grafd1896342012-06-18 12:08:33 +00003060static struct pernet_operations ip6_route_net_late_ops = {
3061 .init = ip6_route_net_init_late,
3062 .exit = ip6_route_net_exit_late,
3063};
3064
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003065static struct notifier_block ip6_route_dev_notifier = {
3066 .notifier_call = ip6_route_dev_notify,
3067 .priority = 0,
3068};
3069
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003070int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003071{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003072 int ret;
3073
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003074 ret = -ENOMEM;
3075 ip6_dst_ops_template.kmem_cachep =
3076 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3077 SLAB_HWCACHE_ALIGN, NULL);
3078 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003079 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003080
Eric Dumazetfc66f952010-10-08 06:37:34 +00003081 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003082 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003083 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003084
David S. Millerc3426b42012-06-09 16:27:05 -07003085 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3086 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003087 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003088
David S. Miller7e52b332012-06-15 15:51:55 -07003089 ret = register_pernet_subsys(&ip6_route_net_ops);
3090 if (ret)
3091 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003092
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003093 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3094
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003095 /* Registering of the loopback is done before this portion of code,
3096 * the loopback reference in rt6_info will not be taken, do it
3097 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003098 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003099 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3100 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003101 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003102 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003103 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003104 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3105 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003106 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003107 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003108 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003109
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003110 ret = xfrm6_init();
3111 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003112 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003113
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003114 ret = fib6_rules_init();
3115 if (ret)
3116 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003117
Thomas Grafd1896342012-06-18 12:08:33 +00003118 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3119 if (ret)
3120 goto fib6_rules_init;
3121
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003122 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003123 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3124 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3125 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003126 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003127
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003128 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003129 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003130 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003131
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003132out:
3133 return ret;
3134
Thomas Grafd1896342012-06-18 12:08:33 +00003135out_register_late_subsys:
3136 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003137fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003138 fib6_rules_cleanup();
3139xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003140 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003141out_fib6_init:
3142 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003143out_register_subsys:
3144 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003145out_register_inetpeer:
3146 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003147out_dst_entries:
3148 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003149out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003150 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003151 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152}
3153
3154void ip6_route_cleanup(void)
3155{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003156 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003157 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003158 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003159 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003160 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003161 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003162 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003163 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003164 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165}