blob: a81c6790a648a948fceadeb60bd3ace1365240c1 [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);
David S. Miller6700c272012-07-17 03:29:28 -070081static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
82 struct sk_buff *skb, u32 mtu);
83static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
84 struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080086#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080087static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000088 const struct in6_addr *prefix, int prefixlen,
89 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +000090 unsigned int pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080091static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000092 const struct in6_addr *prefix, int prefixlen,
93 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080094#endif
95
David S. Miller06582542011-01-27 14:58:42 -080096static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
97{
98 struct rt6_info *rt = (struct rt6_info *) dst;
99 struct inet_peer *peer;
100 u32 *p = NULL;
101
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000102 if (!(rt->dst.flags & DST_HOST))
103 return NULL;
104
David S. Millerfbfe95a2012-06-08 23:24:18 -0700105 peer = rt6_get_peer_create(rt);
David S. Miller06582542011-01-27 14:58:42 -0800106 if (peer) {
107 u32 *old_p = __DST_METRICS_PTR(old);
108 unsigned long prev, new;
109
110 p = peer->metrics;
111 if (inet_metrics_new(peer))
112 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
113
114 new = (unsigned long) p;
115 prev = cmpxchg(&dst->_metrics, old, new);
116
117 if (prev != old) {
118 p = __DST_METRICS_PTR(prev);
119 if (prev & DST_METRICS_READ_ONLY)
120 p = NULL;
121 }
122 }
123 return p;
124}
125
David S. Millerf894cbf2012-07-02 21:52:24 -0700126static inline const void *choose_neigh_daddr(struct rt6_info *rt,
127 struct sk_buff *skb,
128 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500129{
130 struct in6_addr *p = &rt->rt6i_gateway;
131
David S. Millera7563f32012-01-26 16:29:16 -0500132 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500133 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700134 else if (skb)
135 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500136 return daddr;
137}
138
David S. Millerf894cbf2012-07-02 21:52:24 -0700139static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
140 struct sk_buff *skb,
141 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700142{
David S. Miller39232972012-01-26 15:22:32 -0500143 struct rt6_info *rt = (struct rt6_info *) dst;
144 struct neighbour *n;
145
David S. Millerf894cbf2012-07-02 21:52:24 -0700146 daddr = choose_neigh_daddr(rt, skb, daddr);
David S. Miller39232972012-01-26 15:22:32 -0500147 n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500148 if (n)
149 return n;
150 return neigh_create(&nd_tbl, daddr, dst->dev);
151}
152
David S. Miller8ade06c2011-12-29 18:51:57 -0500153static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
David S. Millerf83c7792011-12-28 15:41:23 -0500154{
David S. Miller8ade06c2011-12-29 18:51:57 -0500155 struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
156 if (!n) {
157 n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
158 if (IS_ERR(n))
159 return PTR_ERR(n);
160 }
David S. Miller97cac082012-07-02 22:43:47 -0700161 rt->n = n;
David S. Millerf83c7792011-12-28 15:41:23 -0500162
163 return 0;
David S. Millerd3aaeb32011-07-18 00:40:17 -0700164}
165
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800166static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800168 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 .gc = ip6_dst_gc,
170 .gc_thresh = 1024,
171 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800172 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000173 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800174 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 .destroy = ip6_dst_destroy,
176 .ifdown = ip6_dst_ifdown,
177 .negative_advice = ip6_negative_advice,
178 .link_failure = ip6_link_failure,
179 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700180 .redirect = rt6_do_redirect,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700181 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700182 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183};
184
Steffen Klassertebb762f2011-11-23 02:12:51 +0000185static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800186{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000187 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
188
189 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800190}
191
David S. Miller6700c272012-07-17 03:29:28 -0700192static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
193 struct sk_buff *skb, u32 mtu)
David S. Miller14e50e52007-05-24 18:17:54 -0700194{
195}
196
David S. Miller6700c272012-07-17 03:29:28 -0700197static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
198 struct sk_buff *skb)
David S. Millerb587ee32012-07-12 00:39:24 -0700199{
200}
201
Held Bernhard0972ddb2011-04-24 22:07:32 +0000202static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
203 unsigned long old)
204{
205 return NULL;
206}
207
David S. Miller14e50e52007-05-24 18:17:54 -0700208static struct dst_ops ip6_dst_blackhole_ops = {
209 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800210 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700211 .destroy = ip6_dst_destroy,
212 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000213 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800214 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700215 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700216 .redirect = ip6_rt_blackhole_redirect,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000217 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700218 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700219};
220
David S. Miller62fa8a82011-01-26 20:51:05 -0800221static const u32 ip6_template_metrics[RTAX_MAX] = {
222 [RTAX_HOPLIMIT - 1] = 255,
223};
224
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800225static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700226 .dst = {
227 .__refcnt = ATOMIC_INIT(1),
228 .__use = 1,
229 .obsolete = -1,
230 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700231 .input = ip6_pkt_discard,
232 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 },
234 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700235 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 .rt6i_metric = ~(u32) 0,
237 .rt6i_ref = ATOMIC_INIT(1),
238};
239
Thomas Graf101367c2006-08-04 03:39:02 -0700240#ifdef CONFIG_IPV6_MULTIPLE_TABLES
241
David S. Miller6723ab52006-10-18 21:20:57 -0700242static int ip6_pkt_prohibit(struct sk_buff *skb);
243static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700244
Adrian Bunk280a34c2008-04-21 02:29:32 -0700245static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700246 .dst = {
247 .__refcnt = ATOMIC_INIT(1),
248 .__use = 1,
249 .obsolete = -1,
250 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700251 .input = ip6_pkt_prohibit,
252 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700253 },
254 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700255 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700256 .rt6i_metric = ~(u32) 0,
257 .rt6i_ref = ATOMIC_INIT(1),
258};
259
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800260static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700261 .dst = {
262 .__refcnt = ATOMIC_INIT(1),
263 .__use = 1,
264 .obsolete = -1,
265 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700266 .input = dst_discard,
267 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700268 },
269 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700270 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700271 .rt6i_metric = ~(u32) 0,
272 .rt6i_ref = ATOMIC_INIT(1),
273};
274
275#endif
276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700278static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700279 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700280 int flags,
281 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
David S. Miller97bab732012-06-09 22:36:36 -0700283 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
David S. Millerf5b0a872012-07-19 12:31:33 -0700284 0, DST_OBSOLETE_NONE, flags);
David S. Millercf911662011-04-28 14:31:47 -0700285
David S. Miller97bab732012-06-09 22:36:36 -0700286 if (rt) {
Steffen Klassert81048912012-07-05 23:37:09 +0000287 struct dst_entry *dst = &rt->dst;
288
289 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
David S. Miller8b96d222012-06-11 02:01:56 -0700290 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
David S. Miller97bab732012-06-09 22:36:36 -0700291 }
David S. Millercf911662011-04-28 14:31:47 -0700292 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
295static void ip6_dst_destroy(struct dst_entry *dst)
296{
297 struct rt6_info *rt = (struct rt6_info *)dst;
298 struct inet6_dev *idev = rt->rt6i_idev;
299
David S. Miller97cac082012-07-02 22:43:47 -0700300 if (rt->n)
301 neigh_release(rt->n);
302
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000303 if (!(rt->dst.flags & DST_HOST))
304 dst_destroy_metrics_generic(dst);
305
David S. Miller38308472011-12-03 18:02:47 -0500306 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 rt->rt6i_idev = NULL;
308 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900309 }
Gao feng1716a962012-04-06 00:13:10 +0000310
311 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
312 dst_release(dst->from);
313
David S. Miller97bab732012-06-09 22:36:36 -0700314 if (rt6_has_peer(rt)) {
315 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800316 inet_putpeer(peer);
317 }
318}
319
David S. Miller6431cbc2011-02-07 20:38:06 -0800320static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
321
322static u32 rt6_peer_genid(void)
323{
324 return atomic_read(&__rt6_peer_genid);
325}
326
David S. Millerb3419362010-11-30 12:27:11 -0800327void rt6_bind_peer(struct rt6_info *rt, int create)
328{
David S. Miller97bab732012-06-09 22:36:36 -0700329 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800330 struct inet_peer *peer;
331
David S. Miller97bab732012-06-09 22:36:36 -0700332 base = inetpeer_base_ptr(rt->_rt6i_peer);
333 if (!base)
334 return;
335
336 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700337 if (peer) {
338 if (!rt6_set_peer(rt, peer))
339 inet_putpeer(peer);
340 else
341 rt->rt6i_peer_genid = rt6_peer_genid();
342 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343}
344
345static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
346 int how)
347{
348 struct rt6_info *rt = (struct rt6_info *)dst;
349 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800350 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900351 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
David S. Miller97cac082012-07-02 22:43:47 -0700353 if (dev != loopback_dev) {
354 if (idev && idev->dev == dev) {
355 struct inet6_dev *loopback_idev =
356 in6_dev_get(loopback_dev);
357 if (loopback_idev) {
358 rt->rt6i_idev = loopback_idev;
359 in6_dev_put(idev);
360 }
361 }
362 if (rt->n && rt->n->dev == dev) {
363 rt->n->dev = loopback_dev;
364 dev_hold(loopback_dev);
365 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 }
367 }
368}
369
Eric Dumazeta50feda2012-05-18 18:57:34 +0000370static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371{
Gao feng1716a962012-04-06 00:13:10 +0000372 struct rt6_info *ort = NULL;
373
374 if (rt->rt6i_flags & RTF_EXPIRES) {
375 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000376 return true;
Gao feng1716a962012-04-06 00:13:10 +0000377 } else if (rt->dst.from) {
378 ort = (struct rt6_info *) rt->dst.from;
379 return (ort->rt6i_flags & RTF_EXPIRES) &&
380 time_after(jiffies, ort->dst.expires);
381 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000382 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383}
384
Eric Dumazeta50feda2012-05-18 18:57:34 +0000385static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700386{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000387 return ipv6_addr_type(daddr) &
388 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700389}
390
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700392 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 */
394
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800395static inline struct rt6_info *rt6_device_match(struct net *net,
396 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000397 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700399 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
401 struct rt6_info *local = NULL;
402 struct rt6_info *sprt;
403
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900404 if (!oif && ipv6_addr_any(saddr))
405 goto out;
406
Changli Gaod8d1f302010-06-10 23:31:35 -0700407 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500408 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900409
410 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 if (dev->ifindex == oif)
412 return sprt;
413 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500414 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700416 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900418 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 local->rt6i_idev->dev->ifindex == oif))
420 continue;
421 }
422 local = sprt;
423 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900424 } else {
425 if (ipv6_chk_addr(net, saddr, dev,
426 flags & RT6_LOOKUP_F_IFACE))
427 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900429 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900431 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 if (local)
433 return local;
434
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700435 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800436 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900438out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 return rt;
440}
441
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800442#ifdef CONFIG_IPV6_ROUTER_PREF
443static void rt6_probe(struct rt6_info *rt)
444{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000445 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800446 /*
447 * Okay, this does not seem to be appropriate
448 * for now, however, we need to check if it
449 * is really so; aka Router Reachability Probing.
450 *
451 * Router Reachability Probe MUST be rate-limited
452 * to no more than one per minute.
453 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000454 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700455 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800456 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000457 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800458 read_lock_bh(&neigh->lock);
459 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e163562006-03-20 17:05:47 -0800460 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800461 struct in6_addr mcaddr;
462 struct in6_addr *target;
463
464 neigh->updated = jiffies;
465 read_unlock_bh(&neigh->lock);
466
467 target = (struct in6_addr *)&neigh->primary_key;
468 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500469 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000470 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800471 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000472 }
473out:
474 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800475}
476#else
477static inline void rt6_probe(struct rt6_info *rt)
478{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800479}
480#endif
481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800483 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700485static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486{
David S. Millerd1918542011-12-28 20:19:20 -0500487 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700488 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800489 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700490 if ((dev->flags & IFF_LOOPBACK) &&
491 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
492 return 1;
493 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494}
495
Dave Jonesb6f99a22007-03-22 12:27:49 -0700496static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000498 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800499 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000500
501 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700502 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700503 if (rt->rt6i_flags & RTF_NONEXTHOP ||
504 !(rt->rt6i_flags & RTF_GATEWAY))
505 m = 1;
506 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800507 read_lock_bh(&neigh->lock);
508 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700509 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800510#ifdef CONFIG_IPV6_ROUTER_PREF
511 else if (neigh->nud_state & NUD_FAILED)
512 m = 0;
513#endif
514 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800515 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800516 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800517 } else
518 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000519 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800520 return m;
521}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800523static int rt6_score_route(struct rt6_info *rt, int oif,
524 int strict)
525{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700526 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900527
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700528 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700529 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800530 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800531#ifdef CONFIG_IPV6_ROUTER_PREF
532 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
533#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700534 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800535 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800536 return -1;
537 return m;
538}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
David S. Millerf11e6652007-03-24 20:36:25 -0700540static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
541 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800542{
David S. Millerf11e6652007-03-24 20:36:25 -0700543 int m;
544
545 if (rt6_check_expired(rt))
546 goto out;
547
548 m = rt6_score_route(rt, oif, strict);
549 if (m < 0)
550 goto out;
551
552 if (m > *mpri) {
553 if (strict & RT6_LOOKUP_F_REACHABLE)
554 rt6_probe(match);
555 *mpri = m;
556 match = rt;
557 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
558 rt6_probe(rt);
559 }
560
561out:
562 return match;
563}
564
565static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
566 struct rt6_info *rr_head,
567 u32 metric, int oif, int strict)
568{
569 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800570 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
David S. Millerf11e6652007-03-24 20:36:25 -0700572 match = NULL;
573 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700574 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700575 match = find_match(rt, oif, strict, &mpri, match);
576 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700577 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700578 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800579
David S. Millerf11e6652007-03-24 20:36:25 -0700580 return match;
581}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800582
David S. Millerf11e6652007-03-24 20:36:25 -0700583static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
584{
585 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800586 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
David S. Millerf11e6652007-03-24 20:36:25 -0700588 rt0 = fn->rr_ptr;
589 if (!rt0)
590 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
David S. Millerf11e6652007-03-24 20:36:25 -0700592 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800594 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700595 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700596 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700597
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800598 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700599 if (!next || next->rt6i_metric != rt0->rt6i_metric)
600 next = fn->leaf;
601
602 if (next != rt0)
603 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 }
605
David S. Millerd1918542011-12-28 20:19:20 -0500606 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000607 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608}
609
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800610#ifdef CONFIG_IPV6_ROUTE_INFO
611int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000612 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800613{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900614 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800615 struct route_info *rinfo = (struct route_info *) opt;
616 struct in6_addr prefix_buf, *prefix;
617 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900618 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800619 struct rt6_info *rt;
620
621 if (len < sizeof(struct route_info)) {
622 return -EINVAL;
623 }
624
625 /* Sanity check for prefix_len and length */
626 if (rinfo->length > 3) {
627 return -EINVAL;
628 } else if (rinfo->prefix_len > 128) {
629 return -EINVAL;
630 } else if (rinfo->prefix_len > 64) {
631 if (rinfo->length < 2) {
632 return -EINVAL;
633 }
634 } else if (rinfo->prefix_len > 0) {
635 if (rinfo->length < 1) {
636 return -EINVAL;
637 }
638 }
639
640 pref = rinfo->route_pref;
641 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000642 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800643
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900644 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800645
646 if (rinfo->length == 3)
647 prefix = (struct in6_addr *)rinfo->prefix;
648 else {
649 /* this function is safe */
650 ipv6_addr_prefix(&prefix_buf,
651 (struct in6_addr *)rinfo->prefix,
652 rinfo->prefix_len);
653 prefix = &prefix_buf;
654 }
655
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800656 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
657 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800658
659 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700660 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800661 rt = NULL;
662 }
663
664 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800665 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800666 pref);
667 else if (rt)
668 rt->rt6i_flags = RTF_ROUTEINFO |
669 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
670
671 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000672 if (!addrconf_finite_timeout(lifetime))
673 rt6_clean_expires(rt);
674 else
675 rt6_set_expires(rt, jiffies + HZ * lifetime);
676
Changli Gaod8d1f302010-06-10 23:31:35 -0700677 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800678 }
679 return 0;
680}
681#endif
682
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800683#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700684do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800685 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700686 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700687 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700688 if (fn->fn_flags & RTN_TL_ROOT) \
689 goto out; \
690 pn = fn->parent; \
691 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800692 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700693 else \
694 fn = pn; \
695 if (fn->fn_flags & RTN_RTINFO) \
696 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700697 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700698 } \
David S. Miller38308472011-12-03 18:02:47 -0500699} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700700
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800701static struct rt6_info *ip6_pol_route_lookup(struct net *net,
702 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500703 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704{
705 struct fib6_node *fn;
706 struct rt6_info *rt;
707
Thomas Grafc71099a2006-08-04 23:20:06 -0700708 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500709 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700710restart:
711 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500712 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
713 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700714out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700715 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700716 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700717 return rt;
718
719}
720
Florian Westphalea6e5742011-09-05 16:05:44 +0200721struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
722 int flags)
723{
724 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
725}
726EXPORT_SYMBOL_GPL(ip6_route_lookup);
727
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900728struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
729 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700730{
David S. Miller4c9483b2011-03-12 16:22:43 -0500731 struct flowi6 fl6 = {
732 .flowi6_oif = oif,
733 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700734 };
735 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700736 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700737
Thomas Grafadaa70b2006-10-13 15:01:03 -0700738 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500739 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700740 flags |= RT6_LOOKUP_F_HAS_SADDR;
741 }
742
David S. Miller4c9483b2011-03-12 16:22:43 -0500743 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700744 if (dst->error == 0)
745 return (struct rt6_info *) dst;
746
747 dst_release(dst);
748
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 return NULL;
750}
751
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900752EXPORT_SYMBOL(rt6_lookup);
753
Thomas Grafc71099a2006-08-04 23:20:06 -0700754/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 It takes new route entry, the addition fails by any reason the
756 route is freed. In any case, if caller does not hold it, it may
757 be destroyed.
758 */
759
Thomas Graf86872cb2006-08-22 00:01:08 -0700760static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761{
762 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700763 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Thomas Grafc71099a2006-08-04 23:20:06 -0700765 table = rt->rt6i_table;
766 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700767 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700768 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
770 return err;
771}
772
Thomas Graf40e22e82006-08-22 00:00:45 -0700773int ip6_ins_rt(struct rt6_info *rt)
774{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800775 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500776 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800777 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800778 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700779}
780
Gao feng1716a962012-04-06 00:13:10 +0000781static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000782 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000783 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 struct rt6_info *rt;
786
787 /*
788 * Clone the route.
789 */
790
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000791 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800794 int attempts = !in_softirq();
795
David S. Miller38308472011-12-03 18:02:47 -0500796 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500797 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000798 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900799 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000800 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900801 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
805#ifdef CONFIG_IPV6_SUBTREES
806 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000807 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 rt->rt6i_src.plen = 128;
809 }
810#endif
811
David S. Miller14deae42009-01-04 16:04:39 -0800812 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500813 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500814 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800815 int saved_rt_min_interval =
816 net->ipv6.sysctl.ip6_rt_gc_min_interval;
817 int saved_rt_elasticity =
818 net->ipv6.sysctl.ip6_rt_gc_elasticity;
819
820 if (attempts-- > 0) {
821 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
822 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
823
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000824 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800825
826 net->ipv6.sysctl.ip6_rt_gc_elasticity =
827 saved_rt_elasticity;
828 net->ipv6.sysctl.ip6_rt_gc_min_interval =
829 saved_rt_min_interval;
830 goto retry;
831 }
832
Joe Perchesf3213832012-05-15 14:11:53 +0000833 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700834 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800835 return NULL;
836 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800837 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800839 return rt;
840}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000842static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
843 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800844{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000845 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
846
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800847 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800848 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700849 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800850 }
851 return rt;
852}
853
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800854static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500855 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856{
857 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800858 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700859 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800861 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700862 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700864 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
866relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700867 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800869restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500870 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
872restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700873 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800874
David S. Miller4c9483b2011-03-12 16:22:43 -0500875 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800876 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800877 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800878 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
Changli Gaod8d1f302010-06-10 23:31:35 -0700880 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700881 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800882
David S. Miller97cac082012-07-02 22:43:47 -0700883 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500884 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800885 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500886 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800887 else
888 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800889
Changli Gaod8d1f302010-06-10 23:31:35 -0700890 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800891 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800892
Changli Gaod8d1f302010-06-10 23:31:35 -0700893 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800894 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700895 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800896 if (!err)
897 goto out2;
898 }
899
900 if (--attempts <= 0)
901 goto out2;
902
903 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700904 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800905 * released someone could insert this route. Relookup.
906 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700907 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800908 goto relookup;
909
910out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800911 if (reachable) {
912 reachable = 0;
913 goto restart_2;
914 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700915 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700916 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700918 rt->dst.lastuse = jiffies;
919 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700920
921 return rt;
922}
923
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800924static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500925 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700926{
David S. Miller4c9483b2011-03-12 16:22:43 -0500927 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700928}
929
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000930static struct dst_entry *ip6_route_input_lookup(struct net *net,
931 struct net_device *dev,
932 struct flowi6 *fl6, int flags)
933{
934 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
935 flags |= RT6_LOOKUP_F_IFACE;
936
937 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
938}
939
Thomas Grafc71099a2006-08-04 23:20:06 -0700940void ip6_route_input(struct sk_buff *skb)
941{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000942 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900943 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700944 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500945 struct flowi6 fl6 = {
946 .flowi6_iif = skb->dev->ifindex,
947 .daddr = iph->daddr,
948 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500949 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500950 .flowi6_mark = skb->mark,
951 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700952 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700953
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000954 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700955}
956
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800957static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500958 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700959{
David S. Miller4c9483b2011-03-12 16:22:43 -0500960 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700961}
962
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700963struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500964 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700965{
966 int flags = 0;
967
Pavel Emelyanov1fb94892012-08-08 21:53:36 +0000968 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +0000969
David S. Miller4c9483b2011-03-12 16:22:43 -0500970 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700971 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700972
David S. Miller4c9483b2011-03-12 16:22:43 -0500973 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700974 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000975 else if (sk)
976 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700977
David S. Miller4c9483b2011-03-12 16:22:43 -0500978 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979}
980
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900981EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
David S. Miller2774c132011-03-01 14:59:04 -0800983struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700984{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700985 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700986 struct dst_entry *new = NULL;
987
David S. Millerf5b0a872012-07-19 12:31:33 -0700988 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700989 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700990 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700991
Steffen Klassert81048912012-07-05 23:37:09 +0000992 memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
993 rt6_init_peer(rt, net->ipv6.peers);
994
David S. Miller14e50e52007-05-24 18:17:54 -0700995 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800996 new->input = dst_discard;
997 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700998
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000999 if (dst_metrics_read_only(&ort->dst))
1000 new->_metrics = ort->dst._metrics;
1001 else
1002 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07001003 rt->rt6i_idev = ort->rt6i_idev;
1004 if (rt->rt6i_idev)
1005 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -07001006
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001007 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001008 rt->rt6i_flags = ort->rt6i_flags;
1009 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001010 rt->rt6i_metric = 0;
1011
1012 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1013#ifdef CONFIG_IPV6_SUBTREES
1014 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1015#endif
1016
1017 dst_free(new);
1018 }
1019
David S. Miller69ead7a2011-03-01 14:45:33 -08001020 dst_release(dst_orig);
1021 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001022}
David S. Miller14e50e52007-05-24 18:17:54 -07001023
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024/*
1025 * Destination cache support functions
1026 */
1027
1028static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1029{
1030 struct rt6_info *rt;
1031
1032 rt = (struct rt6_info *) dst;
1033
David S. Miller6431cbc2011-02-07 20:38:06 -08001034 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1035 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001036 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001037 rt6_bind_peer(rt, 0);
1038 rt->rt6i_peer_genid = rt6_peer_genid();
1039 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001041 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 return NULL;
1043}
1044
1045static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1046{
1047 struct rt6_info *rt = (struct rt6_info *) dst;
1048
1049 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001050 if (rt->rt6i_flags & RTF_CACHE) {
1051 if (rt6_check_expired(rt)) {
1052 ip6_del_rt(rt);
1053 dst = NULL;
1054 }
1055 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001057 dst = NULL;
1058 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001060 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061}
1062
1063static void ip6_link_failure(struct sk_buff *skb)
1064{
1065 struct rt6_info *rt;
1066
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001067 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
Eric Dumazetadf30902009-06-02 05:19:30 +00001069 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001071 if (rt->rt6i_flags & RTF_CACHE)
1072 rt6_update_expires(rt, 0);
1073 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 rt->rt6i_node->fn_sernum = -1;
1075 }
1076}
1077
David S. Miller6700c272012-07-17 03:29:28 -07001078static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
1079 struct sk_buff *skb, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080{
1081 struct rt6_info *rt6 = (struct rt6_info*)dst;
1082
David S. Miller81aded22012-06-15 14:54:11 -07001083 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001085 struct net *net = dev_net(dst->dev);
1086
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 rt6->rt6i_flags |= RTF_MODIFIED;
1088 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001089 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001091 features |= RTAX_FEATURE_ALLFRAG;
1092 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 }
David S. Millerdefb3512010-12-08 21:16:57 -08001094 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001095 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 }
1097}
1098
David S. Miller42ae66c2012-06-15 20:01:57 -07001099void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1100 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001101{
1102 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1103 struct dst_entry *dst;
1104 struct flowi6 fl6;
1105
1106 memset(&fl6, 0, sizeof(fl6));
1107 fl6.flowi6_oif = oif;
1108 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001109 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001110 fl6.daddr = iph->daddr;
1111 fl6.saddr = iph->saddr;
1112 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1113
1114 dst = ip6_route_output(net, NULL, &fl6);
1115 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001116 ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07001117 dst_release(dst);
1118}
1119EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1120
1121void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1122{
1123 ip6_update_pmtu(skb, sock_net(sk), mtu,
1124 sk->sk_bound_dev_if, sk->sk_mark);
1125}
1126EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1127
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001128void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1129{
1130 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1131 struct dst_entry *dst;
1132 struct flowi6 fl6;
1133
1134 memset(&fl6, 0, sizeof(fl6));
1135 fl6.flowi6_oif = oif;
1136 fl6.flowi6_mark = mark;
1137 fl6.flowi6_flags = 0;
1138 fl6.daddr = iph->daddr;
1139 fl6.saddr = iph->saddr;
1140 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1141
1142 dst = ip6_route_output(net, NULL, &fl6);
1143 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001144 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001145 dst_release(dst);
1146}
1147EXPORT_SYMBOL_GPL(ip6_redirect);
1148
1149void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1150{
1151 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1152}
1153EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1154
David S. Miller0dbaee32010-12-13 12:52:14 -08001155static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156{
David S. Miller0dbaee32010-12-13 12:52:14 -08001157 struct net_device *dev = dst->dev;
1158 unsigned int mtu = dst_mtu(dst);
1159 struct net *net = dev_net(dev);
1160
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1162
Daniel Lezcano55786892008-03-04 13:47:47 -08001163 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1164 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165
1166 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001167 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1168 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1169 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 * rely only on pmtu discovery"
1171 */
1172 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1173 mtu = IPV6_MAXPLEN;
1174 return mtu;
1175}
1176
Steffen Klassertebb762f2011-11-23 02:12:51 +00001177static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001178{
David S. Millerd33e4552010-12-14 13:01:14 -08001179 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001180 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1181
1182 if (mtu)
1183 return mtu;
1184
1185 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001186
1187 rcu_read_lock();
1188 idev = __in6_dev_get(dst->dev);
1189 if (idev)
1190 mtu = idev->cnf.mtu6;
1191 rcu_read_unlock();
1192
1193 return mtu;
1194}
1195
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001196static struct dst_entry *icmp6_dst_gc_list;
1197static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001198
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001199struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001201 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202{
David S. Miller87a11572011-12-06 17:04:13 -05001203 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 struct rt6_info *rt;
1205 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001206 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207
David S. Miller38308472011-12-03 18:02:47 -05001208 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001209 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
David S. Miller8b96d222012-06-11 02:01:56 -07001211 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001212 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001214 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 goto out;
1216 }
1217
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 if (neigh)
1219 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001220 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001221 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001222 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001223 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001224 dst_free(&rt->dst);
1225 return ERR_CAST(neigh);
1226 }
David S. Miller14deae42009-01-04 16:04:39 -08001227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001229 rt->dst.flags |= DST_HOST;
1230 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001231 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001232 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001233 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001234 rt->rt6i_dst.plen = 128;
1235 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001236 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001238 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001239 rt->dst.next = icmp6_dst_gc_list;
1240 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001241 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
Daniel Lezcano55786892008-03-04 13:47:47 -08001243 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
David S. Miller87a11572011-12-06 17:04:13 -05001245 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1246
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247out:
David S. Miller87a11572011-12-06 17:04:13 -05001248 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249}
1250
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001251int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252{
Hagen Paul Pfeifere9476e952011-02-25 05:45:19 +00001253 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001254 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001256 spin_lock_bh(&icmp6_dst_lock);
1257 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001258
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 while ((dst = *pprev) != NULL) {
1260 if (!atomic_read(&dst->__refcnt)) {
1261 *pprev = dst->next;
1262 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 } else {
1264 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001265 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 }
1267 }
1268
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001269 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001270
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001271 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272}
1273
David S. Miller1e493d12008-09-10 17:27:15 -07001274static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1275 void *arg)
1276{
1277 struct dst_entry *dst, **pprev;
1278
1279 spin_lock_bh(&icmp6_dst_lock);
1280 pprev = &icmp6_dst_gc_list;
1281 while ((dst = *pprev) != NULL) {
1282 struct rt6_info *rt = (struct rt6_info *) dst;
1283 if (func(rt, arg)) {
1284 *pprev = dst->next;
1285 dst_free(dst);
1286 } else {
1287 pprev = &dst->next;
1288 }
1289 }
1290 spin_unlock_bh(&icmp6_dst_lock);
1291}
1292
Daniel Lezcano569d3642008-01-18 03:56:57 -08001293static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001296 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001297 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1298 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1299 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1300 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1301 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001302 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
Eric Dumazetfc66f952010-10-08 06:37:34 +00001304 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001305 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001306 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 goto out;
1308
Benjamin Thery6891a342008-03-04 13:49:47 -08001309 net->ipv6.ip6_rt_gc_expire++;
1310 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1311 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001312 entries = dst_entries_get_slow(ops);
1313 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001314 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001316 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001317 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318}
1319
1320/* Clean host part of a prefix. Not necessary in radix tree,
1321 but results in cleaner routing tables.
1322
1323 Remove it only when all the things will work!
1324 */
1325
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001326int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327{
David S. Miller5170ae82010-12-12 21:35:57 -08001328 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001329 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001330 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001331 struct inet6_dev *idev;
1332
1333 rcu_read_lock();
1334 idev = __in6_dev_get(dev);
1335 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001336 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001337 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001338 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001339 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 }
1341 return hoplimit;
1342}
David S. Millerabbf46a2010-12-12 21:14:46 -08001343EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
1345/*
1346 *
1347 */
1348
Thomas Graf86872cb2006-08-22 00:01:08 -07001349int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350{
1351 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001352 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 struct rt6_info *rt = NULL;
1354 struct net_device *dev = NULL;
1355 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001356 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 int addr_type;
1358
Thomas Graf86872cb2006-08-22 00:01:08 -07001359 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 return -EINVAL;
1361#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001362 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return -EINVAL;
1364#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001365 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001367 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 if (!dev)
1369 goto out;
1370 idev = in6_dev_get(dev);
1371 if (!idev)
1372 goto out;
1373 }
1374
Thomas Graf86872cb2006-08-22 00:01:08 -07001375 if (cfg->fc_metric == 0)
1376 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Matti Vaittinend71314b2011-11-14 00:14:49 +00001378 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001379 if (cfg->fc_nlinfo.nlh &&
1380 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001381 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001382 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001383 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001384 table = fib6_new_table(net, cfg->fc_table);
1385 }
1386 } else {
1387 table = fib6_new_table(net, cfg->fc_table);
1388 }
David S. Miller38308472011-12-03 18:02:47 -05001389
1390 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001391 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001392
David S. Miller8b96d222012-06-11 02:01:56 -07001393 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
David S. Miller38308472011-12-03 18:02:47 -05001395 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 err = -ENOMEM;
1397 goto out;
1398 }
1399
Changli Gaod8d1f302010-06-10 23:31:35 -07001400 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001401
1402 if (cfg->fc_flags & RTF_EXPIRES)
1403 rt6_set_expires(rt, jiffies +
1404 clock_t_to_jiffies(cfg->fc_expires));
1405 else
1406 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
Thomas Graf86872cb2006-08-22 00:01:08 -07001408 if (cfg->fc_protocol == RTPROT_UNSPEC)
1409 cfg->fc_protocol = RTPROT_BOOT;
1410 rt->rt6i_protocol = cfg->fc_protocol;
1411
1412 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
1414 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001415 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001416 else if (cfg->fc_flags & RTF_LOCAL)
1417 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001419 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
Changli Gaod8d1f302010-06-10 23:31:35 -07001421 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
Thomas Graf86872cb2006-08-22 00:01:08 -07001423 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1424 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001426 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001428 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1429 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1430 if (!metrics) {
1431 err = -ENOMEM;
1432 goto out;
1433 }
1434 dst_init_metrics(&rt->dst, metrics, 0);
1435 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001437 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1438 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439#endif
1440
Thomas Graf86872cb2006-08-22 00:01:08 -07001441 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 /* We cannot add true routes via loopback here,
1444 they would result in kernel looping; promote them to reject routes
1445 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001446 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001447 (dev && (dev->flags & IFF_LOOPBACK) &&
1448 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1449 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001451 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 if (dev) {
1453 dev_put(dev);
1454 in6_dev_put(idev);
1455 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001456 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 dev_hold(dev);
1458 idev = in6_dev_get(dev);
1459 if (!idev) {
1460 err = -ENODEV;
1461 goto out;
1462 }
1463 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001464 rt->dst.output = ip6_pkt_discard_out;
1465 rt->dst.input = ip6_pkt_discard;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001467 switch (cfg->fc_type) {
1468 case RTN_BLACKHOLE:
1469 rt->dst.error = -EINVAL;
1470 break;
1471 case RTN_PROHIBIT:
1472 rt->dst.error = -EACCES;
1473 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00001474 case RTN_THROW:
1475 rt->dst.error = -EAGAIN;
1476 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001477 default:
1478 rt->dst.error = -ENETUNREACH;
1479 break;
1480 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 goto install_route;
1482 }
1483
Thomas Graf86872cb2006-08-22 00:01:08 -07001484 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001485 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 int gwa_type;
1487
Thomas Graf86872cb2006-08-22 00:01:08 -07001488 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001489 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 gwa_type = ipv6_addr_type(gw_addr);
1491
1492 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1493 struct rt6_info *grt;
1494
1495 /* IPv6 strictly inhibits using not link-local
1496 addresses as nexthop address.
1497 Otherwise, router will not able to send redirects.
1498 It is very good, but in some (rare!) circumstances
1499 (SIT, PtP, NBMA NOARP links) it is handy to allow
1500 some exceptions. --ANK
1501 */
1502 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001503 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 goto out;
1505
Daniel Lezcano55786892008-03-04 13:47:47 -08001506 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507
1508 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001509 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 goto out;
1511 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001512 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001513 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 goto out;
1515 }
1516 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001517 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 idev = grt->rt6i_idev;
1519 dev_hold(dev);
1520 in6_dev_hold(grt->rt6i_idev);
1521 }
David S. Miller38308472011-12-03 18:02:47 -05001522 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001524 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
1526 if (err)
1527 goto out;
1528 }
1529 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001530 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 goto out;
1532 }
1533
1534 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001535 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 goto out;
1537
Daniel Walterc3968a82011-04-13 21:10:57 +00001538 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1539 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1540 err = -EINVAL;
1541 goto out;
1542 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001543 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001544 rt->rt6i_prefsrc.plen = 128;
1545 } else
1546 rt->rt6i_prefsrc.plen = 0;
1547
Thomas Graf86872cb2006-08-22 00:01:08 -07001548 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001549 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001550 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 }
1553
Thomas Graf86872cb2006-08-22 00:01:08 -07001554 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555
1556install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001557 if (cfg->fc_mx) {
1558 struct nlattr *nla;
1559 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560
Thomas Graf86872cb2006-08-22 00:01:08 -07001561 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001562 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001563
1564 if (type) {
1565 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 err = -EINVAL;
1567 goto out;
1568 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001569
David S. Millerdefb3512010-12-08 21:16:57 -08001570 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 }
1573 }
1574
Changli Gaod8d1f302010-06-10 23:31:35 -07001575 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001577 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001578
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001579 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001580
Thomas Graf86872cb2006-08-22 00:01:08 -07001581 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
1583out:
1584 if (dev)
1585 dev_put(dev);
1586 if (idev)
1587 in6_dev_put(idev);
1588 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001589 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 return err;
1591}
1592
Thomas Graf86872cb2006-08-22 00:01:08 -07001593static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594{
1595 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001596 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001597 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001599 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001600 return -ENOENT;
1601
Thomas Grafc71099a2006-08-04 23:20:06 -07001602 table = rt->rt6i_table;
1603 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Thomas Graf86872cb2006-08-22 00:01:08 -07001605 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001606 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
Thomas Grafc71099a2006-08-04 23:20:06 -07001608 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
1610 return err;
1611}
1612
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001613int ip6_del_rt(struct rt6_info *rt)
1614{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001615 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001616 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001617 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001618 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001619}
1620
Thomas Graf86872cb2006-08-22 00:01:08 -07001621static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622{
Thomas Grafc71099a2006-08-04 23:20:06 -07001623 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 struct fib6_node *fn;
1625 struct rt6_info *rt;
1626 int err = -ESRCH;
1627
Daniel Lezcano55786892008-03-04 13:47:47 -08001628 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001629 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001630 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
Thomas Grafc71099a2006-08-04 23:20:06 -07001632 read_lock_bh(&table->tb6_lock);
1633
1634 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001635 &cfg->fc_dst, cfg->fc_dst_len,
1636 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001637
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001639 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001640 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001641 (!rt->dst.dev ||
1642 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001644 if (cfg->fc_flags & RTF_GATEWAY &&
1645 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001647 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001649 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001650 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Thomas Graf86872cb2006-08-22 00:01:08 -07001652 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 }
1654 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001655 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
1657 return err;
1658}
1659
David S. Miller6700c272012-07-17 03:29:28 -07001660static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001661{
David S. Millere8599ff2012-07-11 23:43:53 -07001662 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001663 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001664 struct rt6_info *rt, *nrt = NULL;
1665 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001666 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001667 const struct in6_addr *dest;
1668 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001669 struct inet6_dev *in6_dev;
1670 struct neighbour *neigh;
1671 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001672 int optlen, on_link;
1673 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001674
1675 optlen = skb->tail - skb->transport_header;
1676 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1677
1678 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001679 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001680 return;
1681 }
1682
1683 icmph = icmp6_hdr(skb);
1684 target = (const struct in6_addr *) (icmph + 1);
1685 dest = target + 1;
1686
1687 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001688 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001689 return;
1690 }
1691
David S. Miller6e157b62012-07-12 00:05:02 -07001692 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001693 if (ipv6_addr_equal(dest, target)) {
1694 on_link = 1;
1695 } else if (ipv6_addr_type(target) !=
1696 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001697 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001698 return;
1699 }
1700
1701 in6_dev = __in6_dev_get(skb->dev);
1702 if (!in6_dev)
1703 return;
1704 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1705 return;
1706
1707 /* RFC2461 8.1:
1708 * The IP source address of the Redirect MUST be the same as the current
1709 * first-hop router for the specified ICMP Destination Address.
1710 */
1711
1712 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1713 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1714 return;
1715 }
David S. Miller6e157b62012-07-12 00:05:02 -07001716
1717 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001718 if (ndopts.nd_opts_tgt_lladdr) {
1719 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1720 skb->dev);
1721 if (!lladdr) {
1722 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1723 return;
1724 }
1725 }
1726
David S. Miller6e157b62012-07-12 00:05:02 -07001727 rt = (struct rt6_info *) dst;
1728 if (rt == net->ipv6.ip6_null_entry) {
1729 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1730 return;
1731 }
1732
1733 /* Redirect received -> path was valid.
1734 * Look, redirects are sent only in response to data packets,
1735 * so that this nexthop apparently is reachable. --ANK
1736 */
1737 dst_confirm(&rt->dst);
1738
David S. Millere8599ff2012-07-11 23:43:53 -07001739 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1740 if (!neigh)
1741 return;
1742
David S. Miller6e157b62012-07-12 00:05:02 -07001743 /* Duplicate redirect: silently ignore. */
1744 old_neigh = rt->n;
1745 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001746 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 /*
1749 * We have finally decided to accept it.
1750 */
1751
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001752 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1754 NEIGH_UPDATE_F_OVERRIDE|
1755 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1756 NEIGH_UPDATE_F_ISROUTER))
1757 );
1758
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001759 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001760 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 goto out;
1762
1763 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1764 if (on_link)
1765 nrt->rt6i_flags &= ~RTF_GATEWAY;
1766
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001767 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001768 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769
Thomas Graf40e22e82006-08-22 00:00:45 -07001770 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 goto out;
1772
Changli Gaod8d1f302010-06-10 23:31:35 -07001773 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001774 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001775 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001776 netevent.new_neigh = neigh;
1777 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001778 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1779
David S. Miller38308472011-12-03 18:02:47 -05001780 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001781 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001782 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 }
1784
1785out:
David S. Millere8599ff2012-07-11 23:43:53 -07001786 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001787}
1788
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 * Misc support functions
1791 */
1792
Gao feng1716a962012-04-06 00:13:10 +00001793static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001794 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795{
David S. Millerd1918542011-12-28 20:19:20 -05001796 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001797 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1798 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
1800 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001801 rt->dst.input = ort->dst.input;
1802 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001803 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001805 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001806 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001807 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001808 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 rt->rt6i_idev = ort->rt6i_idev;
1810 if (rt->rt6i_idev)
1811 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001812 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001814 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001815 rt->rt6i_flags = ort->rt6i_flags;
1816 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1817 (RTF_DEFAULT | RTF_ADDRCONF))
1818 rt6_set_from(rt, ort);
1819 else
1820 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 rt->rt6i_metric = 0;
1822
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823#ifdef CONFIG_IPV6_SUBTREES
1824 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1825#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001826 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001827 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828 }
1829 return rt;
1830}
1831
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001832#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001833static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001834 const struct in6_addr *prefix, int prefixlen,
1835 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001836{
1837 struct fib6_node *fn;
1838 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001839 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001840
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001841 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001842 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001843 return NULL;
1844
1845 write_lock_bh(&table->tb6_lock);
1846 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001847 if (!fn)
1848 goto out;
1849
Changli Gaod8d1f302010-06-10 23:31:35 -07001850 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001851 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001852 continue;
1853 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1854 continue;
1855 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1856 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001857 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001858 break;
1859 }
1860out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001861 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001862 return rt;
1863}
1864
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001865static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001866 const struct in6_addr *prefix, int prefixlen,
1867 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001868 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001869{
Thomas Graf86872cb2006-08-22 00:01:08 -07001870 struct fib6_config cfg = {
1871 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001872 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001873 .fc_ifindex = ifindex,
1874 .fc_dst_len = prefixlen,
1875 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1876 RTF_UP | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001877 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001878 .fc_nlinfo.nlh = NULL,
1879 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001880 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001881
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001882 cfg.fc_dst = *prefix;
1883 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001884
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001885 /* We should treat it as a default route if prefix length is 0. */
1886 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001887 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001888
Thomas Graf86872cb2006-08-22 00:01:08 -07001889 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001890
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001891 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001892}
1893#endif
1894
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001895struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001896{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001898 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001900 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001901 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001902 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903
Thomas Grafc71099a2006-08-04 23:20:06 -07001904 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001905 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001906 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001907 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1909 break;
1910 }
1911 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001912 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001913 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914 return rt;
1915}
1916
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001917struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001918 struct net_device *dev,
1919 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920{
Thomas Graf86872cb2006-08-22 00:01:08 -07001921 struct fib6_config cfg = {
1922 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001923 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001924 .fc_ifindex = dev->ifindex,
1925 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1926 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001927 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08001928 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001929 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001930 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001932 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Thomas Graf86872cb2006-08-22 00:01:08 -07001934 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 return rt6_get_dflt_router(gwaddr, dev);
1937}
1938
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001939void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940{
1941 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001942 struct fib6_table *table;
1943
1944 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001945 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001946 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001947 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948
1949restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001950 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001951 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001953 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001954 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001955 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 goto restart;
1957 }
1958 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001959 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960}
1961
Daniel Lezcano55786892008-03-04 13:47:47 -08001962static void rtmsg_to_fib6_config(struct net *net,
1963 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001964 struct fib6_config *cfg)
1965{
1966 memset(cfg, 0, sizeof(*cfg));
1967
1968 cfg->fc_table = RT6_TABLE_MAIN;
1969 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1970 cfg->fc_metric = rtmsg->rtmsg_metric;
1971 cfg->fc_expires = rtmsg->rtmsg_info;
1972 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1973 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1974 cfg->fc_flags = rtmsg->rtmsg_flags;
1975
Daniel Lezcano55786892008-03-04 13:47:47 -08001976 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001977
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001978 cfg->fc_dst = rtmsg->rtmsg_dst;
1979 cfg->fc_src = rtmsg->rtmsg_src;
1980 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001981}
1982
Daniel Lezcano55786892008-03-04 13:47:47 -08001983int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984{
Thomas Graf86872cb2006-08-22 00:01:08 -07001985 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 struct in6_rtmsg rtmsg;
1987 int err;
1988
1989 switch(cmd) {
1990 case SIOCADDRT: /* Add a route */
1991 case SIOCDELRT: /* Delete a route */
1992 if (!capable(CAP_NET_ADMIN))
1993 return -EPERM;
1994 err = copy_from_user(&rtmsg, arg,
1995 sizeof(struct in6_rtmsg));
1996 if (err)
1997 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001998
Daniel Lezcano55786892008-03-04 13:47:47 -08001999 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07002000
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 rtnl_lock();
2002 switch (cmd) {
2003 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002004 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 break;
2006 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002007 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 break;
2009 default:
2010 err = -EINVAL;
2011 }
2012 rtnl_unlock();
2013
2014 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002015 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
2017 return -EINVAL;
2018}
2019
2020/*
2021 * Drop the packet on the floor
2022 */
2023
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002024static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002026 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002027 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002028 switch (ipstats_mib_noroutes) {
2029 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002030 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002031 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002032 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2033 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002034 break;
2035 }
2036 /* FALLTHROUGH */
2037 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002038 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2039 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002040 break;
2041 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002042 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 kfree_skb(skb);
2044 return 0;
2045}
2046
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002047static int ip6_pkt_discard(struct sk_buff *skb)
2048{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002049 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002050}
2051
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002052static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053{
Eric Dumazetadf30902009-06-02 05:19:30 +00002054 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002055 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056}
2057
David S. Miller6723ab52006-10-18 21:20:57 -07002058#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2059
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002060static int ip6_pkt_prohibit(struct sk_buff *skb)
2061{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002062 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002063}
2064
2065static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2066{
Eric Dumazetadf30902009-06-02 05:19:30 +00002067 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002068 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002069}
2070
David S. Miller6723ab52006-10-18 21:20:57 -07002071#endif
2072
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073/*
2074 * Allocate a dst for local (unicast / anycast) address.
2075 */
2076
2077struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2078 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002079 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002081 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002082 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002083 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084
David S. Miller38308472011-12-03 18:02:47 -05002085 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002086 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002088 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 in6_dev_hold(idev);
2091
David S. Miller11d53b42011-06-24 15:23:34 -07002092 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002093 rt->dst.input = ip6_input;
2094 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002096 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097
2098 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002099 if (anycast)
2100 rt->rt6i_flags |= RTF_ANYCAST;
2101 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002103 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002104 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002105 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002106 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 }
2108
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002109 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002111 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112
Changli Gaod8d1f302010-06-10 23:31:35 -07002113 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114
2115 return rt;
2116}
2117
Daniel Walterc3968a82011-04-13 21:10:57 +00002118int ip6_route_get_saddr(struct net *net,
2119 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002120 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002121 unsigned int prefs,
2122 struct in6_addr *saddr)
2123{
2124 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2125 int err = 0;
2126 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002127 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002128 else
2129 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2130 daddr, prefs, saddr);
2131 return err;
2132}
2133
2134/* remove deleted ip from prefsrc entries */
2135struct arg_dev_net_ip {
2136 struct net_device *dev;
2137 struct net *net;
2138 struct in6_addr *addr;
2139};
2140
2141static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2142{
2143 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2144 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2145 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2146
David S. Millerd1918542011-12-28 20:19:20 -05002147 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002148 rt != net->ipv6.ip6_null_entry &&
2149 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2150 /* remove prefsrc entry */
2151 rt->rt6i_prefsrc.plen = 0;
2152 }
2153 return 0;
2154}
2155
2156void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2157{
2158 struct net *net = dev_net(ifp->idev->dev);
2159 struct arg_dev_net_ip adni = {
2160 .dev = ifp->idev->dev,
2161 .net = net,
2162 .addr = &ifp->addr,
2163 };
2164 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2165}
2166
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002167struct arg_dev_net {
2168 struct net_device *dev;
2169 struct net *net;
2170};
2171
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172static int fib6_ifdown(struct rt6_info *rt, void *arg)
2173{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002174 const struct arg_dev_net *adn = arg;
2175 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002176
David S. Millerd1918542011-12-28 20:19:20 -05002177 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002178 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002180
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181 return 0;
2182}
2183
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002184void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002186 struct arg_dev_net adn = {
2187 .dev = dev,
2188 .net = net,
2189 };
2190
2191 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002192 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193}
2194
Eric Dumazet95c96172012-04-15 05:58:06 +00002195struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002197 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198};
2199
2200static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2201{
2202 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2203 struct inet6_dev *idev;
2204
2205 /* In IPv6 pmtu discovery is not optional,
2206 so that RTAX_MTU lock cannot disable it.
2207 We still use this lock to block changes
2208 caused by addrconf/ndisc.
2209 */
2210
2211 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002212 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 return 0;
2214
2215 /* For administrative MTU increase, there is no way to discover
2216 IPv6 PMTU increase, so PMTU increase should be updated here.
2217 Since RFC 1981 doesn't include administrative MTU increase
2218 update PMTU increase is a MUST. (i.e. jumbo frame)
2219 */
2220 /*
2221 If new MTU is less than route PMTU, this new MTU will be the
2222 lowest MTU in the path, update the route PMTU to reflect PMTU
2223 decreases; if new MTU is greater than route PMTU, and the
2224 old MTU is the lowest MTU in the path, update the route PMTU
2225 to reflect the increase. In this case if the other nodes' MTU
2226 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2227 PMTU discouvery.
2228 */
David S. Millerd1918542011-12-28 20:19:20 -05002229 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002230 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2231 (dst_mtu(&rt->dst) >= arg->mtu ||
2232 (dst_mtu(&rt->dst) < arg->mtu &&
2233 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002234 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002235 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 return 0;
2237}
2238
Eric Dumazet95c96172012-04-15 05:58:06 +00002239void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240{
Thomas Grafc71099a2006-08-04 23:20:06 -07002241 struct rt6_mtu_change_arg arg = {
2242 .dev = dev,
2243 .mtu = mtu,
2244 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002246 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247}
2248
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002249static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002250 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002251 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002252 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002253 [RTA_PRIORITY] = { .type = NLA_U32 },
2254 [RTA_METRICS] = { .type = NLA_NESTED },
2255};
2256
2257static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2258 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259{
Thomas Graf86872cb2006-08-22 00:01:08 -07002260 struct rtmsg *rtm;
2261 struct nlattr *tb[RTA_MAX+1];
2262 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263
Thomas Graf86872cb2006-08-22 00:01:08 -07002264 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2265 if (err < 0)
2266 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267
Thomas Graf86872cb2006-08-22 00:01:08 -07002268 err = -EINVAL;
2269 rtm = nlmsg_data(nlh);
2270 memset(cfg, 0, sizeof(*cfg));
2271
2272 cfg->fc_table = rtm->rtm_table;
2273 cfg->fc_dst_len = rtm->rtm_dst_len;
2274 cfg->fc_src_len = rtm->rtm_src_len;
2275 cfg->fc_flags = RTF_UP;
2276 cfg->fc_protocol = rtm->rtm_protocol;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002277 cfg->fc_type = rtm->rtm_type;
Thomas Graf86872cb2006-08-22 00:01:08 -07002278
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002279 if (rtm->rtm_type == RTN_UNREACHABLE ||
2280 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002281 rtm->rtm_type == RTN_PROHIBIT ||
2282 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07002283 cfg->fc_flags |= RTF_REJECT;
2284
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002285 if (rtm->rtm_type == RTN_LOCAL)
2286 cfg->fc_flags |= RTF_LOCAL;
2287
Eric W. Biederman15e47302012-09-07 20:12:54 +00002288 cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
Thomas Graf86872cb2006-08-22 00:01:08 -07002289 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002290 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002291
2292 if (tb[RTA_GATEWAY]) {
2293 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2294 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002296
2297 if (tb[RTA_DST]) {
2298 int plen = (rtm->rtm_dst_len + 7) >> 3;
2299
2300 if (nla_len(tb[RTA_DST]) < plen)
2301 goto errout;
2302
2303 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002305
2306 if (tb[RTA_SRC]) {
2307 int plen = (rtm->rtm_src_len + 7) >> 3;
2308
2309 if (nla_len(tb[RTA_SRC]) < plen)
2310 goto errout;
2311
2312 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002314
Daniel Walterc3968a82011-04-13 21:10:57 +00002315 if (tb[RTA_PREFSRC])
2316 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2317
Thomas Graf86872cb2006-08-22 00:01:08 -07002318 if (tb[RTA_OIF])
2319 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2320
2321 if (tb[RTA_PRIORITY])
2322 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2323
2324 if (tb[RTA_METRICS]) {
2325 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2326 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002328
2329 if (tb[RTA_TABLE])
2330 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2331
2332 err = 0;
2333errout:
2334 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335}
2336
Thomas Grafc127ea22007-03-22 11:58:32 -07002337static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338{
Thomas Graf86872cb2006-08-22 00:01:08 -07002339 struct fib6_config cfg;
2340 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341
Thomas Graf86872cb2006-08-22 00:01:08 -07002342 err = rtm_to_fib6_config(skb, nlh, &cfg);
2343 if (err < 0)
2344 return err;
2345
2346 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347}
2348
Thomas Grafc127ea22007-03-22 11:58:32 -07002349static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350{
Thomas Graf86872cb2006-08-22 00:01:08 -07002351 struct fib6_config cfg;
2352 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353
Thomas Graf86872cb2006-08-22 00:01:08 -07002354 err = rtm_to_fib6_config(skb, nlh, &cfg);
2355 if (err < 0)
2356 return err;
2357
2358 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359}
2360
Thomas Graf339bf982006-11-10 14:10:15 -08002361static inline size_t rt6_nlmsg_size(void)
2362{
2363 return NLMSG_ALIGN(sizeof(struct rtmsg))
2364 + nla_total_size(16) /* RTA_SRC */
2365 + nla_total_size(16) /* RTA_DST */
2366 + nla_total_size(16) /* RTA_GATEWAY */
2367 + nla_total_size(16) /* RTA_PREFSRC */
2368 + nla_total_size(4) /* RTA_TABLE */
2369 + nla_total_size(4) /* RTA_IIF */
2370 + nla_total_size(4) /* RTA_OIF */
2371 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002372 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002373 + nla_total_size(sizeof(struct rta_cacheinfo));
2374}
2375
Brian Haley191cd582008-08-14 15:33:21 -07002376static int rt6_fill_node(struct net *net,
2377 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002378 struct in6_addr *dst, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002379 int iif, int type, u32 portid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002380 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381{
2382 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002383 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002384 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002385 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002386 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387
2388 if (prefix) { /* user wants prefix routes only */
2389 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2390 /* success since this is not a prefix route */
2391 return 1;
2392 }
2393 }
2394
Eric W. Biederman15e47302012-09-07 20:12:54 +00002395 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002396 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002397 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002398
2399 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 rtm->rtm_family = AF_INET6;
2401 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2402 rtm->rtm_src_len = rt->rt6i_src.plen;
2403 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002404 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002405 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002406 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002407 table = RT6_TABLE_UNSPEC;
2408 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002409 if (nla_put_u32(skb, RTA_TABLE, table))
2410 goto nla_put_failure;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002411 if (rt->rt6i_flags & RTF_REJECT) {
2412 switch (rt->dst.error) {
2413 case -EINVAL:
2414 rtm->rtm_type = RTN_BLACKHOLE;
2415 break;
2416 case -EACCES:
2417 rtm->rtm_type = RTN_PROHIBIT;
2418 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002419 case -EAGAIN:
2420 rtm->rtm_type = RTN_THROW;
2421 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002422 default:
2423 rtm->rtm_type = RTN_UNREACHABLE;
2424 break;
2425 }
2426 }
David S. Miller38308472011-12-03 18:02:47 -05002427 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002428 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002429 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430 rtm->rtm_type = RTN_LOCAL;
2431 else
2432 rtm->rtm_type = RTN_UNICAST;
2433 rtm->rtm_flags = 0;
2434 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2435 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002436 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437 rtm->rtm_protocol = RTPROT_REDIRECT;
Denis Ovsienkof0396f602012-07-10 04:45:50 +00002438 else if (rt->rt6i_flags & RTF_ADDRCONF) {
2439 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
2440 rtm->rtm_protocol = RTPROT_RA;
2441 else
2442 rtm->rtm_protocol = RTPROT_KERNEL;
2443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444
David S. Miller38308472011-12-03 18:02:47 -05002445 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446 rtm->rtm_flags |= RTM_F_CLONED;
2447
2448 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002449 if (nla_put(skb, RTA_DST, 16, dst))
2450 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002451 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002453 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2454 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455#ifdef CONFIG_IPV6_SUBTREES
2456 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002457 if (nla_put(skb, RTA_SRC, 16, src))
2458 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002459 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002460 } else if (rtm->rtm_src_len &&
2461 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2462 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002464 if (iif) {
2465#ifdef CONFIG_IPV6_MROUTE
2466 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002467 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002468 if (err <= 0) {
2469 if (!nowait) {
2470 if (err == 0)
2471 return 0;
2472 goto nla_put_failure;
2473 } else {
2474 if (err == -EMSGSIZE)
2475 goto nla_put_failure;
2476 }
2477 }
2478 } else
2479#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002480 if (nla_put_u32(skb, RTA_IIF, iif))
2481 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002482 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002484 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2485 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2486 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002488
Daniel Walterc3968a82011-04-13 21:10:57 +00002489 if (rt->rt6i_prefsrc.plen) {
2490 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002491 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002492 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2493 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002494 }
2495
David S. Millerdefb3512010-12-08 21:16:57 -08002496 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002497 goto nla_put_failure;
2498
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002499 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002500 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002501 if (n) {
2502 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2503 rcu_read_unlock();
2504 goto nla_put_failure;
2505 }
2506 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002507 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002508
David S. Millerc78679e2012-04-01 20:27:33 -04002509 if (rt->dst.dev &&
2510 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2511 goto nla_put_failure;
2512 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2513 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00002514
2515 expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002516
David S. Miller87a50692012-07-10 05:06:14 -07002517 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002518 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519
Thomas Graf2d7202b2006-08-22 00:01:27 -07002520 return nlmsg_end(skb, nlh);
2521
2522nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002523 nlmsg_cancel(skb, nlh);
2524 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525}
2526
Patrick McHardy1b43af52006-08-10 23:11:17 -07002527int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528{
2529 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2530 int prefix;
2531
Thomas Graf2d7202b2006-08-22 00:01:27 -07002532 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2533 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2535 } else
2536 prefix = 0;
2537
Brian Haley191cd582008-08-14 15:33:21 -07002538 return rt6_fill_node(arg->net,
2539 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002540 NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002541 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542}
2543
Thomas Grafc127ea22007-03-22 11:58:32 -07002544static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002546 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002547 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002549 struct sk_buff *skb;
2550 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002551 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002552 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002553
2554 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2555 if (err < 0)
2556 goto errout;
2557
2558 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002559 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002560
2561 if (tb[RTA_SRC]) {
2562 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2563 goto errout;
2564
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002565 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002566 }
2567
2568 if (tb[RTA_DST]) {
2569 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2570 goto errout;
2571
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002572 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002573 }
2574
2575 if (tb[RTA_IIF])
2576 iif = nla_get_u32(tb[RTA_IIF]);
2577
2578 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002579 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002580
2581 if (iif) {
2582 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002583 int flags = 0;
2584
Daniel Lezcano55786892008-03-04 13:47:47 -08002585 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002586 if (!dev) {
2587 err = -ENODEV;
2588 goto errout;
2589 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002590
2591 fl6.flowi6_iif = iif;
2592
2593 if (!ipv6_addr_any(&fl6.saddr))
2594 flags |= RT6_LOOKUP_F_HAS_SADDR;
2595
2596 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2597 flags);
2598 } else {
2599 fl6.flowi6_oif = oif;
2600
2601 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002602 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603
2604 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002605 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002606 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002607 err = -ENOBUFS;
2608 goto errout;
2609 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610
2611 /* Reserve room for dummy headers, this skb can pass
2612 through good chunk of routing engine.
2613 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002614 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2616
Changli Gaod8d1f302010-06-10 23:31:35 -07002617 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618
David S. Miller4c9483b2011-03-12 16:22:43 -05002619 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002620 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002621 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002623 kfree_skb(skb);
2624 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 }
2626
Eric W. Biederman15e47302012-09-07 20:12:54 +00002627 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07002628errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630}
2631
Thomas Graf86872cb2006-08-22 00:01:08 -07002632void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633{
2634 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002635 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002636 u32 seq;
2637 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002639 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002640 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002641
Thomas Graf339bf982006-11-10 14:10:15 -08002642 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002643 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002644 goto errout;
2645
Brian Haley191cd582008-08-14 15:33:21 -07002646 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002647 event, info->portid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002648 if (err < 0) {
2649 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2650 WARN_ON(err == -EMSGSIZE);
2651 kfree_skb(skb);
2652 goto errout;
2653 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00002654 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002655 info->nlh, gfp_any());
2656 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002657errout:
2658 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002659 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660}
2661
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002662static int ip6_route_dev_notify(struct notifier_block *this,
2663 unsigned long event, void *data)
2664{
2665 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002666 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002667
2668 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002669 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002670 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2671#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002672 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002673 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002674 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002675 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2676#endif
2677 }
2678
2679 return NOTIFY_OK;
2680}
2681
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682/*
2683 * /proc
2684 */
2685
2686#ifdef CONFIG_PROC_FS
2687
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688struct rt6_proc_arg
2689{
2690 char *buffer;
2691 int offset;
2692 int length;
2693 int skip;
2694 int len;
2695};
2696
2697static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2698{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002699 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002700 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002702 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703
2704#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002705 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002707 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002709 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002710 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002711 if (n) {
2712 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002714 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002716 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002717 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002718 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2719 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002720 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 return 0;
2722}
2723
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002724static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002726 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002727 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002728 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729}
2730
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002731static int ipv6_route_open(struct inode *inode, struct file *file)
2732{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002733 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002734}
2735
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002736static const struct file_operations ipv6_route_proc_fops = {
2737 .owner = THIS_MODULE,
2738 .open = ipv6_route_open,
2739 .read = seq_read,
2740 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002741 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002742};
2743
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2745{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002746 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002748 net->ipv6.rt6_stats->fib_nodes,
2749 net->ipv6.rt6_stats->fib_route_nodes,
2750 net->ipv6.rt6_stats->fib_rt_alloc,
2751 net->ipv6.rt6_stats->fib_rt_entries,
2752 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002753 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002754 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755
2756 return 0;
2757}
2758
2759static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2760{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002761 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002762}
2763
Arjan van de Ven9a321442007-02-12 00:55:35 -08002764static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 .owner = THIS_MODULE,
2766 .open = rt6_stats_seq_open,
2767 .read = seq_read,
2768 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002769 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770};
2771#endif /* CONFIG_PROC_FS */
2772
2773#ifdef CONFIG_SYSCTL
2774
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002776int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 void __user *buffer, size_t *lenp, loff_t *ppos)
2778{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002779 struct net *net;
2780 int delay;
2781 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002783
2784 net = (struct net *)ctl->extra1;
2785 delay = net->ipv6.sysctl.flush_delay;
2786 proc_dointvec(ctl, write, buffer, lenp, ppos);
2787 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2788 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789}
2790
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002791ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002792 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002794 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002796 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002797 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 },
2799 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002801 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 .maxlen = sizeof(int),
2803 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002804 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 },
2806 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002808 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 .maxlen = sizeof(int),
2810 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002811 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 },
2813 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002815 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 .maxlen = sizeof(int),
2817 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002818 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 },
2820 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002822 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 .maxlen = sizeof(int),
2824 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002825 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 },
2827 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002829 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 .maxlen = sizeof(int),
2831 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002832 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 },
2834 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002836 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837 .maxlen = sizeof(int),
2838 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002839 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 },
2841 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002843 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844 .maxlen = sizeof(int),
2845 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002846 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 },
2848 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002850 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851 .maxlen = sizeof(int),
2852 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002853 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 },
2855 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002857 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 .maxlen = sizeof(int),
2859 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002860 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002862 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863};
2864
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002865struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002866{
2867 struct ctl_table *table;
2868
2869 table = kmemdup(ipv6_route_table_template,
2870 sizeof(ipv6_route_table_template),
2871 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002872
2873 if (table) {
2874 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002875 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002876 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002877 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2878 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2879 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2880 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2881 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2882 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2883 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002884 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002885 }
2886
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002887 return table;
2888}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889#endif
2890
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002891static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002892{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002893 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002894
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002895 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2896 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002897
Eric Dumazetfc66f952010-10-08 06:37:34 +00002898 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2899 goto out_ip6_dst_ops;
2900
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002901 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2902 sizeof(*net->ipv6.ip6_null_entry),
2903 GFP_KERNEL);
2904 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002905 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002906 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002907 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002908 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002909 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2910 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002911
2912#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2913 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2914 sizeof(*net->ipv6.ip6_prohibit_entry),
2915 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002916 if (!net->ipv6.ip6_prohibit_entry)
2917 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002918 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002919 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002920 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002921 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2922 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002923
2924 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2925 sizeof(*net->ipv6.ip6_blk_hole_entry),
2926 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002927 if (!net->ipv6.ip6_blk_hole_entry)
2928 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002929 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002930 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002931 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002932 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2933 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002934#endif
2935
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002936 net->ipv6.sysctl.flush_delay = 0;
2937 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2938 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2939 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2940 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2941 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2942 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2943 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2944
Benjamin Thery6891a342008-03-04 13:49:47 -08002945 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2946
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002947 ret = 0;
2948out:
2949 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002950
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002951#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2952out_ip6_prohibit_entry:
2953 kfree(net->ipv6.ip6_prohibit_entry);
2954out_ip6_null_entry:
2955 kfree(net->ipv6.ip6_null_entry);
2956#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002957out_ip6_dst_entries:
2958 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002959out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002960 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002961}
2962
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002963static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002964{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002965 kfree(net->ipv6.ip6_null_entry);
2966#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2967 kfree(net->ipv6.ip6_prohibit_entry);
2968 kfree(net->ipv6.ip6_blk_hole_entry);
2969#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002970 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002971}
2972
Thomas Grafd1896342012-06-18 12:08:33 +00002973static int __net_init ip6_route_net_init_late(struct net *net)
2974{
2975#ifdef CONFIG_PROC_FS
2976 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2977 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2978#endif
2979 return 0;
2980}
2981
2982static void __net_exit ip6_route_net_exit_late(struct net *net)
2983{
2984#ifdef CONFIG_PROC_FS
2985 proc_net_remove(net, "ipv6_route");
2986 proc_net_remove(net, "rt6_stats");
2987#endif
2988}
2989
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002990static struct pernet_operations ip6_route_net_ops = {
2991 .init = ip6_route_net_init,
2992 .exit = ip6_route_net_exit,
2993};
2994
David S. Millerc3426b42012-06-09 16:27:05 -07002995static int __net_init ipv6_inetpeer_init(struct net *net)
2996{
2997 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2998
2999 if (!bp)
3000 return -ENOMEM;
3001 inet_peer_base_init(bp);
3002 net->ipv6.peers = bp;
3003 return 0;
3004}
3005
3006static void __net_exit ipv6_inetpeer_exit(struct net *net)
3007{
3008 struct inet_peer_base *bp = net->ipv6.peers;
3009
3010 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07003011 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07003012 kfree(bp);
3013}
3014
David S. Miller2b823f72012-06-09 19:00:16 -07003015static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003016 .init = ipv6_inetpeer_init,
3017 .exit = ipv6_inetpeer_exit,
3018};
3019
Thomas Grafd1896342012-06-18 12:08:33 +00003020static struct pernet_operations ip6_route_net_late_ops = {
3021 .init = ip6_route_net_init_late,
3022 .exit = ip6_route_net_exit_late,
3023};
3024
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003025static struct notifier_block ip6_route_dev_notifier = {
3026 .notifier_call = ip6_route_dev_notify,
3027 .priority = 0,
3028};
3029
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003030int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003031{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003032 int ret;
3033
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003034 ret = -ENOMEM;
3035 ip6_dst_ops_template.kmem_cachep =
3036 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3037 SLAB_HWCACHE_ALIGN, NULL);
3038 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003039 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003040
Eric Dumazetfc66f952010-10-08 06:37:34 +00003041 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003042 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003043 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003044
David S. Millerc3426b42012-06-09 16:27:05 -07003045 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3046 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003047 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003048
David S. Miller7e52b332012-06-15 15:51:55 -07003049 ret = register_pernet_subsys(&ip6_route_net_ops);
3050 if (ret)
3051 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003052
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003053 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3054
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003055 /* Registering of the loopback is done before this portion of code,
3056 * the loopback reference in rt6_info will not be taken, do it
3057 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003058 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003059 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3060 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003061 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003062 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003063 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003064 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3065 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003066 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003067 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003068 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003069
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003070 ret = xfrm6_init();
3071 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003072 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003073
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003074 ret = fib6_rules_init();
3075 if (ret)
3076 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003077
Thomas Grafd1896342012-06-18 12:08:33 +00003078 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3079 if (ret)
3080 goto fib6_rules_init;
3081
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003082 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003083 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3084 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3085 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003086 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003087
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003088 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003089 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003090 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003091
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003092out:
3093 return ret;
3094
Thomas Grafd1896342012-06-18 12:08:33 +00003095out_register_late_subsys:
3096 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003097fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003098 fib6_rules_cleanup();
3099xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003100 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003101out_fib6_init:
3102 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003103out_register_subsys:
3104 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003105out_register_inetpeer:
3106 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003107out_dst_entries:
3108 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003109out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003110 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003111 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003112}
3113
3114void ip6_route_cleanup(void)
3115{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003116 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003117 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003118 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003121 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003122 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003123 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003124 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003125}