blob: 0607ee3a0eac889b9efe2e03120755c3afdaeeb0 [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
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000225static const 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
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000245static const 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
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000260static const 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 if (rt->rt6i_flags & RTF_EXPIRES) {
373 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000374 return true;
Gao feng1716a962012-04-06 00:13:10 +0000375 } else if (rt->dst.from) {
Li RongQing3fd91fb2012-09-13 19:54:57 +0000376 return rt6_check_expired((struct rt6_info *) rt->dst.from);
Gao feng1716a962012-04-06 00:13:10 +0000377 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000378 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
Eric Dumazeta50feda2012-05-18 18:57:34 +0000381static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700382{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000383 return ipv6_addr_type(daddr) &
384 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700385}
386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700388 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 */
390
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800391static inline struct rt6_info *rt6_device_match(struct net *net,
392 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000393 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700395 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396{
397 struct rt6_info *local = NULL;
398 struct rt6_info *sprt;
399
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900400 if (!oif && ipv6_addr_any(saddr))
401 goto out;
402
Changli Gaod8d1f302010-06-10 23:31:35 -0700403 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500404 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900405
406 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 if (dev->ifindex == oif)
408 return sprt;
409 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500410 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700412 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900414 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 local->rt6i_idev->dev->ifindex == oif))
416 continue;
417 }
418 local = sprt;
419 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900420 } else {
421 if (ipv6_chk_addr(net, saddr, dev,
422 flags & RT6_LOOKUP_F_IFACE))
423 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900427 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 if (local)
429 return local;
430
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700431 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800432 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900434out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 return rt;
436}
437
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800438#ifdef CONFIG_IPV6_ROUTER_PREF
439static void rt6_probe(struct rt6_info *rt)
440{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000441 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800442 /*
443 * Okay, this does not seem to be appropriate
444 * for now, however, we need to check if it
445 * is really so; aka Router Reachability Probing.
446 *
447 * Router Reachability Probe MUST be rate-limited
448 * to no more than one per minute.
449 */
David S. Miller97cac082012-07-02 22:43:47 -0700450 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800451 if (!neigh || (neigh->nud_state & NUD_VALID))
Amerigo Wangfdd66812012-09-10 02:48:44 +0000452 return;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800453 read_lock_bh(&neigh->lock);
454 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800455 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800456 struct in6_addr mcaddr;
457 struct in6_addr *target;
458
459 neigh->updated = jiffies;
460 read_unlock_bh(&neigh->lock);
461
462 target = (struct in6_addr *)&neigh->primary_key;
463 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500464 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000465 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800466 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000467 }
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800468}
469#else
470static inline void rt6_probe(struct rt6_info *rt)
471{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800472}
473#endif
474
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800476 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700478static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
David S. Millerd1918542011-12-28 20:19:20 -0500480 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700481 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800482 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700483 if ((dev->flags & IFF_LOOPBACK) &&
484 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
485 return 1;
486 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487}
488
Dave Jonesb6f99a22007-03-22 12:27:49 -0700489static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000491 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800492 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000493
David S. Miller97cac082012-07-02 22:43:47 -0700494 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700495 if (rt->rt6i_flags & RTF_NONEXTHOP ||
496 !(rt->rt6i_flags & RTF_GATEWAY))
497 m = 1;
498 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800499 read_lock_bh(&neigh->lock);
500 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700501 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800502#ifdef CONFIG_IPV6_ROUTER_PREF
503 else if (neigh->nud_state & NUD_FAILED)
504 m = 0;
505#endif
506 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800507 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800508 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800509 } else
510 m = 0;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800511 return m;
512}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800514static int rt6_score_route(struct rt6_info *rt, int oif,
515 int strict)
516{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700517 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900518
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700519 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700520 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800521 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800522#ifdef CONFIG_IPV6_ROUTER_PREF
523 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
524#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700525 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800526 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800527 return -1;
528 return m;
529}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
David S. Millerf11e6652007-03-24 20:36:25 -0700531static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
532 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800533{
David S. Millerf11e6652007-03-24 20:36:25 -0700534 int m;
535
536 if (rt6_check_expired(rt))
537 goto out;
538
539 m = rt6_score_route(rt, oif, strict);
540 if (m < 0)
541 goto out;
542
543 if (m > *mpri) {
544 if (strict & RT6_LOOKUP_F_REACHABLE)
545 rt6_probe(match);
546 *mpri = m;
547 match = rt;
548 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
549 rt6_probe(rt);
550 }
551
552out:
553 return match;
554}
555
556static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
557 struct rt6_info *rr_head,
558 u32 metric, int oif, int strict)
559{
560 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800561 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
David S. Millerf11e6652007-03-24 20:36:25 -0700563 match = NULL;
564 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700565 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700566 match = find_match(rt, oif, strict, &mpri, match);
567 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700568 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700569 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800570
David S. Millerf11e6652007-03-24 20:36:25 -0700571 return match;
572}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800573
David S. Millerf11e6652007-03-24 20:36:25 -0700574static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
575{
576 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800577 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
David S. Millerf11e6652007-03-24 20:36:25 -0700579 rt0 = fn->rr_ptr;
580 if (!rt0)
581 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
David S. Millerf11e6652007-03-24 20:36:25 -0700583 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800585 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700586 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700587 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700588
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800589 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700590 if (!next || next->rt6i_metric != rt0->rt6i_metric)
591 next = fn->leaf;
592
593 if (next != rt0)
594 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 }
596
David S. Millerd1918542011-12-28 20:19:20 -0500597 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000598 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
600
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800601#ifdef CONFIG_IPV6_ROUTE_INFO
602int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000603 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800604{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900605 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800606 struct route_info *rinfo = (struct route_info *) opt;
607 struct in6_addr prefix_buf, *prefix;
608 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900609 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800610 struct rt6_info *rt;
611
612 if (len < sizeof(struct route_info)) {
613 return -EINVAL;
614 }
615
616 /* Sanity check for prefix_len and length */
617 if (rinfo->length > 3) {
618 return -EINVAL;
619 } else if (rinfo->prefix_len > 128) {
620 return -EINVAL;
621 } else if (rinfo->prefix_len > 64) {
622 if (rinfo->length < 2) {
623 return -EINVAL;
624 }
625 } else if (rinfo->prefix_len > 0) {
626 if (rinfo->length < 1) {
627 return -EINVAL;
628 }
629 }
630
631 pref = rinfo->route_pref;
632 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000633 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800634
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900635 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800636
637 if (rinfo->length == 3)
638 prefix = (struct in6_addr *)rinfo->prefix;
639 else {
640 /* this function is safe */
641 ipv6_addr_prefix(&prefix_buf,
642 (struct in6_addr *)rinfo->prefix,
643 rinfo->prefix_len);
644 prefix = &prefix_buf;
645 }
646
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800647 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
648 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800649
650 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700651 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800652 rt = NULL;
653 }
654
655 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800656 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800657 pref);
658 else if (rt)
659 rt->rt6i_flags = RTF_ROUTEINFO |
660 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
661
662 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000663 if (!addrconf_finite_timeout(lifetime))
664 rt6_clean_expires(rt);
665 else
666 rt6_set_expires(rt, jiffies + HZ * lifetime);
667
Changli Gaod8d1f302010-06-10 23:31:35 -0700668 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800669 }
670 return 0;
671}
672#endif
673
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800674#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700675do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800676 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700677 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700678 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700679 if (fn->fn_flags & RTN_TL_ROOT) \
680 goto out; \
681 pn = fn->parent; \
682 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800683 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700684 else \
685 fn = pn; \
686 if (fn->fn_flags & RTN_RTINFO) \
687 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700688 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700689 } \
David S. Miller38308472011-12-03 18:02:47 -0500690} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700691
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800692static struct rt6_info *ip6_pol_route_lookup(struct net *net,
693 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500694 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695{
696 struct fib6_node *fn;
697 struct rt6_info *rt;
698
Thomas Grafc71099a2006-08-04 23:20:06 -0700699 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500700 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700701restart:
702 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500703 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
704 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700705out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700706 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700707 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700708 return rt;
709
710}
711
Florian Westphalea6e5742011-09-05 16:05:44 +0200712struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
713 int flags)
714{
715 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
716}
717EXPORT_SYMBOL_GPL(ip6_route_lookup);
718
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900719struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
720 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700721{
David S. Miller4c9483b2011-03-12 16:22:43 -0500722 struct flowi6 fl6 = {
723 .flowi6_oif = oif,
724 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700725 };
726 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700727 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700728
Thomas Grafadaa70b2006-10-13 15:01:03 -0700729 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500730 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700731 flags |= RT6_LOOKUP_F_HAS_SADDR;
732 }
733
David S. Miller4c9483b2011-03-12 16:22:43 -0500734 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700735 if (dst->error == 0)
736 return (struct rt6_info *) dst;
737
738 dst_release(dst);
739
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 return NULL;
741}
742
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900743EXPORT_SYMBOL(rt6_lookup);
744
Thomas Grafc71099a2006-08-04 23:20:06 -0700745/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 It takes new route entry, the addition fails by any reason the
747 route is freed. In any case, if caller does not hold it, it may
748 be destroyed.
749 */
750
Thomas Graf86872cb2006-08-22 00:01:08 -0700751static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752{
753 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700754 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Thomas Grafc71099a2006-08-04 23:20:06 -0700756 table = rt->rt6i_table;
757 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700758 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700759 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
761 return err;
762}
763
Thomas Graf40e22e82006-08-22 00:00:45 -0700764int ip6_ins_rt(struct rt6_info *rt)
765{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800766 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500767 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800768 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800769 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700770}
771
Gao feng1716a962012-04-06 00:13:10 +0000772static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000773 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000774 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 struct rt6_info *rt;
777
778 /*
779 * Clone the route.
780 */
781
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000782 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
784 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800785 int attempts = !in_softirq();
786
David S. Miller38308472011-12-03 18:02:47 -0500787 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500788 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000789 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900790 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000791 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900792 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796#ifdef CONFIG_IPV6_SUBTREES
797 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000798 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 rt->rt6i_src.plen = 128;
800 }
801#endif
802
David S. Miller14deae42009-01-04 16:04:39 -0800803 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500804 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500805 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800806 int saved_rt_min_interval =
807 net->ipv6.sysctl.ip6_rt_gc_min_interval;
808 int saved_rt_elasticity =
809 net->ipv6.sysctl.ip6_rt_gc_elasticity;
810
811 if (attempts-- > 0) {
812 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
813 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
814
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000815 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800816
817 net->ipv6.sysctl.ip6_rt_gc_elasticity =
818 saved_rt_elasticity;
819 net->ipv6.sysctl.ip6_rt_gc_min_interval =
820 saved_rt_min_interval;
821 goto retry;
822 }
823
Joe Perchesf3213832012-05-15 14:11:53 +0000824 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700825 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800826 return NULL;
827 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800830 return rt;
831}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000833static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
834 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800835{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000836 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
837
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800838 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800839 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700840 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800841 }
842 return rt;
843}
844
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800845static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500846 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847{
848 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800849 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700850 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800852 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700853 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700855 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
857relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700858 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800860restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500861 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
863restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700864 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800865
David S. Miller4c9483b2011-03-12 16:22:43 -0500866 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800867 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800868 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800869 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
Changli Gaod8d1f302010-06-10 23:31:35 -0700871 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700872 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800873
David S. Miller97cac082012-07-02 22:43:47 -0700874 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500875 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800876 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500877 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800878 else
879 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800880
Changli Gaod8d1f302010-06-10 23:31:35 -0700881 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800882 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800883
Changli Gaod8d1f302010-06-10 23:31:35 -0700884 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800885 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700886 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800887 if (!err)
888 goto out2;
889 }
890
891 if (--attempts <= 0)
892 goto out2;
893
894 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700895 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800896 * released someone could insert this route. Relookup.
897 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700898 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800899 goto relookup;
900
901out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800902 if (reachable) {
903 reachable = 0;
904 goto restart_2;
905 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700906 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700907 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700909 rt->dst.lastuse = jiffies;
910 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700911
912 return rt;
913}
914
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800915static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500916 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700917{
David S. Miller4c9483b2011-03-12 16:22:43 -0500918 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700919}
920
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000921static struct dst_entry *ip6_route_input_lookup(struct net *net,
922 struct net_device *dev,
923 struct flowi6 *fl6, int flags)
924{
925 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
926 flags |= RT6_LOOKUP_F_IFACE;
927
928 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
929}
930
Thomas Grafc71099a2006-08-04 23:20:06 -0700931void ip6_route_input(struct sk_buff *skb)
932{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000933 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900934 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700935 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500936 struct flowi6 fl6 = {
937 .flowi6_iif = skb->dev->ifindex,
938 .daddr = iph->daddr,
939 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500940 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500941 .flowi6_mark = skb->mark,
942 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700943 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700944
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000945 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700946}
947
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800948static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500949 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700950{
David S. Miller4c9483b2011-03-12 16:22:43 -0500951 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700952}
953
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700954struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500955 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700956{
957 int flags = 0;
958
Pavel Emelyanov1fb94892012-08-08 21:53:36 +0000959 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +0000960
David S. Miller4c9483b2011-03-12 16:22:43 -0500961 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700962 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700963
David S. Miller4c9483b2011-03-12 16:22:43 -0500964 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700965 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000966 else if (sk)
967 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700968
David S. Miller4c9483b2011-03-12 16:22:43 -0500969 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970}
971
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900972EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
David S. Miller2774c132011-03-01 14:59:04 -0800974struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700975{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700976 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700977 struct dst_entry *new = NULL;
978
David S. Millerf5b0a872012-07-19 12:31:33 -0700979 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700980 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700981 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700982
Steffen Klassert81048912012-07-05 23:37:09 +0000983 memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
984 rt6_init_peer(rt, net->ipv6.peers);
985
David S. Miller14e50e52007-05-24 18:17:54 -0700986 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800987 new->input = dst_discard;
988 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700989
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000990 if (dst_metrics_read_only(&ort->dst))
991 new->_metrics = ort->dst._metrics;
992 else
993 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700994 rt->rt6i_idev = ort->rt6i_idev;
995 if (rt->rt6i_idev)
996 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -0700997
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000998 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +0000999 rt->rt6i_flags = ort->rt6i_flags;
1000 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001001 rt->rt6i_metric = 0;
1002
1003 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1004#ifdef CONFIG_IPV6_SUBTREES
1005 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1006#endif
1007
1008 dst_free(new);
1009 }
1010
David S. Miller69ead7a2011-03-01 14:45:33 -08001011 dst_release(dst_orig);
1012 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001013}
David S. Miller14e50e52007-05-24 18:17:54 -07001014
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015/*
1016 * Destination cache support functions
1017 */
1018
1019static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1020{
1021 struct rt6_info *rt;
1022
1023 rt = (struct rt6_info *) dst;
1024
David S. Miller6431cbc2011-02-07 20:38:06 -08001025 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1026 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001027 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001028 rt6_bind_peer(rt, 0);
1029 rt->rt6i_peer_genid = rt6_peer_genid();
1030 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001032 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 return NULL;
1034}
1035
1036static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1037{
1038 struct rt6_info *rt = (struct rt6_info *) dst;
1039
1040 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001041 if (rt->rt6i_flags & RTF_CACHE) {
1042 if (rt6_check_expired(rt)) {
1043 ip6_del_rt(rt);
1044 dst = NULL;
1045 }
1046 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001048 dst = NULL;
1049 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001051 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052}
1053
1054static void ip6_link_failure(struct sk_buff *skb)
1055{
1056 struct rt6_info *rt;
1057
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001058 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
Eric Dumazetadf30902009-06-02 05:19:30 +00001060 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001062 if (rt->rt6i_flags & RTF_CACHE)
1063 rt6_update_expires(rt, 0);
1064 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 rt->rt6i_node->fn_sernum = -1;
1066 }
1067}
1068
David S. Miller6700c272012-07-17 03:29:28 -07001069static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
1070 struct sk_buff *skb, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071{
1072 struct rt6_info *rt6 = (struct rt6_info*)dst;
1073
David S. Miller81aded22012-06-15 14:54:11 -07001074 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001076 struct net *net = dev_net(dst->dev);
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 rt6->rt6i_flags |= RTF_MODIFIED;
1079 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001080 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001082 features |= RTAX_FEATURE_ALLFRAG;
1083 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 }
David S. Millerdefb3512010-12-08 21:16:57 -08001085 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001086 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 }
1088}
1089
David S. Miller42ae66c2012-06-15 20:01:57 -07001090void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1091 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001092{
1093 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1094 struct dst_entry *dst;
1095 struct flowi6 fl6;
1096
1097 memset(&fl6, 0, sizeof(fl6));
1098 fl6.flowi6_oif = oif;
1099 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001100 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001101 fl6.daddr = iph->daddr;
1102 fl6.saddr = iph->saddr;
1103 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1104
1105 dst = ip6_route_output(net, NULL, &fl6);
1106 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001107 ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07001108 dst_release(dst);
1109}
1110EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1111
1112void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1113{
1114 ip6_update_pmtu(skb, sock_net(sk), mtu,
1115 sk->sk_bound_dev_if, sk->sk_mark);
1116}
1117EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1118
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001119void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1120{
1121 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1122 struct dst_entry *dst;
1123 struct flowi6 fl6;
1124
1125 memset(&fl6, 0, sizeof(fl6));
1126 fl6.flowi6_oif = oif;
1127 fl6.flowi6_mark = mark;
1128 fl6.flowi6_flags = 0;
1129 fl6.daddr = iph->daddr;
1130 fl6.saddr = iph->saddr;
1131 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1132
1133 dst = ip6_route_output(net, NULL, &fl6);
1134 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001135 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001136 dst_release(dst);
1137}
1138EXPORT_SYMBOL_GPL(ip6_redirect);
1139
1140void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1141{
1142 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1143}
1144EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1145
David S. Miller0dbaee32010-12-13 12:52:14 -08001146static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147{
David S. Miller0dbaee32010-12-13 12:52:14 -08001148 struct net_device *dev = dst->dev;
1149 unsigned int mtu = dst_mtu(dst);
1150 struct net *net = dev_net(dev);
1151
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1153
Daniel Lezcano55786892008-03-04 13:47:47 -08001154 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1155 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001158 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1159 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1160 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 * rely only on pmtu discovery"
1162 */
1163 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1164 mtu = IPV6_MAXPLEN;
1165 return mtu;
1166}
1167
Steffen Klassertebb762f2011-11-23 02:12:51 +00001168static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001169{
David S. Millerd33e4552010-12-14 13:01:14 -08001170 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001171 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1172
1173 if (mtu)
1174 return mtu;
1175
1176 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001177
1178 rcu_read_lock();
1179 idev = __in6_dev_get(dst->dev);
1180 if (idev)
1181 mtu = idev->cnf.mtu6;
1182 rcu_read_unlock();
1183
1184 return mtu;
1185}
1186
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001187static struct dst_entry *icmp6_dst_gc_list;
1188static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001189
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001190struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001192 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193{
David S. Miller87a11572011-12-06 17:04:13 -05001194 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 struct rt6_info *rt;
1196 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001197 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
David S. Miller38308472011-12-03 18:02:47 -05001199 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001200 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201
David S. Miller8b96d222012-06-11 02:01:56 -07001202 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001203 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001205 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 goto out;
1207 }
1208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 if (neigh)
1210 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001211 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001212 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001213 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001214 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001215 dst_free(&rt->dst);
1216 return ERR_CAST(neigh);
1217 }
David S. Miller14deae42009-01-04 16:04:39 -08001218 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001220 rt->dst.flags |= DST_HOST;
1221 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001222 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001223 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001224 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001225 rt->rt6i_dst.plen = 128;
1226 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001227 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001229 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001230 rt->dst.next = icmp6_dst_gc_list;
1231 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001232 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233
Daniel Lezcano55786892008-03-04 13:47:47 -08001234 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
David S. Miller87a11572011-12-06 17:04:13 -05001236 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1237
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238out:
David S. Miller87a11572011-12-06 17:04:13 -05001239 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240}
1241
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001242int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001244 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001245 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001247 spin_lock_bh(&icmp6_dst_lock);
1248 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001249
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 while ((dst = *pprev) != NULL) {
1251 if (!atomic_read(&dst->__refcnt)) {
1252 *pprev = dst->next;
1253 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 } else {
1255 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001256 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 }
1258 }
1259
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001260 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001261
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001262 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263}
1264
David S. Miller1e493d12008-09-10 17:27:15 -07001265static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1266 void *arg)
1267{
1268 struct dst_entry *dst, **pprev;
1269
1270 spin_lock_bh(&icmp6_dst_lock);
1271 pprev = &icmp6_dst_gc_list;
1272 while ((dst = *pprev) != NULL) {
1273 struct rt6_info *rt = (struct rt6_info *) dst;
1274 if (func(rt, arg)) {
1275 *pprev = dst->next;
1276 dst_free(dst);
1277 } else {
1278 pprev = &dst->next;
1279 }
1280 }
1281 spin_unlock_bh(&icmp6_dst_lock);
1282}
1283
Daniel Lezcano569d3642008-01-18 03:56:57 -08001284static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001287 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001288 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1289 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1290 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1291 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1292 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001293 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Eric Dumazetfc66f952010-10-08 06:37:34 +00001295 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001296 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001297 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 goto out;
1299
Benjamin Thery6891a342008-03-04 13:49:47 -08001300 net->ipv6.ip6_rt_gc_expire++;
1301 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1302 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001303 entries = dst_entries_get_slow(ops);
1304 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001305 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001307 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001308 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309}
1310
1311/* Clean host part of a prefix. Not necessary in radix tree,
1312 but results in cleaner routing tables.
1313
1314 Remove it only when all the things will work!
1315 */
1316
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001317int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318{
David S. Miller5170ae82010-12-12 21:35:57 -08001319 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001320 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001321 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001322 struct inet6_dev *idev;
1323
1324 rcu_read_lock();
1325 idev = __in6_dev_get(dev);
1326 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001327 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001328 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001329 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001330 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 }
1332 return hoplimit;
1333}
David S. Millerabbf46a2010-12-12 21:14:46 -08001334EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
1336/*
1337 *
1338 */
1339
Thomas Graf86872cb2006-08-22 00:01:08 -07001340int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
1342 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001343 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 struct rt6_info *rt = NULL;
1345 struct net_device *dev = NULL;
1346 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001347 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 int addr_type;
1349
Thomas Graf86872cb2006-08-22 00:01:08 -07001350 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 return -EINVAL;
1352#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001353 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 return -EINVAL;
1355#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001356 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001358 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 if (!dev)
1360 goto out;
1361 idev = in6_dev_get(dev);
1362 if (!idev)
1363 goto out;
1364 }
1365
Thomas Graf86872cb2006-08-22 00:01:08 -07001366 if (cfg->fc_metric == 0)
1367 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Matti Vaittinend71314b2011-11-14 00:14:49 +00001369 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001370 if (cfg->fc_nlinfo.nlh &&
1371 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001372 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001373 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001374 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001375 table = fib6_new_table(net, cfg->fc_table);
1376 }
1377 } else {
1378 table = fib6_new_table(net, cfg->fc_table);
1379 }
David S. Miller38308472011-12-03 18:02:47 -05001380
1381 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001382 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001383
David S. Miller8b96d222012-06-11 02:01:56 -07001384 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
David S. Miller38308472011-12-03 18:02:47 -05001386 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 err = -ENOMEM;
1388 goto out;
1389 }
1390
Changli Gaod8d1f302010-06-10 23:31:35 -07001391 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001392
1393 if (cfg->fc_flags & RTF_EXPIRES)
1394 rt6_set_expires(rt, jiffies +
1395 clock_t_to_jiffies(cfg->fc_expires));
1396 else
1397 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Thomas Graf86872cb2006-08-22 00:01:08 -07001399 if (cfg->fc_protocol == RTPROT_UNSPEC)
1400 cfg->fc_protocol = RTPROT_BOOT;
1401 rt->rt6i_protocol = cfg->fc_protocol;
1402
1403 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
1405 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001406 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001407 else if (cfg->fc_flags & RTF_LOCAL)
1408 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001410 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411
Changli Gaod8d1f302010-06-10 23:31:35 -07001412 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
Thomas Graf86872cb2006-08-22 00:01:08 -07001414 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1415 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001417 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001419 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1420 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1421 if (!metrics) {
1422 err = -ENOMEM;
1423 goto out;
1424 }
1425 dst_init_metrics(&rt->dst, metrics, 0);
1426 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001428 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1429 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430#endif
1431
Thomas Graf86872cb2006-08-22 00:01:08 -07001432 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
1434 /* We cannot add true routes via loopback here,
1435 they would result in kernel looping; promote them to reject routes
1436 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001437 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001438 (dev && (dev->flags & IFF_LOOPBACK) &&
1439 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1440 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001442 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 if (dev) {
1444 dev_put(dev);
1445 in6_dev_put(idev);
1446 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001447 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 dev_hold(dev);
1449 idev = in6_dev_get(dev);
1450 if (!idev) {
1451 err = -ENODEV;
1452 goto out;
1453 }
1454 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001455 rt->dst.output = ip6_pkt_discard_out;
1456 rt->dst.input = ip6_pkt_discard;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001458 switch (cfg->fc_type) {
1459 case RTN_BLACKHOLE:
1460 rt->dst.error = -EINVAL;
1461 break;
1462 case RTN_PROHIBIT:
1463 rt->dst.error = -EACCES;
1464 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00001465 case RTN_THROW:
1466 rt->dst.error = -EAGAIN;
1467 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001468 default:
1469 rt->dst.error = -ENETUNREACH;
1470 break;
1471 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 goto install_route;
1473 }
1474
Thomas Graf86872cb2006-08-22 00:01:08 -07001475 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001476 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 int gwa_type;
1478
Thomas Graf86872cb2006-08-22 00:01:08 -07001479 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001480 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 gwa_type = ipv6_addr_type(gw_addr);
1482
1483 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1484 struct rt6_info *grt;
1485
1486 /* IPv6 strictly inhibits using not link-local
1487 addresses as nexthop address.
1488 Otherwise, router will not able to send redirects.
1489 It is very good, but in some (rare!) circumstances
1490 (SIT, PtP, NBMA NOARP links) it is handy to allow
1491 some exceptions. --ANK
1492 */
1493 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001494 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 goto out;
1496
Daniel Lezcano55786892008-03-04 13:47:47 -08001497 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498
1499 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001500 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 goto out;
1502 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001503 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001504 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 goto out;
1506 }
1507 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001508 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 idev = grt->rt6i_idev;
1510 dev_hold(dev);
1511 in6_dev_hold(grt->rt6i_idev);
1512 }
David S. Miller38308472011-12-03 18:02:47 -05001513 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001515 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
1517 if (err)
1518 goto out;
1519 }
1520 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001521 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 goto out;
1523 }
1524
1525 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001526 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 goto out;
1528
Daniel Walterc3968a82011-04-13 21:10:57 +00001529 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1530 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1531 err = -EINVAL;
1532 goto out;
1533 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001534 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001535 rt->rt6i_prefsrc.plen = 128;
1536 } else
1537 rt->rt6i_prefsrc.plen = 0;
1538
Thomas Graf86872cb2006-08-22 00:01:08 -07001539 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001540 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001541 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 }
1544
Thomas Graf86872cb2006-08-22 00:01:08 -07001545 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
1547install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001548 if (cfg->fc_mx) {
1549 struct nlattr *nla;
1550 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
Thomas Graf86872cb2006-08-22 00:01:08 -07001552 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001553 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001554
1555 if (type) {
1556 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 err = -EINVAL;
1558 goto out;
1559 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001560
David S. Millerdefb3512010-12-08 21:16:57 -08001561 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 }
1564 }
1565
Changli Gaod8d1f302010-06-10 23:31:35 -07001566 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001568 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001569
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001570 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001571
Thomas Graf86872cb2006-08-22 00:01:08 -07001572 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573
1574out:
1575 if (dev)
1576 dev_put(dev);
1577 if (idev)
1578 in6_dev_put(idev);
1579 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001580 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 return err;
1582}
1583
Thomas Graf86872cb2006-08-22 00:01:08 -07001584static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585{
1586 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001587 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001588 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001590 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001591 return -ENOENT;
1592
Thomas Grafc71099a2006-08-04 23:20:06 -07001593 table = rt->rt6i_table;
1594 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
Thomas Graf86872cb2006-08-22 00:01:08 -07001596 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001597 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
Thomas Grafc71099a2006-08-04 23:20:06 -07001599 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600
1601 return err;
1602}
1603
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001604int ip6_del_rt(struct rt6_info *rt)
1605{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001606 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001607 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001608 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001609 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001610}
1611
Thomas Graf86872cb2006-08-22 00:01:08 -07001612static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613{
Thomas Grafc71099a2006-08-04 23:20:06 -07001614 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 struct fib6_node *fn;
1616 struct rt6_info *rt;
1617 int err = -ESRCH;
1618
Daniel Lezcano55786892008-03-04 13:47:47 -08001619 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001620 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001621 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Thomas Grafc71099a2006-08-04 23:20:06 -07001623 read_lock_bh(&table->tb6_lock);
1624
1625 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001626 &cfg->fc_dst, cfg->fc_dst_len,
1627 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001628
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001630 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001631 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001632 (!rt->dst.dev ||
1633 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001635 if (cfg->fc_flags & RTF_GATEWAY &&
1636 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001638 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001640 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001641 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
Thomas Graf86872cb2006-08-22 00:01:08 -07001643 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 }
1645 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001646 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
1648 return err;
1649}
1650
David S. Miller6700c272012-07-17 03:29:28 -07001651static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001652{
David S. Millere8599ff2012-07-11 23:43:53 -07001653 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001654 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001655 struct rt6_info *rt, *nrt = NULL;
1656 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001657 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001658 const struct in6_addr *dest;
1659 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001660 struct inet6_dev *in6_dev;
1661 struct neighbour *neigh;
1662 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001663 int optlen, on_link;
1664 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001665
1666 optlen = skb->tail - skb->transport_header;
1667 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1668
1669 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001670 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001671 return;
1672 }
1673
1674 icmph = icmp6_hdr(skb);
1675 target = (const struct in6_addr *) (icmph + 1);
1676 dest = target + 1;
1677
1678 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001679 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001680 return;
1681 }
1682
David S. Miller6e157b62012-07-12 00:05:02 -07001683 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001684 if (ipv6_addr_equal(dest, target)) {
1685 on_link = 1;
1686 } else if (ipv6_addr_type(target) !=
1687 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001688 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001689 return;
1690 }
1691
1692 in6_dev = __in6_dev_get(skb->dev);
1693 if (!in6_dev)
1694 return;
1695 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1696 return;
1697
1698 /* RFC2461 8.1:
1699 * The IP source address of the Redirect MUST be the same as the current
1700 * first-hop router for the specified ICMP Destination Address.
1701 */
1702
1703 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1704 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1705 return;
1706 }
David S. Miller6e157b62012-07-12 00:05:02 -07001707
1708 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001709 if (ndopts.nd_opts_tgt_lladdr) {
1710 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1711 skb->dev);
1712 if (!lladdr) {
1713 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1714 return;
1715 }
1716 }
1717
David S. Miller6e157b62012-07-12 00:05:02 -07001718 rt = (struct rt6_info *) dst;
1719 if (rt == net->ipv6.ip6_null_entry) {
1720 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1721 return;
1722 }
1723
1724 /* Redirect received -> path was valid.
1725 * Look, redirects are sent only in response to data packets,
1726 * so that this nexthop apparently is reachable. --ANK
1727 */
1728 dst_confirm(&rt->dst);
1729
David S. Millere8599ff2012-07-11 23:43:53 -07001730 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1731 if (!neigh)
1732 return;
1733
David S. Miller6e157b62012-07-12 00:05:02 -07001734 /* Duplicate redirect: silently ignore. */
1735 old_neigh = rt->n;
1736 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001737 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 /*
1740 * We have finally decided to accept it.
1741 */
1742
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001743 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1745 NEIGH_UPDATE_F_OVERRIDE|
1746 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1747 NEIGH_UPDATE_F_ISROUTER))
1748 );
1749
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001750 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001751 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 goto out;
1753
1754 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1755 if (on_link)
1756 nrt->rt6i_flags &= ~RTF_GATEWAY;
1757
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001758 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001759 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760
Thomas Graf40e22e82006-08-22 00:00:45 -07001761 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 goto out;
1763
Changli Gaod8d1f302010-06-10 23:31:35 -07001764 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001765 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001766 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001767 netevent.new_neigh = neigh;
1768 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001769 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1770
David S. Miller38308472011-12-03 18:02:47 -05001771 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001772 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001773 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774 }
1775
1776out:
David S. Millere8599ff2012-07-11 23:43:53 -07001777 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001778}
1779
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 * Misc support functions
1782 */
1783
Gao feng1716a962012-04-06 00:13:10 +00001784static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001785 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786{
David S. Millerd1918542011-12-28 20:19:20 -05001787 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001788 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1789 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
1791 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001792 rt->dst.input = ort->dst.input;
1793 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001794 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001796 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001797 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001798 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001799 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 rt->rt6i_idev = ort->rt6i_idev;
1801 if (rt->rt6i_idev)
1802 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001803 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001805 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001806 rt->rt6i_flags = ort->rt6i_flags;
1807 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1808 (RTF_DEFAULT | RTF_ADDRCONF))
1809 rt6_set_from(rt, ort);
1810 else
1811 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812 rt->rt6i_metric = 0;
1813
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814#ifdef CONFIG_IPV6_SUBTREES
1815 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1816#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001817 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001818 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 }
1820 return rt;
1821}
1822
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001823#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001824static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001825 const struct in6_addr *prefix, int prefixlen,
1826 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001827{
1828 struct fib6_node *fn;
1829 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001830 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001831
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001832 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001833 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001834 return NULL;
1835
Li RongQing5744dd92012-09-11 21:59:01 +00001836 read_lock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -07001837 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001838 if (!fn)
1839 goto out;
1840
Changli Gaod8d1f302010-06-10 23:31:35 -07001841 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001842 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001843 continue;
1844 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1845 continue;
1846 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1847 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001848 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001849 break;
1850 }
1851out:
Li RongQing5744dd92012-09-11 21:59:01 +00001852 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001853 return rt;
1854}
1855
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001856static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001857 const struct in6_addr *prefix, int prefixlen,
1858 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001859 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001860{
Thomas Graf86872cb2006-08-22 00:01:08 -07001861 struct fib6_config cfg = {
1862 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001863 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001864 .fc_ifindex = ifindex,
1865 .fc_dst_len = prefixlen,
1866 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1867 RTF_UP | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001868 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001869 .fc_nlinfo.nlh = NULL,
1870 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001871 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001872
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001873 cfg.fc_dst = *prefix;
1874 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001875
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001876 /* We should treat it as a default route if prefix length is 0. */
1877 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001878 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001879
Thomas Graf86872cb2006-08-22 00:01:08 -07001880 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001881
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001882 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001883}
1884#endif
1885
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001886struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001887{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001889 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001891 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001892 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001893 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894
Li RongQing5744dd92012-09-11 21:59:01 +00001895 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001896 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001897 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001898 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1900 break;
1901 }
1902 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001903 dst_hold(&rt->dst);
Li RongQing5744dd92012-09-11 21:59:01 +00001904 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 return rt;
1906}
1907
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001908struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001909 struct net_device *dev,
1910 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911{
Thomas Graf86872cb2006-08-22 00:01:08 -07001912 struct fib6_config cfg = {
1913 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001914 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001915 .fc_ifindex = dev->ifindex,
1916 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1917 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001918 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08001919 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001920 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001921 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001923 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924
Thomas Graf86872cb2006-08-22 00:01:08 -07001925 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 return rt6_get_dflt_router(gwaddr, dev);
1928}
1929
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001930void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931{
1932 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001933 struct fib6_table *table;
1934
1935 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001936 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001937 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001938 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939
1940restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001941 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001942 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001944 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001945 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001946 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 goto restart;
1948 }
1949 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001950 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951}
1952
Daniel Lezcano55786892008-03-04 13:47:47 -08001953static void rtmsg_to_fib6_config(struct net *net,
1954 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001955 struct fib6_config *cfg)
1956{
1957 memset(cfg, 0, sizeof(*cfg));
1958
1959 cfg->fc_table = RT6_TABLE_MAIN;
1960 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1961 cfg->fc_metric = rtmsg->rtmsg_metric;
1962 cfg->fc_expires = rtmsg->rtmsg_info;
1963 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1964 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1965 cfg->fc_flags = rtmsg->rtmsg_flags;
1966
Daniel Lezcano55786892008-03-04 13:47:47 -08001967 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001968
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001969 cfg->fc_dst = rtmsg->rtmsg_dst;
1970 cfg->fc_src = rtmsg->rtmsg_src;
1971 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001972}
1973
Daniel Lezcano55786892008-03-04 13:47:47 -08001974int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975{
Thomas Graf86872cb2006-08-22 00:01:08 -07001976 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 struct in6_rtmsg rtmsg;
1978 int err;
1979
1980 switch(cmd) {
1981 case SIOCADDRT: /* Add a route */
1982 case SIOCDELRT: /* Delete a route */
1983 if (!capable(CAP_NET_ADMIN))
1984 return -EPERM;
1985 err = copy_from_user(&rtmsg, arg,
1986 sizeof(struct in6_rtmsg));
1987 if (err)
1988 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001989
Daniel Lezcano55786892008-03-04 13:47:47 -08001990 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001991
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 rtnl_lock();
1993 switch (cmd) {
1994 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001995 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 break;
1997 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001998 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 break;
2000 default:
2001 err = -EINVAL;
2002 }
2003 rtnl_unlock();
2004
2005 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002006 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007
2008 return -EINVAL;
2009}
2010
2011/*
2012 * Drop the packet on the floor
2013 */
2014
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002015static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002017 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002018 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002019 switch (ipstats_mib_noroutes) {
2020 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002021 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002022 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002023 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2024 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002025 break;
2026 }
2027 /* FALLTHROUGH */
2028 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002029 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2030 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002031 break;
2032 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002033 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 kfree_skb(skb);
2035 return 0;
2036}
2037
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002038static int ip6_pkt_discard(struct sk_buff *skb)
2039{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002040 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002041}
2042
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002043static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044{
Eric Dumazetadf30902009-06-02 05:19:30 +00002045 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002046 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047}
2048
David S. Miller6723ab52006-10-18 21:20:57 -07002049#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2050
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002051static int ip6_pkt_prohibit(struct sk_buff *skb)
2052{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002053 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002054}
2055
2056static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2057{
Eric Dumazetadf30902009-06-02 05:19:30 +00002058 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002059 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002060}
2061
David S. Miller6723ab52006-10-18 21:20:57 -07002062#endif
2063
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064/*
2065 * Allocate a dst for local (unicast / anycast) address.
2066 */
2067
2068struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2069 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002070 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002072 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002073 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002074 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075
David S. Miller38308472011-12-03 18:02:47 -05002076 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002077 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002079 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 in6_dev_hold(idev);
2082
David S. Miller11d53b42011-06-24 15:23:34 -07002083 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002084 rt->dst.input = ip6_input;
2085 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002087 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088
2089 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002090 if (anycast)
2091 rt->rt6i_flags |= RTF_ANYCAST;
2092 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002094 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002095 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002096 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002097 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 }
2099
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002100 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002102 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103
Changli Gaod8d1f302010-06-10 23:31:35 -07002104 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105
2106 return rt;
2107}
2108
Daniel Walterc3968a82011-04-13 21:10:57 +00002109int ip6_route_get_saddr(struct net *net,
2110 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002111 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002112 unsigned int prefs,
2113 struct in6_addr *saddr)
2114{
2115 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2116 int err = 0;
2117 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002118 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002119 else
2120 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2121 daddr, prefs, saddr);
2122 return err;
2123}
2124
2125/* remove deleted ip from prefsrc entries */
2126struct arg_dev_net_ip {
2127 struct net_device *dev;
2128 struct net *net;
2129 struct in6_addr *addr;
2130};
2131
2132static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2133{
2134 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2135 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2136 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2137
David S. Millerd1918542011-12-28 20:19:20 -05002138 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002139 rt != net->ipv6.ip6_null_entry &&
2140 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2141 /* remove prefsrc entry */
2142 rt->rt6i_prefsrc.plen = 0;
2143 }
2144 return 0;
2145}
2146
2147void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2148{
2149 struct net *net = dev_net(ifp->idev->dev);
2150 struct arg_dev_net_ip adni = {
2151 .dev = ifp->idev->dev,
2152 .net = net,
2153 .addr = &ifp->addr,
2154 };
2155 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2156}
2157
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002158struct arg_dev_net {
2159 struct net_device *dev;
2160 struct net *net;
2161};
2162
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163static int fib6_ifdown(struct rt6_info *rt, void *arg)
2164{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002165 const struct arg_dev_net *adn = arg;
2166 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002167
David S. Millerd1918542011-12-28 20:19:20 -05002168 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002169 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002171
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172 return 0;
2173}
2174
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002175void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002177 struct arg_dev_net adn = {
2178 .dev = dev,
2179 .net = net,
2180 };
2181
2182 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002183 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184}
2185
Eric Dumazet95c96172012-04-15 05:58:06 +00002186struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002188 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189};
2190
2191static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2192{
2193 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2194 struct inet6_dev *idev;
2195
2196 /* In IPv6 pmtu discovery is not optional,
2197 so that RTAX_MTU lock cannot disable it.
2198 We still use this lock to block changes
2199 caused by addrconf/ndisc.
2200 */
2201
2202 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002203 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204 return 0;
2205
2206 /* For administrative MTU increase, there is no way to discover
2207 IPv6 PMTU increase, so PMTU increase should be updated here.
2208 Since RFC 1981 doesn't include administrative MTU increase
2209 update PMTU increase is a MUST. (i.e. jumbo frame)
2210 */
2211 /*
2212 If new MTU is less than route PMTU, this new MTU will be the
2213 lowest MTU in the path, update the route PMTU to reflect PMTU
2214 decreases; if new MTU is greater than route PMTU, and the
2215 old MTU is the lowest MTU in the path, update the route PMTU
2216 to reflect the increase. In this case if the other nodes' MTU
2217 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2218 PMTU discouvery.
2219 */
David S. Millerd1918542011-12-28 20:19:20 -05002220 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002221 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2222 (dst_mtu(&rt->dst) >= arg->mtu ||
2223 (dst_mtu(&rt->dst) < arg->mtu &&
2224 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002225 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002226 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 return 0;
2228}
2229
Eric Dumazet95c96172012-04-15 05:58:06 +00002230void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231{
Thomas Grafc71099a2006-08-04 23:20:06 -07002232 struct rt6_mtu_change_arg arg = {
2233 .dev = dev,
2234 .mtu = mtu,
2235 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002237 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238}
2239
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002240static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002241 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002242 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002243 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002244 [RTA_PRIORITY] = { .type = NLA_U32 },
2245 [RTA_METRICS] = { .type = NLA_NESTED },
2246};
2247
2248static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2249 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250{
Thomas Graf86872cb2006-08-22 00:01:08 -07002251 struct rtmsg *rtm;
2252 struct nlattr *tb[RTA_MAX+1];
2253 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254
Thomas Graf86872cb2006-08-22 00:01:08 -07002255 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2256 if (err < 0)
2257 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258
Thomas Graf86872cb2006-08-22 00:01:08 -07002259 err = -EINVAL;
2260 rtm = nlmsg_data(nlh);
2261 memset(cfg, 0, sizeof(*cfg));
2262
2263 cfg->fc_table = rtm->rtm_table;
2264 cfg->fc_dst_len = rtm->rtm_dst_len;
2265 cfg->fc_src_len = rtm->rtm_src_len;
2266 cfg->fc_flags = RTF_UP;
2267 cfg->fc_protocol = rtm->rtm_protocol;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002268 cfg->fc_type = rtm->rtm_type;
Thomas Graf86872cb2006-08-22 00:01:08 -07002269
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002270 if (rtm->rtm_type == RTN_UNREACHABLE ||
2271 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002272 rtm->rtm_type == RTN_PROHIBIT ||
2273 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07002274 cfg->fc_flags |= RTF_REJECT;
2275
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002276 if (rtm->rtm_type == RTN_LOCAL)
2277 cfg->fc_flags |= RTF_LOCAL;
2278
Eric W. Biederman15e47302012-09-07 20:12:54 +00002279 cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
Thomas Graf86872cb2006-08-22 00:01:08 -07002280 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002281 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002282
2283 if (tb[RTA_GATEWAY]) {
2284 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2285 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002287
2288 if (tb[RTA_DST]) {
2289 int plen = (rtm->rtm_dst_len + 7) >> 3;
2290
2291 if (nla_len(tb[RTA_DST]) < plen)
2292 goto errout;
2293
2294 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002296
2297 if (tb[RTA_SRC]) {
2298 int plen = (rtm->rtm_src_len + 7) >> 3;
2299
2300 if (nla_len(tb[RTA_SRC]) < plen)
2301 goto errout;
2302
2303 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002305
Daniel Walterc3968a82011-04-13 21:10:57 +00002306 if (tb[RTA_PREFSRC])
2307 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2308
Thomas Graf86872cb2006-08-22 00:01:08 -07002309 if (tb[RTA_OIF])
2310 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2311
2312 if (tb[RTA_PRIORITY])
2313 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2314
2315 if (tb[RTA_METRICS]) {
2316 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2317 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002319
2320 if (tb[RTA_TABLE])
2321 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2322
2323 err = 0;
2324errout:
2325 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326}
2327
Thomas Grafc127ea22007-03-22 11:58:32 -07002328static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329{
Thomas Graf86872cb2006-08-22 00:01:08 -07002330 struct fib6_config cfg;
2331 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332
Thomas Graf86872cb2006-08-22 00:01:08 -07002333 err = rtm_to_fib6_config(skb, nlh, &cfg);
2334 if (err < 0)
2335 return err;
2336
2337 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338}
2339
Thomas Grafc127ea22007-03-22 11:58:32 -07002340static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341{
Thomas Graf86872cb2006-08-22 00:01:08 -07002342 struct fib6_config cfg;
2343 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344
Thomas Graf86872cb2006-08-22 00:01:08 -07002345 err = rtm_to_fib6_config(skb, nlh, &cfg);
2346 if (err < 0)
2347 return err;
2348
2349 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350}
2351
Thomas Graf339bf982006-11-10 14:10:15 -08002352static inline size_t rt6_nlmsg_size(void)
2353{
2354 return NLMSG_ALIGN(sizeof(struct rtmsg))
2355 + nla_total_size(16) /* RTA_SRC */
2356 + nla_total_size(16) /* RTA_DST */
2357 + nla_total_size(16) /* RTA_GATEWAY */
2358 + nla_total_size(16) /* RTA_PREFSRC */
2359 + nla_total_size(4) /* RTA_TABLE */
2360 + nla_total_size(4) /* RTA_IIF */
2361 + nla_total_size(4) /* RTA_OIF */
2362 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002363 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002364 + nla_total_size(sizeof(struct rta_cacheinfo));
2365}
2366
Brian Haley191cd582008-08-14 15:33:21 -07002367static int rt6_fill_node(struct net *net,
2368 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002369 struct in6_addr *dst, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002370 int iif, int type, u32 portid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002371 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372{
2373 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002374 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002375 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002376 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002377 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378
2379 if (prefix) { /* user wants prefix routes only */
2380 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2381 /* success since this is not a prefix route */
2382 return 1;
2383 }
2384 }
2385
Eric W. Biederman15e47302012-09-07 20:12:54 +00002386 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002387 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002388 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002389
2390 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002391 rtm->rtm_family = AF_INET6;
2392 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2393 rtm->rtm_src_len = rt->rt6i_src.plen;
2394 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002395 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002396 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002397 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002398 table = RT6_TABLE_UNSPEC;
2399 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002400 if (nla_put_u32(skb, RTA_TABLE, table))
2401 goto nla_put_failure;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002402 if (rt->rt6i_flags & RTF_REJECT) {
2403 switch (rt->dst.error) {
2404 case -EINVAL:
2405 rtm->rtm_type = RTN_BLACKHOLE;
2406 break;
2407 case -EACCES:
2408 rtm->rtm_type = RTN_PROHIBIT;
2409 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002410 case -EAGAIN:
2411 rtm->rtm_type = RTN_THROW;
2412 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002413 default:
2414 rtm->rtm_type = RTN_UNREACHABLE;
2415 break;
2416 }
2417 }
David S. Miller38308472011-12-03 18:02:47 -05002418 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002419 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002420 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 rtm->rtm_type = RTN_LOCAL;
2422 else
2423 rtm->rtm_type = RTN_UNICAST;
2424 rtm->rtm_flags = 0;
2425 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2426 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002427 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 rtm->rtm_protocol = RTPROT_REDIRECT;
Denis Ovsienkof0396f602012-07-10 04:45:50 +00002429 else if (rt->rt6i_flags & RTF_ADDRCONF) {
2430 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
2431 rtm->rtm_protocol = RTPROT_RA;
2432 else
2433 rtm->rtm_protocol = RTPROT_KERNEL;
2434 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435
David S. Miller38308472011-12-03 18:02:47 -05002436 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437 rtm->rtm_flags |= RTM_F_CLONED;
2438
2439 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002440 if (nla_put(skb, RTA_DST, 16, dst))
2441 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002442 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002444 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2445 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446#ifdef CONFIG_IPV6_SUBTREES
2447 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002448 if (nla_put(skb, RTA_SRC, 16, src))
2449 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002450 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002451 } else if (rtm->rtm_src_len &&
2452 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2453 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002455 if (iif) {
2456#ifdef CONFIG_IPV6_MROUTE
2457 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002458 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002459 if (err <= 0) {
2460 if (!nowait) {
2461 if (err == 0)
2462 return 0;
2463 goto nla_put_failure;
2464 } else {
2465 if (err == -EMSGSIZE)
2466 goto nla_put_failure;
2467 }
2468 }
2469 } else
2470#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002471 if (nla_put_u32(skb, RTA_IIF, iif))
2472 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002473 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002475 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2476 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2477 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002479
Daniel Walterc3968a82011-04-13 21:10:57 +00002480 if (rt->rt6i_prefsrc.plen) {
2481 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002482 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002483 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2484 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002485 }
2486
David S. Millerdefb3512010-12-08 21:16:57 -08002487 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002488 goto nla_put_failure;
2489
David S. Miller97cac082012-07-02 22:43:47 -07002490 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002491 if (n) {
Amerigo Wangfdd66812012-09-10 02:48:44 +00002492 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0)
Eric Dumazet94f826b2012-03-27 09:53:52 +00002493 goto nla_put_failure;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002494 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002495
David S. Millerc78679e2012-04-01 20:27:33 -04002496 if (rt->dst.dev &&
2497 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2498 goto nla_put_failure;
2499 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2500 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00002501
2502 expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002503
David S. Miller87a50692012-07-10 05:06:14 -07002504 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002505 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506
Thomas Graf2d7202b2006-08-22 00:01:27 -07002507 return nlmsg_end(skb, nlh);
2508
2509nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002510 nlmsg_cancel(skb, nlh);
2511 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512}
2513
Patrick McHardy1b43af52006-08-10 23:11:17 -07002514int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515{
2516 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2517 int prefix;
2518
Thomas Graf2d7202b2006-08-22 00:01:27 -07002519 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2520 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2522 } else
2523 prefix = 0;
2524
Brian Haley191cd582008-08-14 15:33:21 -07002525 return rt6_fill_node(arg->net,
2526 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002527 NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002528 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529}
2530
Thomas Grafc127ea22007-03-22 11:58:32 -07002531static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002533 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002534 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002536 struct sk_buff *skb;
2537 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002538 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002539 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002540
2541 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2542 if (err < 0)
2543 goto errout;
2544
2545 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002546 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002547
2548 if (tb[RTA_SRC]) {
2549 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2550 goto errout;
2551
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002552 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002553 }
2554
2555 if (tb[RTA_DST]) {
2556 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2557 goto errout;
2558
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002559 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002560 }
2561
2562 if (tb[RTA_IIF])
2563 iif = nla_get_u32(tb[RTA_IIF]);
2564
2565 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002566 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002567
2568 if (iif) {
2569 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002570 int flags = 0;
2571
Daniel Lezcano55786892008-03-04 13:47:47 -08002572 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002573 if (!dev) {
2574 err = -ENODEV;
2575 goto errout;
2576 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002577
2578 fl6.flowi6_iif = iif;
2579
2580 if (!ipv6_addr_any(&fl6.saddr))
2581 flags |= RT6_LOOKUP_F_HAS_SADDR;
2582
2583 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2584 flags);
2585 } else {
2586 fl6.flowi6_oif = oif;
2587
2588 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002589 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590
2591 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002592 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002593 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002594 err = -ENOBUFS;
2595 goto errout;
2596 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597
2598 /* Reserve room for dummy headers, this skb can pass
2599 through good chunk of routing engine.
2600 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002601 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2603
Changli Gaod8d1f302010-06-10 23:31:35 -07002604 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605
David S. Miller4c9483b2011-03-12 16:22:43 -05002606 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002607 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002608 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002610 kfree_skb(skb);
2611 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612 }
2613
Eric W. Biederman15e47302012-09-07 20:12:54 +00002614 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07002615errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617}
2618
Thomas Graf86872cb2006-08-22 00:01:08 -07002619void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620{
2621 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002622 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002623 u32 seq;
2624 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002626 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002627 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002628
Thomas Graf339bf982006-11-10 14:10:15 -08002629 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002630 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002631 goto errout;
2632
Brian Haley191cd582008-08-14 15:33:21 -07002633 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002634 event, info->portid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002635 if (err < 0) {
2636 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2637 WARN_ON(err == -EMSGSIZE);
2638 kfree_skb(skb);
2639 goto errout;
2640 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00002641 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002642 info->nlh, gfp_any());
2643 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002644errout:
2645 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002646 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647}
2648
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002649static int ip6_route_dev_notify(struct notifier_block *this,
2650 unsigned long event, void *data)
2651{
2652 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002653 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002654
2655 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002656 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002657 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2658#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002659 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002660 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002661 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002662 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2663#endif
2664 }
2665
2666 return NOTIFY_OK;
2667}
2668
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669/*
2670 * /proc
2671 */
2672
2673#ifdef CONFIG_PROC_FS
2674
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675struct rt6_proc_arg
2676{
2677 char *buffer;
2678 int offset;
2679 int length;
2680 int skip;
2681 int len;
2682};
2683
2684static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2685{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002686 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002687 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002689 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002692 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002694 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695#endif
David S. Miller97cac082012-07-02 22:43:47 -07002696 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002697 if (n) {
2698 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002700 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 }
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002702 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002703 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2704 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002705 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 return 0;
2707}
2708
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002709static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002711 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002712 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002713 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714}
2715
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002716static int ipv6_route_open(struct inode *inode, struct file *file)
2717{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002718 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002719}
2720
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002721static const struct file_operations ipv6_route_proc_fops = {
2722 .owner = THIS_MODULE,
2723 .open = ipv6_route_open,
2724 .read = seq_read,
2725 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002726 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002727};
2728
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2730{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002731 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002733 net->ipv6.rt6_stats->fib_nodes,
2734 net->ipv6.rt6_stats->fib_route_nodes,
2735 net->ipv6.rt6_stats->fib_rt_alloc,
2736 net->ipv6.rt6_stats->fib_rt_entries,
2737 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002738 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002739 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740
2741 return 0;
2742}
2743
2744static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2745{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002746 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002747}
2748
Arjan van de Ven9a321442007-02-12 00:55:35 -08002749static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 .owner = THIS_MODULE,
2751 .open = rt6_stats_seq_open,
2752 .read = seq_read,
2753 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002754 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755};
2756#endif /* CONFIG_PROC_FS */
2757
2758#ifdef CONFIG_SYSCTL
2759
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002761int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 void __user *buffer, size_t *lenp, loff_t *ppos)
2763{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002764 struct net *net;
2765 int delay;
2766 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002768
2769 net = (struct net *)ctl->extra1;
2770 delay = net->ipv6.sysctl.flush_delay;
2771 proc_dointvec(ctl, write, buffer, lenp, ppos);
2772 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2773 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774}
2775
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002776ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002777 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002779 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002781 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002782 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 },
2784 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002786 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 .maxlen = sizeof(int),
2788 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002789 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 },
2791 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002793 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 .maxlen = sizeof(int),
2795 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002796 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 },
2798 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002800 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 .maxlen = sizeof(int),
2802 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002803 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 },
2805 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002807 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 .maxlen = sizeof(int),
2809 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002810 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 },
2812 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002814 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 .maxlen = sizeof(int),
2816 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002817 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818 },
2819 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002821 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 .maxlen = sizeof(int),
2823 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002824 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 },
2826 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002828 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 .maxlen = sizeof(int),
2830 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002831 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 },
2833 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002835 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 .maxlen = sizeof(int),
2837 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002838 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 },
2840 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002842 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 .maxlen = sizeof(int),
2844 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002845 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002847 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848};
2849
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002850struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002851{
2852 struct ctl_table *table;
2853
2854 table = kmemdup(ipv6_route_table_template,
2855 sizeof(ipv6_route_table_template),
2856 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002857
2858 if (table) {
2859 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002860 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002861 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002862 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2863 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2864 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2865 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2866 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2867 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2868 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002869 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002870 }
2871
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002872 return table;
2873}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874#endif
2875
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002876static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002877{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002878 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002879
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002880 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2881 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002882
Eric Dumazetfc66f952010-10-08 06:37:34 +00002883 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2884 goto out_ip6_dst_ops;
2885
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002886 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2887 sizeof(*net->ipv6.ip6_null_entry),
2888 GFP_KERNEL);
2889 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002890 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002891 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002892 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002893 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002894 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2895 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002896
2897#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2898 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2899 sizeof(*net->ipv6.ip6_prohibit_entry),
2900 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002901 if (!net->ipv6.ip6_prohibit_entry)
2902 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002903 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002904 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002905 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002906 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2907 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002908
2909 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2910 sizeof(*net->ipv6.ip6_blk_hole_entry),
2911 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002912 if (!net->ipv6.ip6_blk_hole_entry)
2913 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002914 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002915 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002916 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002917 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2918 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002919#endif
2920
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002921 net->ipv6.sysctl.flush_delay = 0;
2922 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2923 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2924 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2925 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2926 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2927 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2928 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2929
Benjamin Thery6891a342008-03-04 13:49:47 -08002930 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2931
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002932 ret = 0;
2933out:
2934 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002935
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002936#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2937out_ip6_prohibit_entry:
2938 kfree(net->ipv6.ip6_prohibit_entry);
2939out_ip6_null_entry:
2940 kfree(net->ipv6.ip6_null_entry);
2941#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002942out_ip6_dst_entries:
2943 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002944out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002945 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002946}
2947
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002948static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002949{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002950 kfree(net->ipv6.ip6_null_entry);
2951#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2952 kfree(net->ipv6.ip6_prohibit_entry);
2953 kfree(net->ipv6.ip6_blk_hole_entry);
2954#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002955 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002956}
2957
Thomas Grafd1896342012-06-18 12:08:33 +00002958static int __net_init ip6_route_net_init_late(struct net *net)
2959{
2960#ifdef CONFIG_PROC_FS
2961 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2962 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2963#endif
2964 return 0;
2965}
2966
2967static void __net_exit ip6_route_net_exit_late(struct net *net)
2968{
2969#ifdef CONFIG_PROC_FS
2970 proc_net_remove(net, "ipv6_route");
2971 proc_net_remove(net, "rt6_stats");
2972#endif
2973}
2974
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002975static struct pernet_operations ip6_route_net_ops = {
2976 .init = ip6_route_net_init,
2977 .exit = ip6_route_net_exit,
2978};
2979
David S. Millerc3426b42012-06-09 16:27:05 -07002980static int __net_init ipv6_inetpeer_init(struct net *net)
2981{
2982 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2983
2984 if (!bp)
2985 return -ENOMEM;
2986 inet_peer_base_init(bp);
2987 net->ipv6.peers = bp;
2988 return 0;
2989}
2990
2991static void __net_exit ipv6_inetpeer_exit(struct net *net)
2992{
2993 struct inet_peer_base *bp = net->ipv6.peers;
2994
2995 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07002996 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07002997 kfree(bp);
2998}
2999
David S. Miller2b823f72012-06-09 19:00:16 -07003000static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003001 .init = ipv6_inetpeer_init,
3002 .exit = ipv6_inetpeer_exit,
3003};
3004
Thomas Grafd1896342012-06-18 12:08:33 +00003005static struct pernet_operations ip6_route_net_late_ops = {
3006 .init = ip6_route_net_init_late,
3007 .exit = ip6_route_net_exit_late,
3008};
3009
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003010static struct notifier_block ip6_route_dev_notifier = {
3011 .notifier_call = ip6_route_dev_notify,
3012 .priority = 0,
3013};
3014
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003015int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003016{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003017 int ret;
3018
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003019 ret = -ENOMEM;
3020 ip6_dst_ops_template.kmem_cachep =
3021 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3022 SLAB_HWCACHE_ALIGN, NULL);
3023 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003024 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003025
Eric Dumazetfc66f952010-10-08 06:37:34 +00003026 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003027 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003028 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003029
David S. Millerc3426b42012-06-09 16:27:05 -07003030 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3031 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003032 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003033
David S. Miller7e52b332012-06-15 15:51:55 -07003034 ret = register_pernet_subsys(&ip6_route_net_ops);
3035 if (ret)
3036 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003037
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003038 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3039
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003040 /* Registering of the loopback is done before this portion of code,
3041 * the loopback reference in rt6_info will not be taken, do it
3042 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003043 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003044 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3045 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003046 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003047 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003048 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003049 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3050 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003051 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003052 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003053 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003054
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003055 ret = xfrm6_init();
3056 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003057 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003058
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003059 ret = fib6_rules_init();
3060 if (ret)
3061 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003062
Thomas Grafd1896342012-06-18 12:08:33 +00003063 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3064 if (ret)
3065 goto fib6_rules_init;
3066
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003067 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003068 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3069 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3070 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003071 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003072
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003073 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003074 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003075 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003076
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003077out:
3078 return ret;
3079
Thomas Grafd1896342012-06-18 12:08:33 +00003080out_register_late_subsys:
3081 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003082fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003083 fib6_rules_cleanup();
3084xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003085 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003086out_fib6_init:
3087 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003088out_register_subsys:
3089 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003090out_register_inetpeer:
3091 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003092out_dst_entries:
3093 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003094out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003095 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003096 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097}
3098
3099void ip6_route_cleanup(void)
3100{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003101 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003102 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003103 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003105 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003106 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003107 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003108 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003109 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003110}