blob: 399613b7972fdd893056ef31d5cacb635ab83f20 [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 */
David S. Miller97cac082012-07-02 22:43:47 -0700454 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800455 if (!neigh || (neigh->nud_state & NUD_VALID))
Amerigo Wangfdd66812012-09-10 02:48:44 +0000456 return;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800457 read_lock_bh(&neigh->lock);
458 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800459 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800460 struct in6_addr mcaddr;
461 struct in6_addr *target;
462
463 neigh->updated = jiffies;
464 read_unlock_bh(&neigh->lock);
465
466 target = (struct in6_addr *)&neigh->primary_key;
467 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500468 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000469 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800470 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000471 }
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800472}
473#else
474static inline void rt6_probe(struct rt6_info *rt)
475{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800476}
477#endif
478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800480 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700482static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483{
David S. Millerd1918542011-12-28 20:19:20 -0500484 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700485 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800486 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700487 if ((dev->flags & IFF_LOOPBACK) &&
488 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
489 return 1;
490 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491}
492
Dave Jonesb6f99a22007-03-22 12:27:49 -0700493static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000495 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800496 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000497
David S. Miller97cac082012-07-02 22:43:47 -0700498 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700499 if (rt->rt6i_flags & RTF_NONEXTHOP ||
500 !(rt->rt6i_flags & RTF_GATEWAY))
501 m = 1;
502 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800503 read_lock_bh(&neigh->lock);
504 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700505 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800506#ifdef CONFIG_IPV6_ROUTER_PREF
507 else if (neigh->nud_state & NUD_FAILED)
508 m = 0;
509#endif
510 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800511 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800512 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800513 } else
514 m = 0;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800515 return m;
516}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800518static int rt6_score_route(struct rt6_info *rt, int oif,
519 int strict)
520{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700521 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900522
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700523 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700524 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800525 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800526#ifdef CONFIG_IPV6_ROUTER_PREF
527 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
528#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700529 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800530 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800531 return -1;
532 return m;
533}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
David S. Millerf11e6652007-03-24 20:36:25 -0700535static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
536 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800537{
David S. Millerf11e6652007-03-24 20:36:25 -0700538 int m;
539
540 if (rt6_check_expired(rt))
541 goto out;
542
543 m = rt6_score_route(rt, oif, strict);
544 if (m < 0)
545 goto out;
546
547 if (m > *mpri) {
548 if (strict & RT6_LOOKUP_F_REACHABLE)
549 rt6_probe(match);
550 *mpri = m;
551 match = rt;
552 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
553 rt6_probe(rt);
554 }
555
556out:
557 return match;
558}
559
560static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
561 struct rt6_info *rr_head,
562 u32 metric, int oif, int strict)
563{
564 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800565 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
David S. Millerf11e6652007-03-24 20:36:25 -0700567 match = NULL;
568 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700569 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700570 match = find_match(rt, oif, strict, &mpri, match);
571 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700572 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700573 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800574
David S. Millerf11e6652007-03-24 20:36:25 -0700575 return match;
576}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800577
David S. Millerf11e6652007-03-24 20:36:25 -0700578static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
579{
580 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800581 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
David S. Millerf11e6652007-03-24 20:36:25 -0700583 rt0 = fn->rr_ptr;
584 if (!rt0)
585 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
David S. Millerf11e6652007-03-24 20:36:25 -0700587 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800589 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700590 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700591 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700592
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800593 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700594 if (!next || next->rt6i_metric != rt0->rt6i_metric)
595 next = fn->leaf;
596
597 if (next != rt0)
598 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600
David S. Millerd1918542011-12-28 20:19:20 -0500601 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000602 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603}
604
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800605#ifdef CONFIG_IPV6_ROUTE_INFO
606int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000607 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800608{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900609 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800610 struct route_info *rinfo = (struct route_info *) opt;
611 struct in6_addr prefix_buf, *prefix;
612 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900613 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800614 struct rt6_info *rt;
615
616 if (len < sizeof(struct route_info)) {
617 return -EINVAL;
618 }
619
620 /* Sanity check for prefix_len and length */
621 if (rinfo->length > 3) {
622 return -EINVAL;
623 } else if (rinfo->prefix_len > 128) {
624 return -EINVAL;
625 } else if (rinfo->prefix_len > 64) {
626 if (rinfo->length < 2) {
627 return -EINVAL;
628 }
629 } else if (rinfo->prefix_len > 0) {
630 if (rinfo->length < 1) {
631 return -EINVAL;
632 }
633 }
634
635 pref = rinfo->route_pref;
636 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000637 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800638
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900639 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800640
641 if (rinfo->length == 3)
642 prefix = (struct in6_addr *)rinfo->prefix;
643 else {
644 /* this function is safe */
645 ipv6_addr_prefix(&prefix_buf,
646 (struct in6_addr *)rinfo->prefix,
647 rinfo->prefix_len);
648 prefix = &prefix_buf;
649 }
650
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800651 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
652 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800653
654 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700655 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800656 rt = NULL;
657 }
658
659 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800660 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800661 pref);
662 else if (rt)
663 rt->rt6i_flags = RTF_ROUTEINFO |
664 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
665
666 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000667 if (!addrconf_finite_timeout(lifetime))
668 rt6_clean_expires(rt);
669 else
670 rt6_set_expires(rt, jiffies + HZ * lifetime);
671
Changli Gaod8d1f302010-06-10 23:31:35 -0700672 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800673 }
674 return 0;
675}
676#endif
677
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800678#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700679do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800680 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700681 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700682 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700683 if (fn->fn_flags & RTN_TL_ROOT) \
684 goto out; \
685 pn = fn->parent; \
686 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800687 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700688 else \
689 fn = pn; \
690 if (fn->fn_flags & RTN_RTINFO) \
691 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700692 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700693 } \
David S. Miller38308472011-12-03 18:02:47 -0500694} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700695
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800696static struct rt6_info *ip6_pol_route_lookup(struct net *net,
697 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500698 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699{
700 struct fib6_node *fn;
701 struct rt6_info *rt;
702
Thomas Grafc71099a2006-08-04 23:20:06 -0700703 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500704 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700705restart:
706 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500707 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
708 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700709out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700710 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700711 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700712 return rt;
713
714}
715
Florian Westphalea6e5742011-09-05 16:05:44 +0200716struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
717 int flags)
718{
719 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
720}
721EXPORT_SYMBOL_GPL(ip6_route_lookup);
722
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900723struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
724 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700725{
David S. Miller4c9483b2011-03-12 16:22:43 -0500726 struct flowi6 fl6 = {
727 .flowi6_oif = oif,
728 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700729 };
730 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700731 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700732
Thomas Grafadaa70b2006-10-13 15:01:03 -0700733 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500734 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700735 flags |= RT6_LOOKUP_F_HAS_SADDR;
736 }
737
David S. Miller4c9483b2011-03-12 16:22:43 -0500738 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700739 if (dst->error == 0)
740 return (struct rt6_info *) dst;
741
742 dst_release(dst);
743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 return NULL;
745}
746
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900747EXPORT_SYMBOL(rt6_lookup);
748
Thomas Grafc71099a2006-08-04 23:20:06 -0700749/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 It takes new route entry, the addition fails by any reason the
751 route is freed. In any case, if caller does not hold it, it may
752 be destroyed.
753 */
754
Thomas Graf86872cb2006-08-22 00:01:08 -0700755static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756{
757 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700758 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Thomas Grafc71099a2006-08-04 23:20:06 -0700760 table = rt->rt6i_table;
761 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700762 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700763 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
765 return err;
766}
767
Thomas Graf40e22e82006-08-22 00:00:45 -0700768int ip6_ins_rt(struct rt6_info *rt)
769{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800770 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500771 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800772 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800773 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700774}
775
Gao feng1716a962012-04-06 00:13:10 +0000776static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000777 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000778 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 struct rt6_info *rt;
781
782 /*
783 * Clone the route.
784 */
785
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000786 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
788 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800789 int attempts = !in_softirq();
790
David S. Miller38308472011-12-03 18:02:47 -0500791 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500792 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000793 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900794 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000795 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900796 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800#ifdef CONFIG_IPV6_SUBTREES
801 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000802 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 rt->rt6i_src.plen = 128;
804 }
805#endif
806
David S. Miller14deae42009-01-04 16:04:39 -0800807 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500808 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500809 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800810 int saved_rt_min_interval =
811 net->ipv6.sysctl.ip6_rt_gc_min_interval;
812 int saved_rt_elasticity =
813 net->ipv6.sysctl.ip6_rt_gc_elasticity;
814
815 if (attempts-- > 0) {
816 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
817 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
818
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000819 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800820
821 net->ipv6.sysctl.ip6_rt_gc_elasticity =
822 saved_rt_elasticity;
823 net->ipv6.sysctl.ip6_rt_gc_min_interval =
824 saved_rt_min_interval;
825 goto retry;
826 }
827
Joe Perchesf3213832012-05-15 14:11:53 +0000828 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700829 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800830 return NULL;
831 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800832 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800834 return rt;
835}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000837static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
838 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800839{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000840 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
841
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800842 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800843 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700844 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800845 }
846 return rt;
847}
848
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800849static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500850 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851{
852 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800853 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700854 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800856 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700857 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700859 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
861relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700862 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800864restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500865 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700868 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800869
David S. Miller4c9483b2011-03-12 16:22:43 -0500870 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800871 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800872 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800873 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Changli Gaod8d1f302010-06-10 23:31:35 -0700875 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700876 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800877
David S. Miller97cac082012-07-02 22:43:47 -0700878 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500879 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800880 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500881 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800882 else
883 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800884
Changli Gaod8d1f302010-06-10 23:31:35 -0700885 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800886 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800887
Changli Gaod8d1f302010-06-10 23:31:35 -0700888 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800889 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700890 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800891 if (!err)
892 goto out2;
893 }
894
895 if (--attempts <= 0)
896 goto out2;
897
898 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700899 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800900 * released someone could insert this route. Relookup.
901 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700902 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800903 goto relookup;
904
905out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800906 if (reachable) {
907 reachable = 0;
908 goto restart_2;
909 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700910 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700911 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700913 rt->dst.lastuse = jiffies;
914 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700915
916 return rt;
917}
918
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800919static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500920 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700921{
David S. Miller4c9483b2011-03-12 16:22:43 -0500922 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700923}
924
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000925static struct dst_entry *ip6_route_input_lookup(struct net *net,
926 struct net_device *dev,
927 struct flowi6 *fl6, int flags)
928{
929 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
930 flags |= RT6_LOOKUP_F_IFACE;
931
932 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
933}
934
Thomas Grafc71099a2006-08-04 23:20:06 -0700935void ip6_route_input(struct sk_buff *skb)
936{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000937 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900938 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700939 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500940 struct flowi6 fl6 = {
941 .flowi6_iif = skb->dev->ifindex,
942 .daddr = iph->daddr,
943 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500944 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500945 .flowi6_mark = skb->mark,
946 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700947 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700948
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000949 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700950}
951
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800952static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500953 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700954{
David S. Miller4c9483b2011-03-12 16:22:43 -0500955 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700956}
957
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700958struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500959 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700960{
961 int flags = 0;
962
Pavel Emelyanov1fb94892012-08-08 21:53:36 +0000963 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +0000964
David S. Miller4c9483b2011-03-12 16:22:43 -0500965 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700966 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700967
David S. Miller4c9483b2011-03-12 16:22:43 -0500968 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700969 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000970 else if (sk)
971 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700972
David S. Miller4c9483b2011-03-12 16:22:43 -0500973 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974}
975
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900976EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
David S. Miller2774c132011-03-01 14:59:04 -0800978struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700979{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700980 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700981 struct dst_entry *new = NULL;
982
David S. Millerf5b0a872012-07-19 12:31:33 -0700983 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700984 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700985 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700986
Steffen Klassert81048912012-07-05 23:37:09 +0000987 memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
988 rt6_init_peer(rt, net->ipv6.peers);
989
David S. Miller14e50e52007-05-24 18:17:54 -0700990 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800991 new->input = dst_discard;
992 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700993
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000994 if (dst_metrics_read_only(&ort->dst))
995 new->_metrics = ort->dst._metrics;
996 else
997 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700998 rt->rt6i_idev = ort->rt6i_idev;
999 if (rt->rt6i_idev)
1000 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -07001001
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001002 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001003 rt->rt6i_flags = ort->rt6i_flags;
1004 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001005 rt->rt6i_metric = 0;
1006
1007 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1008#ifdef CONFIG_IPV6_SUBTREES
1009 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1010#endif
1011
1012 dst_free(new);
1013 }
1014
David S. Miller69ead7a2011-03-01 14:45:33 -08001015 dst_release(dst_orig);
1016 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001017}
David S. Miller14e50e52007-05-24 18:17:54 -07001018
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019/*
1020 * Destination cache support functions
1021 */
1022
1023static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1024{
1025 struct rt6_info *rt;
1026
1027 rt = (struct rt6_info *) dst;
1028
David S. Miller6431cbc2011-02-07 20:38:06 -08001029 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1030 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001031 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001032 rt6_bind_peer(rt, 0);
1033 rt->rt6i_peer_genid = rt6_peer_genid();
1034 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001036 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 return NULL;
1038}
1039
1040static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1041{
1042 struct rt6_info *rt = (struct rt6_info *) dst;
1043
1044 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001045 if (rt->rt6i_flags & RTF_CACHE) {
1046 if (rt6_check_expired(rt)) {
1047 ip6_del_rt(rt);
1048 dst = NULL;
1049 }
1050 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001052 dst = NULL;
1053 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001055 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056}
1057
1058static void ip6_link_failure(struct sk_buff *skb)
1059{
1060 struct rt6_info *rt;
1061
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001062 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
Eric Dumazetadf30902009-06-02 05:19:30 +00001064 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001066 if (rt->rt6i_flags & RTF_CACHE)
1067 rt6_update_expires(rt, 0);
1068 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 rt->rt6i_node->fn_sernum = -1;
1070 }
1071}
1072
David S. Miller6700c272012-07-17 03:29:28 -07001073static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
1074 struct sk_buff *skb, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075{
1076 struct rt6_info *rt6 = (struct rt6_info*)dst;
1077
David S. Miller81aded22012-06-15 14:54:11 -07001078 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001080 struct net *net = dev_net(dst->dev);
1081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 rt6->rt6i_flags |= RTF_MODIFIED;
1083 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001084 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001086 features |= RTAX_FEATURE_ALLFRAG;
1087 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 }
David S. Millerdefb3512010-12-08 21:16:57 -08001089 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001090 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 }
1092}
1093
David S. Miller42ae66c2012-06-15 20:01:57 -07001094void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1095 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001096{
1097 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1098 struct dst_entry *dst;
1099 struct flowi6 fl6;
1100
1101 memset(&fl6, 0, sizeof(fl6));
1102 fl6.flowi6_oif = oif;
1103 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001104 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001105 fl6.daddr = iph->daddr;
1106 fl6.saddr = iph->saddr;
1107 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1108
1109 dst = ip6_route_output(net, NULL, &fl6);
1110 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001111 ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07001112 dst_release(dst);
1113}
1114EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1115
1116void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1117{
1118 ip6_update_pmtu(skb, sock_net(sk), mtu,
1119 sk->sk_bound_dev_if, sk->sk_mark);
1120}
1121EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1122
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001123void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1124{
1125 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1126 struct dst_entry *dst;
1127 struct flowi6 fl6;
1128
1129 memset(&fl6, 0, sizeof(fl6));
1130 fl6.flowi6_oif = oif;
1131 fl6.flowi6_mark = mark;
1132 fl6.flowi6_flags = 0;
1133 fl6.daddr = iph->daddr;
1134 fl6.saddr = iph->saddr;
1135 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1136
1137 dst = ip6_route_output(net, NULL, &fl6);
1138 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001139 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001140 dst_release(dst);
1141}
1142EXPORT_SYMBOL_GPL(ip6_redirect);
1143
1144void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1145{
1146 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1147}
1148EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1149
David S. Miller0dbaee32010-12-13 12:52:14 -08001150static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151{
David S. Miller0dbaee32010-12-13 12:52:14 -08001152 struct net_device *dev = dst->dev;
1153 unsigned int mtu = dst_mtu(dst);
1154 struct net *net = dev_net(dev);
1155
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1157
Daniel Lezcano55786892008-03-04 13:47:47 -08001158 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1159 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
1161 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001162 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1163 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1164 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 * rely only on pmtu discovery"
1166 */
1167 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1168 mtu = IPV6_MAXPLEN;
1169 return mtu;
1170}
1171
Steffen Klassertebb762f2011-11-23 02:12:51 +00001172static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001173{
David S. Millerd33e4552010-12-14 13:01:14 -08001174 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001175 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1176
1177 if (mtu)
1178 return mtu;
1179
1180 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001181
1182 rcu_read_lock();
1183 idev = __in6_dev_get(dst->dev);
1184 if (idev)
1185 mtu = idev->cnf.mtu6;
1186 rcu_read_unlock();
1187
1188 return mtu;
1189}
1190
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001191static struct dst_entry *icmp6_dst_gc_list;
1192static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001193
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001194struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001196 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197{
David S. Miller87a11572011-12-06 17:04:13 -05001198 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 struct rt6_info *rt;
1200 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001201 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
David S. Miller38308472011-12-03 18:02:47 -05001203 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001204 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
David S. Miller8b96d222012-06-11 02:01:56 -07001206 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001207 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001209 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 goto out;
1211 }
1212
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 if (neigh)
1214 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001215 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001216 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001217 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001218 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001219 dst_free(&rt->dst);
1220 return ERR_CAST(neigh);
1221 }
David S. Miller14deae42009-01-04 16:04:39 -08001222 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001224 rt->dst.flags |= DST_HOST;
1225 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001226 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001227 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001228 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001229 rt->rt6i_dst.plen = 128;
1230 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001231 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001233 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001234 rt->dst.next = icmp6_dst_gc_list;
1235 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001236 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
Daniel Lezcano55786892008-03-04 13:47:47 -08001238 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
David S. Miller87a11572011-12-06 17:04:13 -05001240 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1241
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242out:
David S. Miller87a11572011-12-06 17:04:13 -05001243 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244}
1245
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001246int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001248 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001249 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001251 spin_lock_bh(&icmp6_dst_lock);
1252 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001253
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 while ((dst = *pprev) != NULL) {
1255 if (!atomic_read(&dst->__refcnt)) {
1256 *pprev = dst->next;
1257 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 } else {
1259 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001260 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 }
1262 }
1263
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001264 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001265
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001266 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267}
1268
David S. Miller1e493d12008-09-10 17:27:15 -07001269static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1270 void *arg)
1271{
1272 struct dst_entry *dst, **pprev;
1273
1274 spin_lock_bh(&icmp6_dst_lock);
1275 pprev = &icmp6_dst_gc_list;
1276 while ((dst = *pprev) != NULL) {
1277 struct rt6_info *rt = (struct rt6_info *) dst;
1278 if (func(rt, arg)) {
1279 *pprev = dst->next;
1280 dst_free(dst);
1281 } else {
1282 pprev = &dst->next;
1283 }
1284 }
1285 spin_unlock_bh(&icmp6_dst_lock);
1286}
1287
Daniel Lezcano569d3642008-01-18 03:56:57 -08001288static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001291 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001292 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1293 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1294 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1295 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1296 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001297 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
Eric Dumazetfc66f952010-10-08 06:37:34 +00001299 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001300 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001301 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 goto out;
1303
Benjamin Thery6891a342008-03-04 13:49:47 -08001304 net->ipv6.ip6_rt_gc_expire++;
1305 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1306 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001307 entries = dst_entries_get_slow(ops);
1308 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001309 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001311 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001312 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313}
1314
1315/* Clean host part of a prefix. Not necessary in radix tree,
1316 but results in cleaner routing tables.
1317
1318 Remove it only when all the things will work!
1319 */
1320
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001321int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322{
David S. Miller5170ae82010-12-12 21:35:57 -08001323 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001324 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001325 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001326 struct inet6_dev *idev;
1327
1328 rcu_read_lock();
1329 idev = __in6_dev_get(dev);
1330 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001331 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001332 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001333 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001334 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 }
1336 return hoplimit;
1337}
David S. Millerabbf46a2010-12-12 21:14:46 -08001338EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340/*
1341 *
1342 */
1343
Thomas Graf86872cb2006-08-22 00:01:08 -07001344int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345{
1346 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001347 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 struct rt6_info *rt = NULL;
1349 struct net_device *dev = NULL;
1350 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001351 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 int addr_type;
1353
Thomas Graf86872cb2006-08-22 00:01:08 -07001354 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 return -EINVAL;
1356#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001357 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 return -EINVAL;
1359#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001360 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001362 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 if (!dev)
1364 goto out;
1365 idev = in6_dev_get(dev);
1366 if (!idev)
1367 goto out;
1368 }
1369
Thomas Graf86872cb2006-08-22 00:01:08 -07001370 if (cfg->fc_metric == 0)
1371 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Matti Vaittinend71314b2011-11-14 00:14:49 +00001373 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001374 if (cfg->fc_nlinfo.nlh &&
1375 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001376 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001377 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001378 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001379 table = fib6_new_table(net, cfg->fc_table);
1380 }
1381 } else {
1382 table = fib6_new_table(net, cfg->fc_table);
1383 }
David S. Miller38308472011-12-03 18:02:47 -05001384
1385 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001386 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001387
David S. Miller8b96d222012-06-11 02:01:56 -07001388 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
David S. Miller38308472011-12-03 18:02:47 -05001390 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 err = -ENOMEM;
1392 goto out;
1393 }
1394
Changli Gaod8d1f302010-06-10 23:31:35 -07001395 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001396
1397 if (cfg->fc_flags & RTF_EXPIRES)
1398 rt6_set_expires(rt, jiffies +
1399 clock_t_to_jiffies(cfg->fc_expires));
1400 else
1401 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
Thomas Graf86872cb2006-08-22 00:01:08 -07001403 if (cfg->fc_protocol == RTPROT_UNSPEC)
1404 cfg->fc_protocol = RTPROT_BOOT;
1405 rt->rt6i_protocol = cfg->fc_protocol;
1406
1407 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
1409 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001410 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001411 else if (cfg->fc_flags & RTF_LOCAL)
1412 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001414 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415
Changli Gaod8d1f302010-06-10 23:31:35 -07001416 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
Thomas Graf86872cb2006-08-22 00:01:08 -07001418 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1419 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001421 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001423 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1424 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1425 if (!metrics) {
1426 err = -ENOMEM;
1427 goto out;
1428 }
1429 dst_init_metrics(&rt->dst, metrics, 0);
1430 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001432 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1433 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434#endif
1435
Thomas Graf86872cb2006-08-22 00:01:08 -07001436 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437
1438 /* We cannot add true routes via loopback here,
1439 they would result in kernel looping; promote them to reject routes
1440 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001441 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001442 (dev && (dev->flags & IFF_LOOPBACK) &&
1443 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1444 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001446 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 if (dev) {
1448 dev_put(dev);
1449 in6_dev_put(idev);
1450 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001451 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 dev_hold(dev);
1453 idev = in6_dev_get(dev);
1454 if (!idev) {
1455 err = -ENODEV;
1456 goto out;
1457 }
1458 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001459 rt->dst.output = ip6_pkt_discard_out;
1460 rt->dst.input = ip6_pkt_discard;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001462 switch (cfg->fc_type) {
1463 case RTN_BLACKHOLE:
1464 rt->dst.error = -EINVAL;
1465 break;
1466 case RTN_PROHIBIT:
1467 rt->dst.error = -EACCES;
1468 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00001469 case RTN_THROW:
1470 rt->dst.error = -EAGAIN;
1471 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001472 default:
1473 rt->dst.error = -ENETUNREACH;
1474 break;
1475 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 goto install_route;
1477 }
1478
Thomas Graf86872cb2006-08-22 00:01:08 -07001479 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001480 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 int gwa_type;
1482
Thomas Graf86872cb2006-08-22 00:01:08 -07001483 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001484 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 gwa_type = ipv6_addr_type(gw_addr);
1486
1487 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1488 struct rt6_info *grt;
1489
1490 /* IPv6 strictly inhibits using not link-local
1491 addresses as nexthop address.
1492 Otherwise, router will not able to send redirects.
1493 It is very good, but in some (rare!) circumstances
1494 (SIT, PtP, NBMA NOARP links) it is handy to allow
1495 some exceptions. --ANK
1496 */
1497 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001498 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 goto out;
1500
Daniel Lezcano55786892008-03-04 13:47:47 -08001501 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001504 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 goto out;
1506 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001507 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001508 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 goto out;
1510 }
1511 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001512 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 idev = grt->rt6i_idev;
1514 dev_hold(dev);
1515 in6_dev_hold(grt->rt6i_idev);
1516 }
David S. Miller38308472011-12-03 18:02:47 -05001517 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001519 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
1521 if (err)
1522 goto out;
1523 }
1524 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001525 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 goto out;
1527 }
1528
1529 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001530 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 goto out;
1532
Daniel Walterc3968a82011-04-13 21:10:57 +00001533 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1534 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1535 err = -EINVAL;
1536 goto out;
1537 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001538 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001539 rt->rt6i_prefsrc.plen = 128;
1540 } else
1541 rt->rt6i_prefsrc.plen = 0;
1542
Thomas Graf86872cb2006-08-22 00:01:08 -07001543 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001544 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001545 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 }
1548
Thomas Graf86872cb2006-08-22 00:01:08 -07001549 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
1551install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001552 if (cfg->fc_mx) {
1553 struct nlattr *nla;
1554 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555
Thomas Graf86872cb2006-08-22 00:01:08 -07001556 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001557 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001558
1559 if (type) {
1560 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 err = -EINVAL;
1562 goto out;
1563 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001564
David S. Millerdefb3512010-12-08 21:16:57 -08001565 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 }
1568 }
1569
Changli Gaod8d1f302010-06-10 23:31:35 -07001570 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001572 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001573
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001574 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001575
Thomas Graf86872cb2006-08-22 00:01:08 -07001576 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
1578out:
1579 if (dev)
1580 dev_put(dev);
1581 if (idev)
1582 in6_dev_put(idev);
1583 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001584 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 return err;
1586}
1587
Thomas Graf86872cb2006-08-22 00:01:08 -07001588static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589{
1590 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001591 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001592 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001594 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001595 return -ENOENT;
1596
Thomas Grafc71099a2006-08-04 23:20:06 -07001597 table = rt->rt6i_table;
1598 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Thomas Graf86872cb2006-08-22 00:01:08 -07001600 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001601 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Thomas Grafc71099a2006-08-04 23:20:06 -07001603 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
1605 return err;
1606}
1607
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001608int ip6_del_rt(struct rt6_info *rt)
1609{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001610 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001611 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001612 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001613 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001614}
1615
Thomas Graf86872cb2006-08-22 00:01:08 -07001616static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617{
Thomas Grafc71099a2006-08-04 23:20:06 -07001618 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 struct fib6_node *fn;
1620 struct rt6_info *rt;
1621 int err = -ESRCH;
1622
Daniel Lezcano55786892008-03-04 13:47:47 -08001623 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001624 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001625 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
Thomas Grafc71099a2006-08-04 23:20:06 -07001627 read_lock_bh(&table->tb6_lock);
1628
1629 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001630 &cfg->fc_dst, cfg->fc_dst_len,
1631 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001632
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001634 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001635 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001636 (!rt->dst.dev ||
1637 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001639 if (cfg->fc_flags & RTF_GATEWAY &&
1640 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001642 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001644 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001645 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646
Thomas Graf86872cb2006-08-22 00:01:08 -07001647 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 }
1649 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001650 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
1652 return err;
1653}
1654
David S. Miller6700c272012-07-17 03:29:28 -07001655static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001656{
David S. Millere8599ff2012-07-11 23:43:53 -07001657 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001658 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001659 struct rt6_info *rt, *nrt = NULL;
1660 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001661 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001662 const struct in6_addr *dest;
1663 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001664 struct inet6_dev *in6_dev;
1665 struct neighbour *neigh;
1666 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001667 int optlen, on_link;
1668 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001669
1670 optlen = skb->tail - skb->transport_header;
1671 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1672
1673 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001674 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001675 return;
1676 }
1677
1678 icmph = icmp6_hdr(skb);
1679 target = (const struct in6_addr *) (icmph + 1);
1680 dest = target + 1;
1681
1682 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001683 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001684 return;
1685 }
1686
David S. Miller6e157b62012-07-12 00:05:02 -07001687 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001688 if (ipv6_addr_equal(dest, target)) {
1689 on_link = 1;
1690 } else if (ipv6_addr_type(target) !=
1691 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001692 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001693 return;
1694 }
1695
1696 in6_dev = __in6_dev_get(skb->dev);
1697 if (!in6_dev)
1698 return;
1699 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1700 return;
1701
1702 /* RFC2461 8.1:
1703 * The IP source address of the Redirect MUST be the same as the current
1704 * first-hop router for the specified ICMP Destination Address.
1705 */
1706
1707 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1708 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1709 return;
1710 }
David S. Miller6e157b62012-07-12 00:05:02 -07001711
1712 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001713 if (ndopts.nd_opts_tgt_lladdr) {
1714 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1715 skb->dev);
1716 if (!lladdr) {
1717 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1718 return;
1719 }
1720 }
1721
David S. Miller6e157b62012-07-12 00:05:02 -07001722 rt = (struct rt6_info *) dst;
1723 if (rt == net->ipv6.ip6_null_entry) {
1724 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1725 return;
1726 }
1727
1728 /* Redirect received -> path was valid.
1729 * Look, redirects are sent only in response to data packets,
1730 * so that this nexthop apparently is reachable. --ANK
1731 */
1732 dst_confirm(&rt->dst);
1733
David S. Millere8599ff2012-07-11 23:43:53 -07001734 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1735 if (!neigh)
1736 return;
1737
David S. Miller6e157b62012-07-12 00:05:02 -07001738 /* Duplicate redirect: silently ignore. */
1739 old_neigh = rt->n;
1740 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001741 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 /*
1744 * We have finally decided to accept it.
1745 */
1746
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001747 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1749 NEIGH_UPDATE_F_OVERRIDE|
1750 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1751 NEIGH_UPDATE_F_ISROUTER))
1752 );
1753
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001754 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001755 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 goto out;
1757
1758 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1759 if (on_link)
1760 nrt->rt6i_flags &= ~RTF_GATEWAY;
1761
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001762 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001763 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764
Thomas Graf40e22e82006-08-22 00:00:45 -07001765 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 goto out;
1767
Changli Gaod8d1f302010-06-10 23:31:35 -07001768 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001769 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001770 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001771 netevent.new_neigh = neigh;
1772 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001773 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1774
David S. Miller38308472011-12-03 18:02:47 -05001775 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001776 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001777 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 }
1779
1780out:
David S. Millere8599ff2012-07-11 23:43:53 -07001781 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001782}
1783
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785 * Misc support functions
1786 */
1787
Gao feng1716a962012-04-06 00:13:10 +00001788static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001789 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790{
David S. Millerd1918542011-12-28 20:19:20 -05001791 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001792 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1793 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794
1795 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001796 rt->dst.input = ort->dst.input;
1797 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001798 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001800 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001801 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001802 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001803 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 rt->rt6i_idev = ort->rt6i_idev;
1805 if (rt->rt6i_idev)
1806 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001807 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001809 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001810 rt->rt6i_flags = ort->rt6i_flags;
1811 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1812 (RTF_DEFAULT | RTF_ADDRCONF))
1813 rt6_set_from(rt, ort);
1814 else
1815 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 rt->rt6i_metric = 0;
1817
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818#ifdef CONFIG_IPV6_SUBTREES
1819 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1820#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001821 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001822 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 }
1824 return rt;
1825}
1826
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001827#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001828static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001829 const struct in6_addr *prefix, int prefixlen,
1830 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001831{
1832 struct fib6_node *fn;
1833 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001834 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001835
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001836 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001837 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001838 return NULL;
1839
1840 write_lock_bh(&table->tb6_lock);
1841 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001842 if (!fn)
1843 goto out;
1844
Changli Gaod8d1f302010-06-10 23:31:35 -07001845 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001846 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001847 continue;
1848 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1849 continue;
1850 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1851 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001852 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001853 break;
1854 }
1855out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001856 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001857 return rt;
1858}
1859
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001860static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001861 const struct in6_addr *prefix, int prefixlen,
1862 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001863 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001864{
Thomas Graf86872cb2006-08-22 00:01:08 -07001865 struct fib6_config cfg = {
1866 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001867 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001868 .fc_ifindex = ifindex,
1869 .fc_dst_len = prefixlen,
1870 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1871 RTF_UP | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001872 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001873 .fc_nlinfo.nlh = NULL,
1874 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001875 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001876
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001877 cfg.fc_dst = *prefix;
1878 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001879
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001880 /* We should treat it as a default route if prefix length is 0. */
1881 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001882 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001883
Thomas Graf86872cb2006-08-22 00:01:08 -07001884 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001885
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001886 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001887}
1888#endif
1889
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001890struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001891{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001893 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001895 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001896 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001897 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898
Thomas Grafc71099a2006-08-04 23:20:06 -07001899 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001900 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001901 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001902 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1904 break;
1905 }
1906 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001907 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001908 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 return rt;
1910}
1911
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001912struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001913 struct net_device *dev,
1914 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915{
Thomas Graf86872cb2006-08-22 00:01:08 -07001916 struct fib6_config cfg = {
1917 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001918 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001919 .fc_ifindex = dev->ifindex,
1920 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1921 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001922 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08001923 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001924 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001925 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001927 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928
Thomas Graf86872cb2006-08-22 00:01:08 -07001929 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 return rt6_get_dflt_router(gwaddr, dev);
1932}
1933
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001934void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935{
1936 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001937 struct fib6_table *table;
1938
1939 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001940 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001941 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001942 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943
1944restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001945 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001946 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001948 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001949 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001950 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 goto restart;
1952 }
1953 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001954 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955}
1956
Daniel Lezcano55786892008-03-04 13:47:47 -08001957static void rtmsg_to_fib6_config(struct net *net,
1958 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001959 struct fib6_config *cfg)
1960{
1961 memset(cfg, 0, sizeof(*cfg));
1962
1963 cfg->fc_table = RT6_TABLE_MAIN;
1964 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1965 cfg->fc_metric = rtmsg->rtmsg_metric;
1966 cfg->fc_expires = rtmsg->rtmsg_info;
1967 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1968 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1969 cfg->fc_flags = rtmsg->rtmsg_flags;
1970
Daniel Lezcano55786892008-03-04 13:47:47 -08001971 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001972
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001973 cfg->fc_dst = rtmsg->rtmsg_dst;
1974 cfg->fc_src = rtmsg->rtmsg_src;
1975 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001976}
1977
Daniel Lezcano55786892008-03-04 13:47:47 -08001978int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979{
Thomas Graf86872cb2006-08-22 00:01:08 -07001980 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 struct in6_rtmsg rtmsg;
1982 int err;
1983
1984 switch(cmd) {
1985 case SIOCADDRT: /* Add a route */
1986 case SIOCDELRT: /* Delete a route */
1987 if (!capable(CAP_NET_ADMIN))
1988 return -EPERM;
1989 err = copy_from_user(&rtmsg, arg,
1990 sizeof(struct in6_rtmsg));
1991 if (err)
1992 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001993
Daniel Lezcano55786892008-03-04 13:47:47 -08001994 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001995
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 rtnl_lock();
1997 switch (cmd) {
1998 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001999 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 break;
2001 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002002 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 break;
2004 default:
2005 err = -EINVAL;
2006 }
2007 rtnl_unlock();
2008
2009 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002010 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011
2012 return -EINVAL;
2013}
2014
2015/*
2016 * Drop the packet on the floor
2017 */
2018
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002019static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002021 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002022 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002023 switch (ipstats_mib_noroutes) {
2024 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002025 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002026 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002027 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2028 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002029 break;
2030 }
2031 /* FALLTHROUGH */
2032 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002033 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2034 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002035 break;
2036 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002037 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038 kfree_skb(skb);
2039 return 0;
2040}
2041
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002042static int ip6_pkt_discard(struct sk_buff *skb)
2043{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002044 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002045}
2046
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002047static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048{
Eric Dumazetadf30902009-06-02 05:19:30 +00002049 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002050 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051}
2052
David S. Miller6723ab52006-10-18 21:20:57 -07002053#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2054
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002055static int ip6_pkt_prohibit(struct sk_buff *skb)
2056{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002057 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002058}
2059
2060static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2061{
Eric Dumazetadf30902009-06-02 05:19:30 +00002062 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002063 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002064}
2065
David S. Miller6723ab52006-10-18 21:20:57 -07002066#endif
2067
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068/*
2069 * Allocate a dst for local (unicast / anycast) address.
2070 */
2071
2072struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2073 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002074 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002076 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002077 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002078 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079
David S. Miller38308472011-12-03 18:02:47 -05002080 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002081 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002083 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 in6_dev_hold(idev);
2086
David S. Miller11d53b42011-06-24 15:23:34 -07002087 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002088 rt->dst.input = ip6_input;
2089 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002091 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092
2093 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002094 if (anycast)
2095 rt->rt6i_flags |= RTF_ANYCAST;
2096 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002098 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002099 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002100 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002101 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 }
2103
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002104 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002106 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107
Changli Gaod8d1f302010-06-10 23:31:35 -07002108 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109
2110 return rt;
2111}
2112
Daniel Walterc3968a82011-04-13 21:10:57 +00002113int ip6_route_get_saddr(struct net *net,
2114 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002115 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002116 unsigned int prefs,
2117 struct in6_addr *saddr)
2118{
2119 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2120 int err = 0;
2121 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002122 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002123 else
2124 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2125 daddr, prefs, saddr);
2126 return err;
2127}
2128
2129/* remove deleted ip from prefsrc entries */
2130struct arg_dev_net_ip {
2131 struct net_device *dev;
2132 struct net *net;
2133 struct in6_addr *addr;
2134};
2135
2136static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2137{
2138 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2139 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2140 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2141
David S. Millerd1918542011-12-28 20:19:20 -05002142 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002143 rt != net->ipv6.ip6_null_entry &&
2144 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2145 /* remove prefsrc entry */
2146 rt->rt6i_prefsrc.plen = 0;
2147 }
2148 return 0;
2149}
2150
2151void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2152{
2153 struct net *net = dev_net(ifp->idev->dev);
2154 struct arg_dev_net_ip adni = {
2155 .dev = ifp->idev->dev,
2156 .net = net,
2157 .addr = &ifp->addr,
2158 };
2159 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2160}
2161
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002162struct arg_dev_net {
2163 struct net_device *dev;
2164 struct net *net;
2165};
2166
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167static int fib6_ifdown(struct rt6_info *rt, void *arg)
2168{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002169 const struct arg_dev_net *adn = arg;
2170 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002171
David S. Millerd1918542011-12-28 20:19:20 -05002172 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002173 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002175
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 return 0;
2177}
2178
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002179void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002181 struct arg_dev_net adn = {
2182 .dev = dev,
2183 .net = net,
2184 };
2185
2186 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002187 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188}
2189
Eric Dumazet95c96172012-04-15 05:58:06 +00002190struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002192 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193};
2194
2195static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2196{
2197 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2198 struct inet6_dev *idev;
2199
2200 /* In IPv6 pmtu discovery is not optional,
2201 so that RTAX_MTU lock cannot disable it.
2202 We still use this lock to block changes
2203 caused by addrconf/ndisc.
2204 */
2205
2206 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002207 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 return 0;
2209
2210 /* For administrative MTU increase, there is no way to discover
2211 IPv6 PMTU increase, so PMTU increase should be updated here.
2212 Since RFC 1981 doesn't include administrative MTU increase
2213 update PMTU increase is a MUST. (i.e. jumbo frame)
2214 */
2215 /*
2216 If new MTU is less than route PMTU, this new MTU will be the
2217 lowest MTU in the path, update the route PMTU to reflect PMTU
2218 decreases; if new MTU is greater than route PMTU, and the
2219 old MTU is the lowest MTU in the path, update the route PMTU
2220 to reflect the increase. In this case if the other nodes' MTU
2221 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2222 PMTU discouvery.
2223 */
David S. Millerd1918542011-12-28 20:19:20 -05002224 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002225 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2226 (dst_mtu(&rt->dst) >= arg->mtu ||
2227 (dst_mtu(&rt->dst) < arg->mtu &&
2228 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002229 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002230 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 return 0;
2232}
2233
Eric Dumazet95c96172012-04-15 05:58:06 +00002234void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235{
Thomas Grafc71099a2006-08-04 23:20:06 -07002236 struct rt6_mtu_change_arg arg = {
2237 .dev = dev,
2238 .mtu = mtu,
2239 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002241 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242}
2243
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002244static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002245 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002246 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002247 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002248 [RTA_PRIORITY] = { .type = NLA_U32 },
2249 [RTA_METRICS] = { .type = NLA_NESTED },
2250};
2251
2252static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2253 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254{
Thomas Graf86872cb2006-08-22 00:01:08 -07002255 struct rtmsg *rtm;
2256 struct nlattr *tb[RTA_MAX+1];
2257 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258
Thomas Graf86872cb2006-08-22 00:01:08 -07002259 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2260 if (err < 0)
2261 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262
Thomas Graf86872cb2006-08-22 00:01:08 -07002263 err = -EINVAL;
2264 rtm = nlmsg_data(nlh);
2265 memset(cfg, 0, sizeof(*cfg));
2266
2267 cfg->fc_table = rtm->rtm_table;
2268 cfg->fc_dst_len = rtm->rtm_dst_len;
2269 cfg->fc_src_len = rtm->rtm_src_len;
2270 cfg->fc_flags = RTF_UP;
2271 cfg->fc_protocol = rtm->rtm_protocol;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002272 cfg->fc_type = rtm->rtm_type;
Thomas Graf86872cb2006-08-22 00:01:08 -07002273
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002274 if (rtm->rtm_type == RTN_UNREACHABLE ||
2275 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002276 rtm->rtm_type == RTN_PROHIBIT ||
2277 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07002278 cfg->fc_flags |= RTF_REJECT;
2279
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002280 if (rtm->rtm_type == RTN_LOCAL)
2281 cfg->fc_flags |= RTF_LOCAL;
2282
Eric W. Biederman15e47302012-09-07 20:12:54 +00002283 cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
Thomas Graf86872cb2006-08-22 00:01:08 -07002284 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002285 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002286
2287 if (tb[RTA_GATEWAY]) {
2288 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2289 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002291
2292 if (tb[RTA_DST]) {
2293 int plen = (rtm->rtm_dst_len + 7) >> 3;
2294
2295 if (nla_len(tb[RTA_DST]) < plen)
2296 goto errout;
2297
2298 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002300
2301 if (tb[RTA_SRC]) {
2302 int plen = (rtm->rtm_src_len + 7) >> 3;
2303
2304 if (nla_len(tb[RTA_SRC]) < plen)
2305 goto errout;
2306
2307 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002309
Daniel Walterc3968a82011-04-13 21:10:57 +00002310 if (tb[RTA_PREFSRC])
2311 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2312
Thomas Graf86872cb2006-08-22 00:01:08 -07002313 if (tb[RTA_OIF])
2314 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2315
2316 if (tb[RTA_PRIORITY])
2317 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2318
2319 if (tb[RTA_METRICS]) {
2320 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2321 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002323
2324 if (tb[RTA_TABLE])
2325 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2326
2327 err = 0;
2328errout:
2329 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330}
2331
Thomas Grafc127ea22007-03-22 11:58:32 -07002332static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333{
Thomas Graf86872cb2006-08-22 00:01:08 -07002334 struct fib6_config cfg;
2335 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336
Thomas Graf86872cb2006-08-22 00:01:08 -07002337 err = rtm_to_fib6_config(skb, nlh, &cfg);
2338 if (err < 0)
2339 return err;
2340
2341 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342}
2343
Thomas Grafc127ea22007-03-22 11:58:32 -07002344static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345{
Thomas Graf86872cb2006-08-22 00:01:08 -07002346 struct fib6_config cfg;
2347 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348
Thomas Graf86872cb2006-08-22 00:01:08 -07002349 err = rtm_to_fib6_config(skb, nlh, &cfg);
2350 if (err < 0)
2351 return err;
2352
2353 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354}
2355
Thomas Graf339bf982006-11-10 14:10:15 -08002356static inline size_t rt6_nlmsg_size(void)
2357{
2358 return NLMSG_ALIGN(sizeof(struct rtmsg))
2359 + nla_total_size(16) /* RTA_SRC */
2360 + nla_total_size(16) /* RTA_DST */
2361 + nla_total_size(16) /* RTA_GATEWAY */
2362 + nla_total_size(16) /* RTA_PREFSRC */
2363 + nla_total_size(4) /* RTA_TABLE */
2364 + nla_total_size(4) /* RTA_IIF */
2365 + nla_total_size(4) /* RTA_OIF */
2366 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002367 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002368 + nla_total_size(sizeof(struct rta_cacheinfo));
2369}
2370
Brian Haley191cd582008-08-14 15:33:21 -07002371static int rt6_fill_node(struct net *net,
2372 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002373 struct in6_addr *dst, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002374 int iif, int type, u32 portid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002375 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376{
2377 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002378 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002379 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002380 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002381 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
2383 if (prefix) { /* user wants prefix routes only */
2384 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2385 /* success since this is not a prefix route */
2386 return 1;
2387 }
2388 }
2389
Eric W. Biederman15e47302012-09-07 20:12:54 +00002390 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002391 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002392 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002393
2394 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395 rtm->rtm_family = AF_INET6;
2396 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2397 rtm->rtm_src_len = rt->rt6i_src.plen;
2398 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002399 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002400 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002401 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002402 table = RT6_TABLE_UNSPEC;
2403 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002404 if (nla_put_u32(skb, RTA_TABLE, table))
2405 goto nla_put_failure;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002406 if (rt->rt6i_flags & RTF_REJECT) {
2407 switch (rt->dst.error) {
2408 case -EINVAL:
2409 rtm->rtm_type = RTN_BLACKHOLE;
2410 break;
2411 case -EACCES:
2412 rtm->rtm_type = RTN_PROHIBIT;
2413 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002414 case -EAGAIN:
2415 rtm->rtm_type = RTN_THROW;
2416 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002417 default:
2418 rtm->rtm_type = RTN_UNREACHABLE;
2419 break;
2420 }
2421 }
David S. Miller38308472011-12-03 18:02:47 -05002422 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002423 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002424 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425 rtm->rtm_type = RTN_LOCAL;
2426 else
2427 rtm->rtm_type = RTN_UNICAST;
2428 rtm->rtm_flags = 0;
2429 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2430 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002431 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432 rtm->rtm_protocol = RTPROT_REDIRECT;
Denis Ovsienkof0396f602012-07-10 04:45:50 +00002433 else if (rt->rt6i_flags & RTF_ADDRCONF) {
2434 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
2435 rtm->rtm_protocol = RTPROT_RA;
2436 else
2437 rtm->rtm_protocol = RTPROT_KERNEL;
2438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439
David S. Miller38308472011-12-03 18:02:47 -05002440 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441 rtm->rtm_flags |= RTM_F_CLONED;
2442
2443 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002444 if (nla_put(skb, RTA_DST, 16, dst))
2445 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002446 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002448 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2449 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450#ifdef CONFIG_IPV6_SUBTREES
2451 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002452 if (nla_put(skb, RTA_SRC, 16, src))
2453 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002454 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002455 } else if (rtm->rtm_src_len &&
2456 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2457 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002459 if (iif) {
2460#ifdef CONFIG_IPV6_MROUTE
2461 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002462 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002463 if (err <= 0) {
2464 if (!nowait) {
2465 if (err == 0)
2466 return 0;
2467 goto nla_put_failure;
2468 } else {
2469 if (err == -EMSGSIZE)
2470 goto nla_put_failure;
2471 }
2472 }
2473 } else
2474#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002475 if (nla_put_u32(skb, RTA_IIF, iif))
2476 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002477 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002479 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2480 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2481 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002483
Daniel Walterc3968a82011-04-13 21:10:57 +00002484 if (rt->rt6i_prefsrc.plen) {
2485 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002486 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002487 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2488 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002489 }
2490
David S. Millerdefb3512010-12-08 21:16:57 -08002491 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002492 goto nla_put_failure;
2493
David S. Miller97cac082012-07-02 22:43:47 -07002494 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002495 if (n) {
Amerigo Wangfdd66812012-09-10 02:48:44 +00002496 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0)
Eric Dumazet94f826b2012-03-27 09:53:52 +00002497 goto nla_put_failure;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002498 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002499
David S. Millerc78679e2012-04-01 20:27:33 -04002500 if (rt->dst.dev &&
2501 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2502 goto nla_put_failure;
2503 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2504 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00002505
2506 expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002507
David S. Miller87a50692012-07-10 05:06:14 -07002508 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002509 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510
Thomas Graf2d7202b2006-08-22 00:01:27 -07002511 return nlmsg_end(skb, nlh);
2512
2513nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002514 nlmsg_cancel(skb, nlh);
2515 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516}
2517
Patrick McHardy1b43af52006-08-10 23:11:17 -07002518int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519{
2520 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2521 int prefix;
2522
Thomas Graf2d7202b2006-08-22 00:01:27 -07002523 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2524 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2526 } else
2527 prefix = 0;
2528
Brian Haley191cd582008-08-14 15:33:21 -07002529 return rt6_fill_node(arg->net,
2530 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002531 NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002532 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533}
2534
Thomas Grafc127ea22007-03-22 11:58:32 -07002535static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002537 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002538 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002540 struct sk_buff *skb;
2541 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002542 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002543 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002544
2545 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2546 if (err < 0)
2547 goto errout;
2548
2549 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002550 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002551
2552 if (tb[RTA_SRC]) {
2553 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2554 goto errout;
2555
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002556 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002557 }
2558
2559 if (tb[RTA_DST]) {
2560 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2561 goto errout;
2562
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002563 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002564 }
2565
2566 if (tb[RTA_IIF])
2567 iif = nla_get_u32(tb[RTA_IIF]);
2568
2569 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002570 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002571
2572 if (iif) {
2573 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002574 int flags = 0;
2575
Daniel Lezcano55786892008-03-04 13:47:47 -08002576 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002577 if (!dev) {
2578 err = -ENODEV;
2579 goto errout;
2580 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002581
2582 fl6.flowi6_iif = iif;
2583
2584 if (!ipv6_addr_any(&fl6.saddr))
2585 flags |= RT6_LOOKUP_F_HAS_SADDR;
2586
2587 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2588 flags);
2589 } else {
2590 fl6.flowi6_oif = oif;
2591
2592 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002593 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594
2595 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002596 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002597 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002598 err = -ENOBUFS;
2599 goto errout;
2600 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601
2602 /* Reserve room for dummy headers, this skb can pass
2603 through good chunk of routing engine.
2604 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002605 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2607
Changli Gaod8d1f302010-06-10 23:31:35 -07002608 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609
David S. Miller4c9483b2011-03-12 16:22:43 -05002610 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002611 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002612 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002614 kfree_skb(skb);
2615 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616 }
2617
Eric W. Biederman15e47302012-09-07 20:12:54 +00002618 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07002619errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621}
2622
Thomas Graf86872cb2006-08-22 00:01:08 -07002623void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624{
2625 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002626 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002627 u32 seq;
2628 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002630 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002631 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002632
Thomas Graf339bf982006-11-10 14:10:15 -08002633 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002634 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002635 goto errout;
2636
Brian Haley191cd582008-08-14 15:33:21 -07002637 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002638 event, info->portid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002639 if (err < 0) {
2640 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2641 WARN_ON(err == -EMSGSIZE);
2642 kfree_skb(skb);
2643 goto errout;
2644 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00002645 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002646 info->nlh, gfp_any());
2647 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002648errout:
2649 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002650 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651}
2652
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002653static int ip6_route_dev_notify(struct notifier_block *this,
2654 unsigned long event, void *data)
2655{
2656 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002657 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002658
2659 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002660 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002661 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2662#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002663 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002664 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002665 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002666 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2667#endif
2668 }
2669
2670 return NOTIFY_OK;
2671}
2672
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673/*
2674 * /proc
2675 */
2676
2677#ifdef CONFIG_PROC_FS
2678
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679struct rt6_proc_arg
2680{
2681 char *buffer;
2682 int offset;
2683 int length;
2684 int skip;
2685 int len;
2686};
2687
2688static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2689{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002690 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002691 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002693 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
2695#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002696 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002698 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699#endif
David S. Miller97cac082012-07-02 22:43:47 -07002700 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002701 if (n) {
2702 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002704 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 }
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002706 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002707 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2708 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002709 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710 return 0;
2711}
2712
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002713static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002715 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002716 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002717 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718}
2719
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002720static int ipv6_route_open(struct inode *inode, struct file *file)
2721{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002722 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002723}
2724
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002725static const struct file_operations ipv6_route_proc_fops = {
2726 .owner = THIS_MODULE,
2727 .open = ipv6_route_open,
2728 .read = seq_read,
2729 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002730 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002731};
2732
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2734{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002735 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002737 net->ipv6.rt6_stats->fib_nodes,
2738 net->ipv6.rt6_stats->fib_route_nodes,
2739 net->ipv6.rt6_stats->fib_rt_alloc,
2740 net->ipv6.rt6_stats->fib_rt_entries,
2741 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002742 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002743 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744
2745 return 0;
2746}
2747
2748static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2749{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002750 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002751}
2752
Arjan van de Ven9a321442007-02-12 00:55:35 -08002753static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 .owner = THIS_MODULE,
2755 .open = rt6_stats_seq_open,
2756 .read = seq_read,
2757 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002758 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759};
2760#endif /* CONFIG_PROC_FS */
2761
2762#ifdef CONFIG_SYSCTL
2763
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002765int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 void __user *buffer, size_t *lenp, loff_t *ppos)
2767{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002768 struct net *net;
2769 int delay;
2770 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002772
2773 net = (struct net *)ctl->extra1;
2774 delay = net->ipv6.sysctl.flush_delay;
2775 proc_dointvec(ctl, write, buffer, lenp, ppos);
2776 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2777 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778}
2779
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002780ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002781 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002783 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002785 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002786 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 },
2788 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002790 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 .maxlen = sizeof(int),
2792 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002793 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 },
2795 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002797 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 .maxlen = sizeof(int),
2799 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002800 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 },
2802 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002804 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 .maxlen = sizeof(int),
2806 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002807 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 },
2809 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002811 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 .maxlen = sizeof(int),
2813 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002814 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 },
2816 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002818 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 .maxlen = sizeof(int),
2820 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002821 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 },
2823 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002825 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 .maxlen = sizeof(int),
2827 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002828 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 },
2830 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002832 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 .maxlen = sizeof(int),
2834 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002835 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 },
2837 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002839 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 .maxlen = sizeof(int),
2841 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002842 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 },
2844 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002846 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 .maxlen = sizeof(int),
2848 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002849 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002851 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852};
2853
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002854struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002855{
2856 struct ctl_table *table;
2857
2858 table = kmemdup(ipv6_route_table_template,
2859 sizeof(ipv6_route_table_template),
2860 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002861
2862 if (table) {
2863 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002864 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002865 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002866 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2867 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2868 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2869 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2870 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2871 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2872 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002873 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002874 }
2875
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002876 return table;
2877}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878#endif
2879
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002880static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002881{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002882 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002883
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002884 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2885 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002886
Eric Dumazetfc66f952010-10-08 06:37:34 +00002887 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2888 goto out_ip6_dst_ops;
2889
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002890 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2891 sizeof(*net->ipv6.ip6_null_entry),
2892 GFP_KERNEL);
2893 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002894 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002895 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002896 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002897 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002898 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2899 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002900
2901#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2902 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2903 sizeof(*net->ipv6.ip6_prohibit_entry),
2904 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002905 if (!net->ipv6.ip6_prohibit_entry)
2906 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002907 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002908 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002909 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002910 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2911 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002912
2913 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2914 sizeof(*net->ipv6.ip6_blk_hole_entry),
2915 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002916 if (!net->ipv6.ip6_blk_hole_entry)
2917 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002918 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002919 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002920 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002921 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2922 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002923#endif
2924
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002925 net->ipv6.sysctl.flush_delay = 0;
2926 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2927 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2928 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2929 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2930 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2931 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2932 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2933
Benjamin Thery6891a342008-03-04 13:49:47 -08002934 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2935
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002936 ret = 0;
2937out:
2938 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002939
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002940#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2941out_ip6_prohibit_entry:
2942 kfree(net->ipv6.ip6_prohibit_entry);
2943out_ip6_null_entry:
2944 kfree(net->ipv6.ip6_null_entry);
2945#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002946out_ip6_dst_entries:
2947 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002948out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002949 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002950}
2951
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002952static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002953{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002954 kfree(net->ipv6.ip6_null_entry);
2955#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2956 kfree(net->ipv6.ip6_prohibit_entry);
2957 kfree(net->ipv6.ip6_blk_hole_entry);
2958#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002959 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002960}
2961
Thomas Grafd1896342012-06-18 12:08:33 +00002962static int __net_init ip6_route_net_init_late(struct net *net)
2963{
2964#ifdef CONFIG_PROC_FS
2965 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2966 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2967#endif
2968 return 0;
2969}
2970
2971static void __net_exit ip6_route_net_exit_late(struct net *net)
2972{
2973#ifdef CONFIG_PROC_FS
2974 proc_net_remove(net, "ipv6_route");
2975 proc_net_remove(net, "rt6_stats");
2976#endif
2977}
2978
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002979static struct pernet_operations ip6_route_net_ops = {
2980 .init = ip6_route_net_init,
2981 .exit = ip6_route_net_exit,
2982};
2983
David S. Millerc3426b42012-06-09 16:27:05 -07002984static int __net_init ipv6_inetpeer_init(struct net *net)
2985{
2986 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2987
2988 if (!bp)
2989 return -ENOMEM;
2990 inet_peer_base_init(bp);
2991 net->ipv6.peers = bp;
2992 return 0;
2993}
2994
2995static void __net_exit ipv6_inetpeer_exit(struct net *net)
2996{
2997 struct inet_peer_base *bp = net->ipv6.peers;
2998
2999 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07003000 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07003001 kfree(bp);
3002}
3003
David S. Miller2b823f72012-06-09 19:00:16 -07003004static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003005 .init = ipv6_inetpeer_init,
3006 .exit = ipv6_inetpeer_exit,
3007};
3008
Thomas Grafd1896342012-06-18 12:08:33 +00003009static struct pernet_operations ip6_route_net_late_ops = {
3010 .init = ip6_route_net_init_late,
3011 .exit = ip6_route_net_exit_late,
3012};
3013
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003014static struct notifier_block ip6_route_dev_notifier = {
3015 .notifier_call = ip6_route_dev_notify,
3016 .priority = 0,
3017};
3018
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003019int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003021 int ret;
3022
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003023 ret = -ENOMEM;
3024 ip6_dst_ops_template.kmem_cachep =
3025 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3026 SLAB_HWCACHE_ALIGN, NULL);
3027 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003028 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003029
Eric Dumazetfc66f952010-10-08 06:37:34 +00003030 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003031 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003032 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003033
David S. Millerc3426b42012-06-09 16:27:05 -07003034 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3035 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003036 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003037
David S. Miller7e52b332012-06-15 15:51:55 -07003038 ret = register_pernet_subsys(&ip6_route_net_ops);
3039 if (ret)
3040 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003041
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003042 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3043
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003044 /* Registering of the loopback is done before this portion of code,
3045 * the loopback reference in rt6_info will not be taken, do it
3046 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003047 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003048 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3049 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003050 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003051 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003052 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003053 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3054 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003055 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003056 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003057 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003058
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003059 ret = xfrm6_init();
3060 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003061 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003062
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003063 ret = fib6_rules_init();
3064 if (ret)
3065 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003066
Thomas Grafd1896342012-06-18 12:08:33 +00003067 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3068 if (ret)
3069 goto fib6_rules_init;
3070
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003071 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003072 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3073 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3074 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003075 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003076
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003077 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003078 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003079 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003080
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003081out:
3082 return ret;
3083
Thomas Grafd1896342012-06-18 12:08:33 +00003084out_register_late_subsys:
3085 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003086fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003087 fib6_rules_cleanup();
3088xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003089 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003090out_fib6_init:
3091 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003092out_register_subsys:
3093 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003094out_register_inetpeer:
3095 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003096out_dst_entries:
3097 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003098out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003099 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003100 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003101}
3102
3103void ip6_route_cleanup(void)
3104{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003105 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003106 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003107 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003108 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003110 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003111 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003112 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003113 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114}