blob: b1e6cf0b95fd9bf3546420e8cb48578d1a7d2190 [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] = {
Li RongQing14edd872012-10-24 14:01:18 +0800222 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800223};
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,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000229 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700230 .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,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000249 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700250 .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,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000264 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700265 .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,
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +0000284 0, DST_OBSOLETE_FORCE_CHK, 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);
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +0000291 rt->rt6i_genid = rt_genid(net);
David S. Miller97bab732012-06-09 22:36:36 -0700292 }
David S. Millercf911662011-04-28 14:31:47 -0700293 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294}
295
296static void ip6_dst_destroy(struct dst_entry *dst)
297{
298 struct rt6_info *rt = (struct rt6_info *)dst;
299 struct inet6_dev *idev = rt->rt6i_idev;
300
David S. Miller97cac082012-07-02 22:43:47 -0700301 if (rt->n)
302 neigh_release(rt->n);
303
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000304 if (!(rt->dst.flags & DST_HOST))
305 dst_destroy_metrics_generic(dst);
306
David S. Miller38308472011-12-03 18:02:47 -0500307 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 rt->rt6i_idev = NULL;
309 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900310 }
Gao feng1716a962012-04-06 00:13:10 +0000311
312 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
313 dst_release(dst->from);
314
David S. Miller97bab732012-06-09 22:36:36 -0700315 if (rt6_has_peer(rt)) {
316 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800317 inet_putpeer(peer);
318 }
319}
320
David S. Miller6431cbc2011-02-07 20:38:06 -0800321static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
322
323static u32 rt6_peer_genid(void)
324{
325 return atomic_read(&__rt6_peer_genid);
326}
327
David S. Millerb3419362010-11-30 12:27:11 -0800328void rt6_bind_peer(struct rt6_info *rt, int create)
329{
David S. Miller97bab732012-06-09 22:36:36 -0700330 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800331 struct inet_peer *peer;
332
David S. Miller97bab732012-06-09 22:36:36 -0700333 base = inetpeer_base_ptr(rt->_rt6i_peer);
334 if (!base)
335 return;
336
337 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700338 if (peer) {
339 if (!rt6_set_peer(rt, peer))
340 inet_putpeer(peer);
341 else
342 rt->rt6i_peer_genid = rt6_peer_genid();
343 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344}
345
346static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
347 int how)
348{
349 struct rt6_info *rt = (struct rt6_info *)dst;
350 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800351 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900352 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
David S. Miller97cac082012-07-02 22:43:47 -0700354 if (dev != loopback_dev) {
355 if (idev && idev->dev == dev) {
356 struct inet6_dev *loopback_idev =
357 in6_dev_get(loopback_dev);
358 if (loopback_idev) {
359 rt->rt6i_idev = loopback_idev;
360 in6_dev_put(idev);
361 }
362 }
363 if (rt->n && rt->n->dev == dev) {
364 rt->n->dev = loopback_dev;
365 dev_hold(loopback_dev);
366 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
368 }
369}
370
Eric Dumazeta50feda2012-05-18 18:57:34 +0000371static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372{
Gao feng1716a962012-04-06 00:13:10 +0000373 if (rt->rt6i_flags & RTF_EXPIRES) {
374 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000375 return true;
Gao feng1716a962012-04-06 00:13:10 +0000376 } else if (rt->dst.from) {
Li RongQing3fd91fb2012-09-13 19:54:57 +0000377 return rt6_check_expired((struct rt6_info *) rt->dst.from);
Gao feng1716a962012-04-06 00:13:10 +0000378 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000379 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380}
381
Eric Dumazeta50feda2012-05-18 18:57:34 +0000382static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700383{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000384 return ipv6_addr_type(daddr) &
385 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700386}
387
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700389 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 */
391
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800392static inline struct rt6_info *rt6_device_match(struct net *net,
393 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000394 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700396 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397{
398 struct rt6_info *local = NULL;
399 struct rt6_info *sprt;
400
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900401 if (!oif && ipv6_addr_any(saddr))
402 goto out;
403
Changli Gaod8d1f302010-06-10 23:31:35 -0700404 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500405 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900406
407 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 if (dev->ifindex == oif)
409 return sprt;
410 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500411 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700413 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900415 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 local->rt6i_idev->dev->ifindex == oif))
417 continue;
418 }
419 local = sprt;
420 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900421 } else {
422 if (ipv6_chk_addr(net, saddr, dev,
423 flags & RT6_LOOKUP_F_IFACE))
424 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900426 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900428 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 if (local)
430 return local;
431
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700432 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800433 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900435out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 return rt;
437}
438
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800439#ifdef CONFIG_IPV6_ROUTER_PREF
440static void rt6_probe(struct rt6_info *rt)
441{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000442 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800443 /*
444 * Okay, this does not seem to be appropriate
445 * for now, however, we need to check if it
446 * is really so; aka Router Reachability Probing.
447 *
448 * Router Reachability Probe MUST be rate-limited
449 * to no more than one per minute.
450 */
David S. Miller97cac082012-07-02 22:43:47 -0700451 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800452 if (!neigh || (neigh->nud_state & NUD_VALID))
Amerigo Wangfdd66812012-09-10 02:48:44 +0000453 return;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800454 read_lock_bh(&neigh->lock);
455 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e163562006-03-20 17:05:47 -0800456 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800457 struct in6_addr mcaddr;
458 struct in6_addr *target;
459
460 neigh->updated = jiffies;
461 read_unlock_bh(&neigh->lock);
462
463 target = (struct in6_addr *)&neigh->primary_key;
464 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500465 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000466 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800467 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000468 }
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800469}
470#else
471static inline void rt6_probe(struct rt6_info *rt)
472{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800473}
474#endif
475
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800477 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700479static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
David S. Millerd1918542011-12-28 20:19:20 -0500481 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700482 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800483 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700484 if ((dev->flags & IFF_LOOPBACK) &&
485 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
486 return 1;
487 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489
Dave Jonesb6f99a22007-03-22 12:27:49 -0700490static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000492 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800493 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000494
David S. Miller97cac082012-07-02 22:43:47 -0700495 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700496 if (rt->rt6i_flags & RTF_NONEXTHOP ||
497 !(rt->rt6i_flags & RTF_GATEWAY))
498 m = 1;
499 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800500 read_lock_bh(&neigh->lock);
501 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700502 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800503#ifdef CONFIG_IPV6_ROUTER_PREF
504 else if (neigh->nud_state & NUD_FAILED)
505 m = 0;
506#endif
507 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800508 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800509 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800510 } else
511 m = 0;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800512 return m;
513}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800515static int rt6_score_route(struct rt6_info *rt, int oif,
516 int strict)
517{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700518 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900519
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700520 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700521 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800522 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800523#ifdef CONFIG_IPV6_ROUTER_PREF
524 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
525#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700526 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800527 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800528 return -1;
529 return m;
530}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
David S. Millerf11e6652007-03-24 20:36:25 -0700532static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
533 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800534{
David S. Millerf11e6652007-03-24 20:36:25 -0700535 int m;
536
537 if (rt6_check_expired(rt))
538 goto out;
539
540 m = rt6_score_route(rt, oif, strict);
541 if (m < 0)
542 goto out;
543
544 if (m > *mpri) {
545 if (strict & RT6_LOOKUP_F_REACHABLE)
546 rt6_probe(match);
547 *mpri = m;
548 match = rt;
549 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
550 rt6_probe(rt);
551 }
552
553out:
554 return match;
555}
556
557static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
558 struct rt6_info *rr_head,
559 u32 metric, int oif, int strict)
560{
561 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800562 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
David S. Millerf11e6652007-03-24 20:36:25 -0700564 match = NULL;
565 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700566 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700567 match = find_match(rt, oif, strict, &mpri, match);
568 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700569 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700570 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800571
David S. Millerf11e6652007-03-24 20:36:25 -0700572 return match;
573}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800574
David S. Millerf11e6652007-03-24 20:36:25 -0700575static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
576{
577 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800578 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
David S. Millerf11e6652007-03-24 20:36:25 -0700580 rt0 = fn->rr_ptr;
581 if (!rt0)
582 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
David S. Millerf11e6652007-03-24 20:36:25 -0700584 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800586 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700587 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700588 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700589
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800590 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700591 if (!next || next->rt6i_metric != rt0->rt6i_metric)
592 next = fn->leaf;
593
594 if (next != rt0)
595 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 }
597
David S. Millerd1918542011-12-28 20:19:20 -0500598 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000599 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600}
601
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800602#ifdef CONFIG_IPV6_ROUTE_INFO
603int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000604 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800605{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900606 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800607 struct route_info *rinfo = (struct route_info *) opt;
608 struct in6_addr prefix_buf, *prefix;
609 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900610 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800611 struct rt6_info *rt;
612
613 if (len < sizeof(struct route_info)) {
614 return -EINVAL;
615 }
616
617 /* Sanity check for prefix_len and length */
618 if (rinfo->length > 3) {
619 return -EINVAL;
620 } else if (rinfo->prefix_len > 128) {
621 return -EINVAL;
622 } else if (rinfo->prefix_len > 64) {
623 if (rinfo->length < 2) {
624 return -EINVAL;
625 }
626 } else if (rinfo->prefix_len > 0) {
627 if (rinfo->length < 1) {
628 return -EINVAL;
629 }
630 }
631
632 pref = rinfo->route_pref;
633 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000634 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800635
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900636 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800637
638 if (rinfo->length == 3)
639 prefix = (struct in6_addr *)rinfo->prefix;
640 else {
641 /* this function is safe */
642 ipv6_addr_prefix(&prefix_buf,
643 (struct in6_addr *)rinfo->prefix,
644 rinfo->prefix_len);
645 prefix = &prefix_buf;
646 }
647
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800648 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
649 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800650
651 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700652 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800653 rt = NULL;
654 }
655
656 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800657 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800658 pref);
659 else if (rt)
660 rt->rt6i_flags = RTF_ROUTEINFO |
661 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
662
663 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000664 if (!addrconf_finite_timeout(lifetime))
665 rt6_clean_expires(rt);
666 else
667 rt6_set_expires(rt, jiffies + HZ * lifetime);
668
Changli Gaod8d1f302010-06-10 23:31:35 -0700669 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800670 }
671 return 0;
672}
673#endif
674
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800675#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700676do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800677 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700678 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700679 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700680 if (fn->fn_flags & RTN_TL_ROOT) \
681 goto out; \
682 pn = fn->parent; \
683 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800684 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700685 else \
686 fn = pn; \
687 if (fn->fn_flags & RTN_RTINFO) \
688 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700689 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700690 } \
David S. Miller38308472011-12-03 18:02:47 -0500691} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700692
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800693static struct rt6_info *ip6_pol_route_lookup(struct net *net,
694 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500695 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696{
697 struct fib6_node *fn;
698 struct rt6_info *rt;
699
Thomas Grafc71099a2006-08-04 23:20:06 -0700700 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500701 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700702restart:
703 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500704 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
705 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700706out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700707 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700708 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700709 return rt;
710
711}
712
Florian Westphalea6e5742011-09-05 16:05:44 +0200713struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
714 int flags)
715{
716 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
717}
718EXPORT_SYMBOL_GPL(ip6_route_lookup);
719
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900720struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
721 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700722{
David S. Miller4c9483b2011-03-12 16:22:43 -0500723 struct flowi6 fl6 = {
724 .flowi6_oif = oif,
725 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700726 };
727 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700728 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700729
Thomas Grafadaa70b2006-10-13 15:01:03 -0700730 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500731 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700732 flags |= RT6_LOOKUP_F_HAS_SADDR;
733 }
734
David S. Miller4c9483b2011-03-12 16:22:43 -0500735 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700736 if (dst->error == 0)
737 return (struct rt6_info *) dst;
738
739 dst_release(dst);
740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 return NULL;
742}
743
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900744EXPORT_SYMBOL(rt6_lookup);
745
Thomas Grafc71099a2006-08-04 23:20:06 -0700746/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 It takes new route entry, the addition fails by any reason the
748 route is freed. In any case, if caller does not hold it, it may
749 be destroyed.
750 */
751
Thomas Graf86872cb2006-08-22 00:01:08 -0700752static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753{
754 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700755 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Thomas Grafc71099a2006-08-04 23:20:06 -0700757 table = rt->rt6i_table;
758 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700759 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700760 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
762 return err;
763}
764
Thomas Graf40e22e82006-08-22 00:00:45 -0700765int ip6_ins_rt(struct rt6_info *rt)
766{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800767 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500768 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800769 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800770 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700771}
772
Gao feng1716a962012-04-06 00:13:10 +0000773static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000774 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000775 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 struct rt6_info *rt;
778
779 /*
780 * Clone the route.
781 */
782
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000783 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
785 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800786 int attempts = !in_softirq();
787
David S. Miller38308472011-12-03 18:02:47 -0500788 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500789 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000790 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900791 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000792 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900793 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797#ifdef CONFIG_IPV6_SUBTREES
798 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000799 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 rt->rt6i_src.plen = 128;
801 }
802#endif
803
David S. Miller14deae42009-01-04 16:04:39 -0800804 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500805 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500806 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800807 int saved_rt_min_interval =
808 net->ipv6.sysctl.ip6_rt_gc_min_interval;
809 int saved_rt_elasticity =
810 net->ipv6.sysctl.ip6_rt_gc_elasticity;
811
812 if (attempts-- > 0) {
813 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
814 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
815
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000816 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800817
818 net->ipv6.sysctl.ip6_rt_gc_elasticity =
819 saved_rt_elasticity;
820 net->ipv6.sysctl.ip6_rt_gc_min_interval =
821 saved_rt_min_interval;
822 goto retry;
823 }
824
Joe Perchesf3213832012-05-15 14:11:53 +0000825 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700826 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800827 return NULL;
828 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800829 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800831 return rt;
832}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000834static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
835 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800836{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000837 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
838
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800839 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800840 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700841 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800842 }
843 return rt;
844}
845
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800846static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500847 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848{
849 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800850 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700851 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800853 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700854 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700856 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700859 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800861restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500862 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
864restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700865 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800866
David S. Miller4c9483b2011-03-12 16:22:43 -0500867 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800868 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800869 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800870 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
Changli Gaod8d1f302010-06-10 23:31:35 -0700872 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700873 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800874
David S. Miller97cac082012-07-02 22:43:47 -0700875 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500876 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800877 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500878 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800879 else
880 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800881
Changli Gaod8d1f302010-06-10 23:31:35 -0700882 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800883 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800884
Changli Gaod8d1f302010-06-10 23:31:35 -0700885 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800886 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700887 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800888 if (!err)
889 goto out2;
890 }
891
892 if (--attempts <= 0)
893 goto out2;
894
895 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700896 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800897 * released someone could insert this route. Relookup.
898 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700899 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800900 goto relookup;
901
902out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800903 if (reachable) {
904 reachable = 0;
905 goto restart_2;
906 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700907 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700908 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700910 rt->dst.lastuse = jiffies;
911 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700912
913 return rt;
914}
915
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800916static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500917 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700918{
David S. Miller4c9483b2011-03-12 16:22:43 -0500919 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700920}
921
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000922static struct dst_entry *ip6_route_input_lookup(struct net *net,
923 struct net_device *dev,
924 struct flowi6 *fl6, int flags)
925{
926 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
927 flags |= RT6_LOOKUP_F_IFACE;
928
929 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
930}
931
Thomas Grafc71099a2006-08-04 23:20:06 -0700932void ip6_route_input(struct sk_buff *skb)
933{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000934 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900935 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700936 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500937 struct flowi6 fl6 = {
938 .flowi6_iif = skb->dev->ifindex,
939 .daddr = iph->daddr,
940 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500941 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500942 .flowi6_mark = skb->mark,
943 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700944 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700945
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000946 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700947}
948
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800949static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500950 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700951{
David S. Miller4c9483b2011-03-12 16:22:43 -0500952 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700953}
954
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700955struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500956 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700957{
958 int flags = 0;
959
Pavel Emelyanov1fb94892012-08-08 21:53:36 +0000960 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +0000961
David S. Miller4c9483b2011-03-12 16:22:43 -0500962 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700963 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700964
David S. Miller4c9483b2011-03-12 16:22:43 -0500965 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700966 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000967 else if (sk)
968 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700969
David S. Miller4c9483b2011-03-12 16:22:43 -0500970 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971}
972
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900973EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974
David S. Miller2774c132011-03-01 14:59:04 -0800975struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700976{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700977 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700978 struct dst_entry *new = NULL;
979
David S. Millerf5b0a872012-07-19 12:31:33 -0700980 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700981 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700982 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700983
Steffen Klassert81048912012-07-05 23:37:09 +0000984 memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
985 rt6_init_peer(rt, net->ipv6.peers);
986
David S. Miller14e50e52007-05-24 18:17:54 -0700987 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800988 new->input = dst_discard;
989 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700990
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000991 if (dst_metrics_read_only(&ort->dst))
992 new->_metrics = ort->dst._metrics;
993 else
994 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700995 rt->rt6i_idev = ort->rt6i_idev;
996 if (rt->rt6i_idev)
997 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -0700998
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000999 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001000 rt->rt6i_flags = ort->rt6i_flags;
1001 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001002 rt->rt6i_metric = 0;
1003
1004 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1005#ifdef CONFIG_IPV6_SUBTREES
1006 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1007#endif
1008
1009 dst_free(new);
1010 }
1011
David S. Miller69ead7a2011-03-01 14:45:33 -08001012 dst_release(dst_orig);
1013 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001014}
David S. Miller14e50e52007-05-24 18:17:54 -07001015
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016/*
1017 * Destination cache support functions
1018 */
1019
1020static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1021{
1022 struct rt6_info *rt;
1023
1024 rt = (struct rt6_info *) dst;
1025
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00001026 /* All IPV6 dsts are created with ->obsolete set to the value
1027 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
1028 * into this function always.
1029 */
1030 if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
1031 return NULL;
1032
David S. Miller6431cbc2011-02-07 20:38:06 -08001033 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1034 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001035 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001036 rt6_bind_peer(rt, 0);
1037 rt->rt6i_peer_genid = rt6_peer_genid();
1038 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001040 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 return NULL;
1042}
1043
1044static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1045{
1046 struct rt6_info *rt = (struct rt6_info *) dst;
1047
1048 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001049 if (rt->rt6i_flags & RTF_CACHE) {
1050 if (rt6_check_expired(rt)) {
1051 ip6_del_rt(rt);
1052 dst = NULL;
1053 }
1054 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001056 dst = NULL;
1057 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001059 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060}
1061
1062static void ip6_link_failure(struct sk_buff *skb)
1063{
1064 struct rt6_info *rt;
1065
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001066 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
Eric Dumazetadf30902009-06-02 05:19:30 +00001068 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001070 if (rt->rt6i_flags & RTF_CACHE)
1071 rt6_update_expires(rt, 0);
1072 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 rt->rt6i_node->fn_sernum = -1;
1074 }
1075}
1076
David S. Miller6700c272012-07-17 03:29:28 -07001077static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
1078 struct sk_buff *skb, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
1080 struct rt6_info *rt6 = (struct rt6_info*)dst;
1081
David S. Miller81aded22012-06-15 14:54:11 -07001082 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001084 struct net *net = dev_net(dst->dev);
1085
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 rt6->rt6i_flags |= RTF_MODIFIED;
1087 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001088 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001090 features |= RTAX_FEATURE_ALLFRAG;
1091 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 }
David S. Millerdefb3512010-12-08 21:16:57 -08001093 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001094 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 }
1096}
1097
David S. Miller42ae66c2012-06-15 20:01:57 -07001098void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1099 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001100{
1101 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1102 struct dst_entry *dst;
1103 struct flowi6 fl6;
1104
1105 memset(&fl6, 0, sizeof(fl6));
1106 fl6.flowi6_oif = oif;
1107 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001108 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001109 fl6.daddr = iph->daddr;
1110 fl6.saddr = iph->saddr;
1111 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1112
1113 dst = ip6_route_output(net, NULL, &fl6);
1114 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001115 ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07001116 dst_release(dst);
1117}
1118EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1119
1120void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1121{
1122 ip6_update_pmtu(skb, sock_net(sk), mtu,
1123 sk->sk_bound_dev_if, sk->sk_mark);
1124}
1125EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1126
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001127void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1128{
1129 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1130 struct dst_entry *dst;
1131 struct flowi6 fl6;
1132
1133 memset(&fl6, 0, sizeof(fl6));
1134 fl6.flowi6_oif = oif;
1135 fl6.flowi6_mark = mark;
1136 fl6.flowi6_flags = 0;
1137 fl6.daddr = iph->daddr;
1138 fl6.saddr = iph->saddr;
1139 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1140
1141 dst = ip6_route_output(net, NULL, &fl6);
1142 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001143 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001144 dst_release(dst);
1145}
1146EXPORT_SYMBOL_GPL(ip6_redirect);
1147
1148void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1149{
1150 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1151}
1152EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1153
David S. Miller0dbaee32010-12-13 12:52:14 -08001154static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155{
David S. Miller0dbaee32010-12-13 12:52:14 -08001156 struct net_device *dev = dst->dev;
1157 unsigned int mtu = dst_mtu(dst);
1158 struct net *net = dev_net(dev);
1159
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1161
Daniel Lezcano55786892008-03-04 13:47:47 -08001162 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1163 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164
1165 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001166 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1167 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1168 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 * rely only on pmtu discovery"
1170 */
1171 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1172 mtu = IPV6_MAXPLEN;
1173 return mtu;
1174}
1175
Steffen Klassertebb762f2011-11-23 02:12:51 +00001176static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001177{
David S. Millerd33e4552010-12-14 13:01:14 -08001178 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001179 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1180
1181 if (mtu)
1182 return mtu;
1183
1184 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001185
1186 rcu_read_lock();
1187 idev = __in6_dev_get(dst->dev);
1188 if (idev)
1189 mtu = idev->cnf.mtu6;
1190 rcu_read_unlock();
1191
1192 return mtu;
1193}
1194
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001195static struct dst_entry *icmp6_dst_gc_list;
1196static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001197
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001198struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001200 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201{
David S. Miller87a11572011-12-06 17:04:13 -05001202 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 struct rt6_info *rt;
1204 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001205 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
David S. Miller38308472011-12-03 18:02:47 -05001207 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001208 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
David S. Miller8b96d222012-06-11 02:01:56 -07001210 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001211 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001213 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 goto out;
1215 }
1216
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 if (neigh)
1218 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001219 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001220 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001221 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001222 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001223 dst_free(&rt->dst);
1224 return ERR_CAST(neigh);
1225 }
David S. Miller14deae42009-01-04 16:04:39 -08001226 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001228 rt->dst.flags |= DST_HOST;
1229 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001230 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001231 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001232 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001233 rt->rt6i_dst.plen = 128;
1234 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08001235 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001237 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001238 rt->dst.next = icmp6_dst_gc_list;
1239 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001240 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
Daniel Lezcano55786892008-03-04 13:47:47 -08001242 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
David S. Miller87a11572011-12-06 17:04:13 -05001244 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1245
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246out:
David S. Miller87a11572011-12-06 17:04:13 -05001247 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248}
1249
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001250int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251{
Hagen Paul Pfeifere9476e952011-02-25 05:45:19 +00001252 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001253 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001255 spin_lock_bh(&icmp6_dst_lock);
1256 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001257
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 while ((dst = *pprev) != NULL) {
1259 if (!atomic_read(&dst->__refcnt)) {
1260 *pprev = dst->next;
1261 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 } else {
1263 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001264 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 }
1266 }
1267
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001268 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001269
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001270 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271}
1272
David S. Miller1e493d12008-09-10 17:27:15 -07001273static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1274 void *arg)
1275{
1276 struct dst_entry *dst, **pprev;
1277
1278 spin_lock_bh(&icmp6_dst_lock);
1279 pprev = &icmp6_dst_gc_list;
1280 while ((dst = *pprev) != NULL) {
1281 struct rt6_info *rt = (struct rt6_info *) dst;
1282 if (func(rt, arg)) {
1283 *pprev = dst->next;
1284 dst_free(dst);
1285 } else {
1286 pprev = &dst->next;
1287 }
1288 }
1289 spin_unlock_bh(&icmp6_dst_lock);
1290}
1291
Daniel Lezcano569d3642008-01-18 03:56:57 -08001292static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001295 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001296 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1297 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1298 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1299 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1300 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001301 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
Eric Dumazetfc66f952010-10-08 06:37:34 +00001303 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001304 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001305 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 goto out;
1307
Benjamin Thery6891a342008-03-04 13:49:47 -08001308 net->ipv6.ip6_rt_gc_expire++;
1309 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1310 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001311 entries = dst_entries_get_slow(ops);
1312 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001313 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001315 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001316 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317}
1318
1319/* Clean host part of a prefix. Not necessary in radix tree,
1320 but results in cleaner routing tables.
1321
1322 Remove it only when all the things will work!
1323 */
1324
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001325int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326{
David S. Miller5170ae82010-12-12 21:35:57 -08001327 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001328 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001329 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001330 struct inet6_dev *idev;
1331
1332 rcu_read_lock();
1333 idev = __in6_dev_get(dev);
1334 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001335 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001336 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001337 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001338 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 }
1340 return hoplimit;
1341}
David S. Millerabbf46a2010-12-12 21:14:46 -08001342EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
1344/*
1345 *
1346 */
1347
Thomas Graf86872cb2006-08-22 00:01:08 -07001348int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349{
1350 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001351 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 struct rt6_info *rt = NULL;
1353 struct net_device *dev = NULL;
1354 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001355 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 int addr_type;
1357
Thomas Graf86872cb2006-08-22 00:01:08 -07001358 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 return -EINVAL;
1360#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001361 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 return -EINVAL;
1363#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001364 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001366 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 if (!dev)
1368 goto out;
1369 idev = in6_dev_get(dev);
1370 if (!idev)
1371 goto out;
1372 }
1373
Thomas Graf86872cb2006-08-22 00:01:08 -07001374 if (cfg->fc_metric == 0)
1375 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
Matti Vaittinend71314b2011-11-14 00:14:49 +00001377 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001378 if (cfg->fc_nlinfo.nlh &&
1379 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001380 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001381 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001382 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001383 table = fib6_new_table(net, cfg->fc_table);
1384 }
1385 } else {
1386 table = fib6_new_table(net, cfg->fc_table);
1387 }
David S. Miller38308472011-12-03 18:02:47 -05001388
1389 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001390 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001391
David S. Miller8b96d222012-06-11 02:01:56 -07001392 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
David S. Miller38308472011-12-03 18:02:47 -05001394 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 err = -ENOMEM;
1396 goto out;
1397 }
1398
Gao feng1716a962012-04-06 00:13:10 +00001399 if (cfg->fc_flags & RTF_EXPIRES)
1400 rt6_set_expires(rt, jiffies +
1401 clock_t_to_jiffies(cfg->fc_expires));
1402 else
1403 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
Thomas Graf86872cb2006-08-22 00:01:08 -07001405 if (cfg->fc_protocol == RTPROT_UNSPEC)
1406 cfg->fc_protocol = RTPROT_BOOT;
1407 rt->rt6i_protocol = cfg->fc_protocol;
1408
1409 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410
1411 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001412 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001413 else if (cfg->fc_flags & RTF_LOCAL)
1414 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001416 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
Changli Gaod8d1f302010-06-10 23:31:35 -07001418 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
Thomas Graf86872cb2006-08-22 00:01:08 -07001420 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1421 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001423 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001425 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1426 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1427 if (!metrics) {
1428 err = -ENOMEM;
1429 goto out;
1430 }
1431 dst_init_metrics(&rt->dst, metrics, 0);
1432 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001434 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1435 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436#endif
1437
Thomas Graf86872cb2006-08-22 00:01:08 -07001438 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
1440 /* We cannot add true routes via loopback here,
1441 they would result in kernel looping; promote them to reject routes
1442 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001443 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001444 (dev && (dev->flags & IFF_LOOPBACK) &&
1445 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1446 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001448 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 if (dev) {
1450 dev_put(dev);
1451 in6_dev_put(idev);
1452 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001453 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 dev_hold(dev);
1455 idev = in6_dev_get(dev);
1456 if (!idev) {
1457 err = -ENODEV;
1458 goto out;
1459 }
1460 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001461 rt->dst.output = ip6_pkt_discard_out;
1462 rt->dst.input = ip6_pkt_discard;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001464 switch (cfg->fc_type) {
1465 case RTN_BLACKHOLE:
1466 rt->dst.error = -EINVAL;
1467 break;
1468 case RTN_PROHIBIT:
1469 rt->dst.error = -EACCES;
1470 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00001471 case RTN_THROW:
1472 rt->dst.error = -EAGAIN;
1473 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001474 default:
1475 rt->dst.error = -ENETUNREACH;
1476 break;
1477 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 goto install_route;
1479 }
1480
Thomas Graf86872cb2006-08-22 00:01:08 -07001481 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001482 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 int gwa_type;
1484
Thomas Graf86872cb2006-08-22 00:01:08 -07001485 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001486 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 gwa_type = ipv6_addr_type(gw_addr);
1488
1489 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1490 struct rt6_info *grt;
1491
1492 /* IPv6 strictly inhibits using not link-local
1493 addresses as nexthop address.
1494 Otherwise, router will not able to send redirects.
1495 It is very good, but in some (rare!) circumstances
1496 (SIT, PtP, NBMA NOARP links) it is handy to allow
1497 some exceptions. --ANK
1498 */
1499 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001500 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 goto out;
1502
Daniel Lezcano55786892008-03-04 13:47:47 -08001503 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001506 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 goto out;
1508 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001509 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001510 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 goto out;
1512 }
1513 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001514 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 idev = grt->rt6i_idev;
1516 dev_hold(dev);
1517 in6_dev_hold(grt->rt6i_idev);
1518 }
David S. Miller38308472011-12-03 18:02:47 -05001519 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001521 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
1523 if (err)
1524 goto out;
1525 }
1526 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001527 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 goto out;
1529 }
1530
1531 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001532 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 goto out;
1534
Daniel Walterc3968a82011-04-13 21:10:57 +00001535 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1536 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1537 err = -EINVAL;
1538 goto out;
1539 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001540 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001541 rt->rt6i_prefsrc.plen = 128;
1542 } else
1543 rt->rt6i_prefsrc.plen = 0;
1544
Thomas Graf86872cb2006-08-22 00:01:08 -07001545 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001546 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001547 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 }
1550
Thomas Graf86872cb2006-08-22 00:01:08 -07001551 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
1553install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001554 if (cfg->fc_mx) {
1555 struct nlattr *nla;
1556 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Thomas Graf86872cb2006-08-22 00:01:08 -07001558 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001559 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001560
1561 if (type) {
1562 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 err = -EINVAL;
1564 goto out;
1565 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001566
David S. Millerdefb3512010-12-08 21:16:57 -08001567 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 }
1570 }
1571
Changli Gaod8d1f302010-06-10 23:31:35 -07001572 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001574 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001575
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001576 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001577
Thomas Graf86872cb2006-08-22 00:01:08 -07001578 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
1580out:
1581 if (dev)
1582 dev_put(dev);
1583 if (idev)
1584 in6_dev_put(idev);
1585 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001586 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 return err;
1588}
1589
Thomas Graf86872cb2006-08-22 00:01:08 -07001590static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591{
1592 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001593 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001594 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
Gao feng6825a262012-09-19 19:25:34 +00001596 if (rt == net->ipv6.ip6_null_entry) {
1597 err = -ENOENT;
1598 goto out;
1599 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07001600
Thomas Grafc71099a2006-08-04 23:20:06 -07001601 table = rt->rt6i_table;
1602 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07001603 err = fib6_del(rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -07001604 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
Gao feng6825a262012-09-19 19:25:34 +00001606out:
1607 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 return err;
1609}
1610
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001611int ip6_del_rt(struct rt6_info *rt)
1612{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001613 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001614 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001615 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001616 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001617}
1618
Thomas Graf86872cb2006-08-22 00:01:08 -07001619static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620{
Thomas Grafc71099a2006-08-04 23:20:06 -07001621 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 struct fib6_node *fn;
1623 struct rt6_info *rt;
1624 int err = -ESRCH;
1625
Daniel Lezcano55786892008-03-04 13:47:47 -08001626 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001627 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001628 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
Thomas Grafc71099a2006-08-04 23:20:06 -07001630 read_lock_bh(&table->tb6_lock);
1631
1632 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001633 &cfg->fc_dst, cfg->fc_dst_len,
1634 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001635
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001637 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001638 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001639 (!rt->dst.dev ||
1640 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001642 if (cfg->fc_flags & RTF_GATEWAY &&
1643 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001645 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001647 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001648 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Thomas Graf86872cb2006-08-22 00:01:08 -07001650 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 }
1652 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001653 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
1655 return err;
1656}
1657
David S. Miller6700c272012-07-17 03:29:28 -07001658static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001659{
David S. Millere8599ff2012-07-11 23:43:53 -07001660 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001661 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001662 struct rt6_info *rt, *nrt = NULL;
1663 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001664 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001665 const struct in6_addr *dest;
1666 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001667 struct inet6_dev *in6_dev;
1668 struct neighbour *neigh;
1669 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001670 int optlen, on_link;
1671 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001672
1673 optlen = skb->tail - skb->transport_header;
1674 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1675
1676 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001677 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001678 return;
1679 }
1680
1681 icmph = icmp6_hdr(skb);
1682 target = (const struct in6_addr *) (icmph + 1);
1683 dest = target + 1;
1684
1685 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001686 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001687 return;
1688 }
1689
David S. Miller6e157b62012-07-12 00:05:02 -07001690 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001691 if (ipv6_addr_equal(dest, target)) {
1692 on_link = 1;
1693 } else if (ipv6_addr_type(target) !=
1694 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001695 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001696 return;
1697 }
1698
1699 in6_dev = __in6_dev_get(skb->dev);
1700 if (!in6_dev)
1701 return;
1702 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1703 return;
1704
1705 /* RFC2461 8.1:
1706 * The IP source address of the Redirect MUST be the same as the current
1707 * first-hop router for the specified ICMP Destination Address.
1708 */
1709
1710 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1711 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1712 return;
1713 }
David S. Miller6e157b62012-07-12 00:05:02 -07001714
1715 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001716 if (ndopts.nd_opts_tgt_lladdr) {
1717 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1718 skb->dev);
1719 if (!lladdr) {
1720 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1721 return;
1722 }
1723 }
1724
David S. Miller6e157b62012-07-12 00:05:02 -07001725 rt = (struct rt6_info *) dst;
1726 if (rt == net->ipv6.ip6_null_entry) {
1727 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1728 return;
1729 }
1730
1731 /* Redirect received -> path was valid.
1732 * Look, redirects are sent only in response to data packets,
1733 * so that this nexthop apparently is reachable. --ANK
1734 */
1735 dst_confirm(&rt->dst);
1736
David S. Millere8599ff2012-07-11 23:43:53 -07001737 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1738 if (!neigh)
1739 return;
1740
David S. Miller6e157b62012-07-12 00:05:02 -07001741 /* Duplicate redirect: silently ignore. */
1742 old_neigh = rt->n;
1743 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001744 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 /*
1747 * We have finally decided to accept it.
1748 */
1749
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001750 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1752 NEIGH_UPDATE_F_OVERRIDE|
1753 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1754 NEIGH_UPDATE_F_ISROUTER))
1755 );
1756
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001757 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001758 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759 goto out;
1760
1761 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1762 if (on_link)
1763 nrt->rt6i_flags &= ~RTF_GATEWAY;
1764
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001765 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001766 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767
Thomas Graf40e22e82006-08-22 00:00:45 -07001768 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 goto out;
1770
Changli Gaod8d1f302010-06-10 23:31:35 -07001771 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001772 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001773 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001774 netevent.new_neigh = neigh;
1775 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001776 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1777
David S. Miller38308472011-12-03 18:02:47 -05001778 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001779 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001780 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 }
1782
1783out:
David S. Millere8599ff2012-07-11 23:43:53 -07001784 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001785}
1786
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 * Misc support functions
1789 */
1790
Gao feng1716a962012-04-06 00:13:10 +00001791static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001792 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793{
David S. Millerd1918542011-12-28 20:19:20 -05001794 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001795 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1796 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
1798 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001799 rt->dst.input = ort->dst.input;
1800 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001801 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001803 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001804 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001805 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001806 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 rt->rt6i_idev = ort->rt6i_idev;
1808 if (rt->rt6i_idev)
1809 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001810 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001812 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001813 rt->rt6i_flags = ort->rt6i_flags;
1814 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1815 (RTF_DEFAULT | RTF_ADDRCONF))
1816 rt6_set_from(rt, ort);
1817 else
1818 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 rt->rt6i_metric = 0;
1820
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821#ifdef CONFIG_IPV6_SUBTREES
1822 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1823#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001824 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001825 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 }
1827 return rt;
1828}
1829
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001830#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001831static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001832 const struct in6_addr *prefix, int prefixlen,
1833 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001834{
1835 struct fib6_node *fn;
1836 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001837 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001838
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001839 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001840 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001841 return NULL;
1842
Li RongQing5744dd92012-09-11 21:59:01 +00001843 read_lock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -07001844 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001845 if (!fn)
1846 goto out;
1847
Changli Gaod8d1f302010-06-10 23:31:35 -07001848 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001849 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001850 continue;
1851 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1852 continue;
1853 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1854 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001855 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001856 break;
1857 }
1858out:
Li RongQing5744dd92012-09-11 21:59:01 +00001859 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001860 return rt;
1861}
1862
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001863static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001864 const struct in6_addr *prefix, int prefixlen,
1865 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001866 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001867{
Thomas Graf86872cb2006-08-22 00:01:08 -07001868 struct fib6_config cfg = {
1869 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001870 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001871 .fc_ifindex = ifindex,
1872 .fc_dst_len = prefixlen,
1873 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1874 RTF_UP | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001875 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001876 .fc_nlinfo.nlh = NULL,
1877 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001878 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001879
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001880 cfg.fc_dst = *prefix;
1881 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001882
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001883 /* We should treat it as a default route if prefix length is 0. */
1884 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001885 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001886
Thomas Graf86872cb2006-08-22 00:01:08 -07001887 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001888
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001889 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001890}
1891#endif
1892
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001893struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001894{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001896 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001898 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001899 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001900 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901
Li RongQing5744dd92012-09-11 21:59:01 +00001902 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001903 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001904 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001905 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1907 break;
1908 }
1909 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001910 dst_hold(&rt->dst);
Li RongQing5744dd92012-09-11 21:59:01 +00001911 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 return rt;
1913}
1914
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001915struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001916 struct net_device *dev,
1917 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918{
Thomas Graf86872cb2006-08-22 00:01:08 -07001919 struct fib6_config cfg = {
1920 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001921 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001922 .fc_ifindex = dev->ifindex,
1923 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1924 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001925 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08001926 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001927 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001928 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001930 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Thomas Graf86872cb2006-08-22 00:01:08 -07001932 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 return rt6_get_dflt_router(gwaddr, dev);
1935}
1936
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001937void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938{
1939 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001940 struct fib6_table *table;
1941
1942 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001943 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001944 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001945 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946
1947restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001948 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001949 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001951 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001952 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001953 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 goto restart;
1955 }
1956 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001957 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958}
1959
Daniel Lezcano55786892008-03-04 13:47:47 -08001960static void rtmsg_to_fib6_config(struct net *net,
1961 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001962 struct fib6_config *cfg)
1963{
1964 memset(cfg, 0, sizeof(*cfg));
1965
1966 cfg->fc_table = RT6_TABLE_MAIN;
1967 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1968 cfg->fc_metric = rtmsg->rtmsg_metric;
1969 cfg->fc_expires = rtmsg->rtmsg_info;
1970 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1971 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1972 cfg->fc_flags = rtmsg->rtmsg_flags;
1973
Daniel Lezcano55786892008-03-04 13:47:47 -08001974 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001975
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001976 cfg->fc_dst = rtmsg->rtmsg_dst;
1977 cfg->fc_src = rtmsg->rtmsg_src;
1978 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001979}
1980
Daniel Lezcano55786892008-03-04 13:47:47 -08001981int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982{
Thomas Graf86872cb2006-08-22 00:01:08 -07001983 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 struct in6_rtmsg rtmsg;
1985 int err;
1986
1987 switch(cmd) {
1988 case SIOCADDRT: /* Add a route */
1989 case SIOCDELRT: /* Delete a route */
1990 if (!capable(CAP_NET_ADMIN))
1991 return -EPERM;
1992 err = copy_from_user(&rtmsg, arg,
1993 sizeof(struct in6_rtmsg));
1994 if (err)
1995 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001996
Daniel Lezcano55786892008-03-04 13:47:47 -08001997 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001998
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 rtnl_lock();
2000 switch (cmd) {
2001 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002002 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 break;
2004 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002005 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 break;
2007 default:
2008 err = -EINVAL;
2009 }
2010 rtnl_unlock();
2011
2012 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002013 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014
2015 return -EINVAL;
2016}
2017
2018/*
2019 * Drop the packet on the floor
2020 */
2021
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002022static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002024 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002025 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002026 switch (ipstats_mib_noroutes) {
2027 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002028 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002029 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002030 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2031 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002032 break;
2033 }
2034 /* FALLTHROUGH */
2035 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002036 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2037 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002038 break;
2039 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002040 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 kfree_skb(skb);
2042 return 0;
2043}
2044
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002045static int ip6_pkt_discard(struct sk_buff *skb)
2046{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002047 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002048}
2049
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002050static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
Eric Dumazetadf30902009-06-02 05:19:30 +00002052 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002053 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054}
2055
David S. Miller6723ab52006-10-18 21:20:57 -07002056#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2057
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002058static int ip6_pkt_prohibit(struct sk_buff *skb)
2059{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002060 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002061}
2062
2063static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2064{
Eric Dumazetadf30902009-06-02 05:19:30 +00002065 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002066 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002067}
2068
David S. Miller6723ab52006-10-18 21:20:57 -07002069#endif
2070
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071/*
2072 * Allocate a dst for local (unicast / anycast) address.
2073 */
2074
2075struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2076 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002077 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002079 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002080 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002081 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082
David S. Miller38308472011-12-03 18:02:47 -05002083 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002084 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002086 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 in6_dev_hold(idev);
2089
David S. Miller11d53b42011-06-24 15:23:34 -07002090 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002091 rt->dst.input = ip6_input;
2092 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 rt->rt6i_idev = idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094
2095 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002096 if (anycast)
2097 rt->rt6i_flags |= RTF_ANYCAST;
2098 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002100 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002101 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002102 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002103 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 }
2105
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002106 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002108 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109
Changli Gaod8d1f302010-06-10 23:31:35 -07002110 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111
2112 return rt;
2113}
2114
Daniel Walterc3968a82011-04-13 21:10:57 +00002115int ip6_route_get_saddr(struct net *net,
2116 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002117 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002118 unsigned int prefs,
2119 struct in6_addr *saddr)
2120{
2121 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2122 int err = 0;
2123 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002124 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002125 else
2126 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2127 daddr, prefs, saddr);
2128 return err;
2129}
2130
2131/* remove deleted ip from prefsrc entries */
2132struct arg_dev_net_ip {
2133 struct net_device *dev;
2134 struct net *net;
2135 struct in6_addr *addr;
2136};
2137
2138static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2139{
2140 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2141 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2142 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2143
David S. Millerd1918542011-12-28 20:19:20 -05002144 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002145 rt != net->ipv6.ip6_null_entry &&
2146 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2147 /* remove prefsrc entry */
2148 rt->rt6i_prefsrc.plen = 0;
2149 }
2150 return 0;
2151}
2152
2153void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2154{
2155 struct net *net = dev_net(ifp->idev->dev);
2156 struct arg_dev_net_ip adni = {
2157 .dev = ifp->idev->dev,
2158 .net = net,
2159 .addr = &ifp->addr,
2160 };
2161 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2162}
2163
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002164struct arg_dev_net {
2165 struct net_device *dev;
2166 struct net *net;
2167};
2168
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169static int fib6_ifdown(struct rt6_info *rt, void *arg)
2170{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002171 const struct arg_dev_net *adn = arg;
2172 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002173
David S. Millerd1918542011-12-28 20:19:20 -05002174 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002175 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002177
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 return 0;
2179}
2180
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002181void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002183 struct arg_dev_net adn = {
2184 .dev = dev,
2185 .net = net,
2186 };
2187
2188 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002189 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190}
2191
Eric Dumazet95c96172012-04-15 05:58:06 +00002192struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002194 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195};
2196
2197static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2198{
2199 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2200 struct inet6_dev *idev;
2201
2202 /* In IPv6 pmtu discovery is not optional,
2203 so that RTAX_MTU lock cannot disable it.
2204 We still use this lock to block changes
2205 caused by addrconf/ndisc.
2206 */
2207
2208 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002209 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210 return 0;
2211
2212 /* For administrative MTU increase, there is no way to discover
2213 IPv6 PMTU increase, so PMTU increase should be updated here.
2214 Since RFC 1981 doesn't include administrative MTU increase
2215 update PMTU increase is a MUST. (i.e. jumbo frame)
2216 */
2217 /*
2218 If new MTU is less than route PMTU, this new MTU will be the
2219 lowest MTU in the path, update the route PMTU to reflect PMTU
2220 decreases; if new MTU is greater than route PMTU, and the
2221 old MTU is the lowest MTU in the path, update the route PMTU
2222 to reflect the increase. In this case if the other nodes' MTU
2223 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2224 PMTU discouvery.
2225 */
David S. Millerd1918542011-12-28 20:19:20 -05002226 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002227 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2228 (dst_mtu(&rt->dst) >= arg->mtu ||
2229 (dst_mtu(&rt->dst) < arg->mtu &&
2230 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002231 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002232 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 return 0;
2234}
2235
Eric Dumazet95c96172012-04-15 05:58:06 +00002236void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237{
Thomas Grafc71099a2006-08-04 23:20:06 -07002238 struct rt6_mtu_change_arg arg = {
2239 .dev = dev,
2240 .mtu = mtu,
2241 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002243 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244}
2245
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002246static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002247 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002248 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002249 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002250 [RTA_PRIORITY] = { .type = NLA_U32 },
2251 [RTA_METRICS] = { .type = NLA_NESTED },
2252};
2253
2254static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2255 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256{
Thomas Graf86872cb2006-08-22 00:01:08 -07002257 struct rtmsg *rtm;
2258 struct nlattr *tb[RTA_MAX+1];
2259 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260
Thomas Graf86872cb2006-08-22 00:01:08 -07002261 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2262 if (err < 0)
2263 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264
Thomas Graf86872cb2006-08-22 00:01:08 -07002265 err = -EINVAL;
2266 rtm = nlmsg_data(nlh);
2267 memset(cfg, 0, sizeof(*cfg));
2268
2269 cfg->fc_table = rtm->rtm_table;
2270 cfg->fc_dst_len = rtm->rtm_dst_len;
2271 cfg->fc_src_len = rtm->rtm_src_len;
2272 cfg->fc_flags = RTF_UP;
2273 cfg->fc_protocol = rtm->rtm_protocol;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002274 cfg->fc_type = rtm->rtm_type;
Thomas Graf86872cb2006-08-22 00:01:08 -07002275
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002276 if (rtm->rtm_type == RTN_UNREACHABLE ||
2277 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002278 rtm->rtm_type == RTN_PROHIBIT ||
2279 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07002280 cfg->fc_flags |= RTF_REJECT;
2281
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002282 if (rtm->rtm_type == RTN_LOCAL)
2283 cfg->fc_flags |= RTF_LOCAL;
2284
Eric W. Biederman15e47302012-09-07 20:12:54 +00002285 cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
Thomas Graf86872cb2006-08-22 00:01:08 -07002286 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002287 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002288
2289 if (tb[RTA_GATEWAY]) {
2290 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2291 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002293
2294 if (tb[RTA_DST]) {
2295 int plen = (rtm->rtm_dst_len + 7) >> 3;
2296
2297 if (nla_len(tb[RTA_DST]) < plen)
2298 goto errout;
2299
2300 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002302
2303 if (tb[RTA_SRC]) {
2304 int plen = (rtm->rtm_src_len + 7) >> 3;
2305
2306 if (nla_len(tb[RTA_SRC]) < plen)
2307 goto errout;
2308
2309 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002311
Daniel Walterc3968a82011-04-13 21:10:57 +00002312 if (tb[RTA_PREFSRC])
2313 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2314
Thomas Graf86872cb2006-08-22 00:01:08 -07002315 if (tb[RTA_OIF])
2316 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2317
2318 if (tb[RTA_PRIORITY])
2319 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2320
2321 if (tb[RTA_METRICS]) {
2322 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2323 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002325
2326 if (tb[RTA_TABLE])
2327 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2328
2329 err = 0;
2330errout:
2331 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332}
2333
Thomas Grafc127ea22007-03-22 11:58:32 -07002334static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335{
Thomas Graf86872cb2006-08-22 00:01:08 -07002336 struct fib6_config cfg;
2337 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338
Thomas Graf86872cb2006-08-22 00:01:08 -07002339 err = rtm_to_fib6_config(skb, nlh, &cfg);
2340 if (err < 0)
2341 return err;
2342
2343 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344}
2345
Thomas Grafc127ea22007-03-22 11:58:32 -07002346static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347{
Thomas Graf86872cb2006-08-22 00:01:08 -07002348 struct fib6_config cfg;
2349 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
Thomas Graf86872cb2006-08-22 00:01:08 -07002351 err = rtm_to_fib6_config(skb, nlh, &cfg);
2352 if (err < 0)
2353 return err;
2354
2355 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356}
2357
Thomas Graf339bf982006-11-10 14:10:15 -08002358static inline size_t rt6_nlmsg_size(void)
2359{
2360 return NLMSG_ALIGN(sizeof(struct rtmsg))
2361 + nla_total_size(16) /* RTA_SRC */
2362 + nla_total_size(16) /* RTA_DST */
2363 + nla_total_size(16) /* RTA_GATEWAY */
2364 + nla_total_size(16) /* RTA_PREFSRC */
2365 + nla_total_size(4) /* RTA_TABLE */
2366 + nla_total_size(4) /* RTA_IIF */
2367 + nla_total_size(4) /* RTA_OIF */
2368 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002369 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002370 + nla_total_size(sizeof(struct rta_cacheinfo));
2371}
2372
Brian Haley191cd582008-08-14 15:33:21 -07002373static int rt6_fill_node(struct net *net,
2374 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002375 struct in6_addr *dst, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002376 int iif, int type, u32 portid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002377 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378{
2379 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002380 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002381 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002382 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002383 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384
2385 if (prefix) { /* user wants prefix routes only */
2386 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2387 /* success since this is not a prefix route */
2388 return 1;
2389 }
2390 }
2391
Eric W. Biederman15e47302012-09-07 20:12:54 +00002392 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002393 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002394 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002395
2396 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 rtm->rtm_family = AF_INET6;
2398 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2399 rtm->rtm_src_len = rt->rt6i_src.plen;
2400 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002401 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002402 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002403 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002404 table = RT6_TABLE_UNSPEC;
2405 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002406 if (nla_put_u32(skb, RTA_TABLE, table))
2407 goto nla_put_failure;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002408 if (rt->rt6i_flags & RTF_REJECT) {
2409 switch (rt->dst.error) {
2410 case -EINVAL:
2411 rtm->rtm_type = RTN_BLACKHOLE;
2412 break;
2413 case -EACCES:
2414 rtm->rtm_type = RTN_PROHIBIT;
2415 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002416 case -EAGAIN:
2417 rtm->rtm_type = RTN_THROW;
2418 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002419 default:
2420 rtm->rtm_type = RTN_UNREACHABLE;
2421 break;
2422 }
2423 }
David S. Miller38308472011-12-03 18:02:47 -05002424 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002425 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002426 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 rtm->rtm_type = RTN_LOCAL;
2428 else
2429 rtm->rtm_type = RTN_UNICAST;
2430 rtm->rtm_flags = 0;
2431 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2432 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002433 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 rtm->rtm_protocol = RTPROT_REDIRECT;
Denis Ovsienkof0396f602012-07-10 04:45:50 +00002435 else if (rt->rt6i_flags & RTF_ADDRCONF) {
2436 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
2437 rtm->rtm_protocol = RTPROT_RA;
2438 else
2439 rtm->rtm_protocol = RTPROT_KERNEL;
2440 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441
David S. Miller38308472011-12-03 18:02:47 -05002442 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 rtm->rtm_flags |= RTM_F_CLONED;
2444
2445 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002446 if (nla_put(skb, RTA_DST, 16, dst))
2447 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002448 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002450 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2451 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452#ifdef CONFIG_IPV6_SUBTREES
2453 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002454 if (nla_put(skb, RTA_SRC, 16, src))
2455 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002456 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002457 } else if (rtm->rtm_src_len &&
2458 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2459 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002461 if (iif) {
2462#ifdef CONFIG_IPV6_MROUTE
2463 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002464 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002465 if (err <= 0) {
2466 if (!nowait) {
2467 if (err == 0)
2468 return 0;
2469 goto nla_put_failure;
2470 } else {
2471 if (err == -EMSGSIZE)
2472 goto nla_put_failure;
2473 }
2474 }
2475 } else
2476#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002477 if (nla_put_u32(skb, RTA_IIF, iif))
2478 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002479 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002481 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2482 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2483 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002485
Daniel Walterc3968a82011-04-13 21:10:57 +00002486 if (rt->rt6i_prefsrc.plen) {
2487 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002488 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002489 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2490 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002491 }
2492
David S. Millerdefb3512010-12-08 21:16:57 -08002493 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002494 goto nla_put_failure;
2495
David S. Miller97cac082012-07-02 22:43:47 -07002496 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002497 if (n) {
Amerigo Wangfdd66812012-09-10 02:48:44 +00002498 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0)
Eric Dumazet94f826b2012-03-27 09:53:52 +00002499 goto nla_put_failure;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002500 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002501
David S. Millerc78679e2012-04-01 20:27:33 -04002502 if (rt->dst.dev &&
2503 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2504 goto nla_put_failure;
2505 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2506 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00002507
2508 expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002509
David S. Miller87a50692012-07-10 05:06:14 -07002510 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002511 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512
Thomas Graf2d7202b2006-08-22 00:01:27 -07002513 return nlmsg_end(skb, nlh);
2514
2515nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002516 nlmsg_cancel(skb, nlh);
2517 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518}
2519
Patrick McHardy1b43af52006-08-10 23:11:17 -07002520int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521{
2522 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2523 int prefix;
2524
Thomas Graf2d7202b2006-08-22 00:01:27 -07002525 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2526 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2528 } else
2529 prefix = 0;
2530
Brian Haley191cd582008-08-14 15:33:21 -07002531 return rt6_fill_node(arg->net,
2532 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002533 NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002534 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535}
2536
Thomas Grafc127ea22007-03-22 11:58:32 -07002537static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002539 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002540 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002542 struct sk_buff *skb;
2543 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002544 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002545 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002546
2547 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2548 if (err < 0)
2549 goto errout;
2550
2551 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002552 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002553
2554 if (tb[RTA_SRC]) {
2555 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2556 goto errout;
2557
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002558 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002559 }
2560
2561 if (tb[RTA_DST]) {
2562 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2563 goto errout;
2564
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002565 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002566 }
2567
2568 if (tb[RTA_IIF])
2569 iif = nla_get_u32(tb[RTA_IIF]);
2570
2571 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002572 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002573
2574 if (iif) {
2575 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002576 int flags = 0;
2577
Daniel Lezcano55786892008-03-04 13:47:47 -08002578 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002579 if (!dev) {
2580 err = -ENODEV;
2581 goto errout;
2582 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002583
2584 fl6.flowi6_iif = iif;
2585
2586 if (!ipv6_addr_any(&fl6.saddr))
2587 flags |= RT6_LOOKUP_F_HAS_SADDR;
2588
2589 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2590 flags);
2591 } else {
2592 fl6.flowi6_oif = oif;
2593
2594 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002595 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596
2597 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002598 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002599 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002600 err = -ENOBUFS;
2601 goto errout;
2602 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603
2604 /* Reserve room for dummy headers, this skb can pass
2605 through good chunk of routing engine.
2606 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002607 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2609
Changli Gaod8d1f302010-06-10 23:31:35 -07002610 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611
David S. Miller4c9483b2011-03-12 16:22:43 -05002612 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002613 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002614 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002616 kfree_skb(skb);
2617 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618 }
2619
Eric W. Biederman15e47302012-09-07 20:12:54 +00002620 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07002621errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623}
2624
Thomas Graf86872cb2006-08-22 00:01:08 -07002625void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626{
2627 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002628 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002629 u32 seq;
2630 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002632 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002633 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002634
Thomas Graf339bf982006-11-10 14:10:15 -08002635 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002636 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002637 goto errout;
2638
Brian Haley191cd582008-08-14 15:33:21 -07002639 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002640 event, info->portid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002641 if (err < 0) {
2642 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2643 WARN_ON(err == -EMSGSIZE);
2644 kfree_skb(skb);
2645 goto errout;
2646 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00002647 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002648 info->nlh, gfp_any());
2649 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002650errout:
2651 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002652 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653}
2654
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002655static int ip6_route_dev_notify(struct notifier_block *this,
2656 unsigned long event, void *data)
2657{
2658 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002659 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002660
2661 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002662 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002663 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2664#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002665 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002666 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002667 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002668 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2669#endif
2670 }
2671
2672 return NOTIFY_OK;
2673}
2674
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675/*
2676 * /proc
2677 */
2678
2679#ifdef CONFIG_PROC_FS
2680
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681struct rt6_proc_arg
2682{
2683 char *buffer;
2684 int offset;
2685 int length;
2686 int skip;
2687 int len;
2688};
2689
2690static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2691{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002692 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002693 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002695 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696
2697#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002698 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002700 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701#endif
David S. Miller97cac082012-07-02 22:43:47 -07002702 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002703 if (n) {
2704 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002706 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 }
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002708 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002709 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2710 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002711 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 return 0;
2713}
2714
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002715static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002717 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002718 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002719 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720}
2721
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002722static int ipv6_route_open(struct inode *inode, struct file *file)
2723{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002724 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002725}
2726
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002727static const struct file_operations ipv6_route_proc_fops = {
2728 .owner = THIS_MODULE,
2729 .open = ipv6_route_open,
2730 .read = seq_read,
2731 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002732 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002733};
2734
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2736{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002737 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002739 net->ipv6.rt6_stats->fib_nodes,
2740 net->ipv6.rt6_stats->fib_route_nodes,
2741 net->ipv6.rt6_stats->fib_rt_alloc,
2742 net->ipv6.rt6_stats->fib_rt_entries,
2743 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002744 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002745 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746
2747 return 0;
2748}
2749
2750static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2751{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002752 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002753}
2754
Arjan van de Ven9a321442007-02-12 00:55:35 -08002755static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 .owner = THIS_MODULE,
2757 .open = rt6_stats_seq_open,
2758 .read = seq_read,
2759 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002760 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761};
2762#endif /* CONFIG_PROC_FS */
2763
2764#ifdef CONFIG_SYSCTL
2765
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002767int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 void __user *buffer, size_t *lenp, loff_t *ppos)
2769{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002770 struct net *net;
2771 int delay;
2772 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002774
2775 net = (struct net *)ctl->extra1;
2776 delay = net->ipv6.sysctl.flush_delay;
2777 proc_dointvec(ctl, write, buffer, lenp, ppos);
2778 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2779 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780}
2781
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002782ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002783 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002785 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002787 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002788 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 },
2790 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002792 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 .maxlen = sizeof(int),
2794 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002795 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 },
2797 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002799 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 .maxlen = sizeof(int),
2801 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002802 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 },
2804 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002806 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 .maxlen = sizeof(int),
2808 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002809 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 },
2811 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002813 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 .maxlen = sizeof(int),
2815 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002816 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817 },
2818 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002820 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821 .maxlen = sizeof(int),
2822 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002823 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 },
2825 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002827 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 .maxlen = sizeof(int),
2829 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002830 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 },
2832 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002834 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 .maxlen = sizeof(int),
2836 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002837 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 },
2839 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002841 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 .maxlen = sizeof(int),
2843 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002844 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 },
2846 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002848 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 .maxlen = sizeof(int),
2850 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002851 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002853 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854};
2855
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002856struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002857{
2858 struct ctl_table *table;
2859
2860 table = kmemdup(ipv6_route_table_template,
2861 sizeof(ipv6_route_table_template),
2862 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002863
2864 if (table) {
2865 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002866 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002867 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002868 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2869 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2870 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2871 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2872 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2873 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2874 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002875 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002876 }
2877
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002878 return table;
2879}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880#endif
2881
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002882static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002883{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002884 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002885
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002886 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2887 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002888
Eric Dumazetfc66f952010-10-08 06:37:34 +00002889 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2890 goto out_ip6_dst_ops;
2891
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002892 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2893 sizeof(*net->ipv6.ip6_null_entry),
2894 GFP_KERNEL);
2895 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002896 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002897 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002898 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002899 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002900 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2901 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002902
2903#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2904 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2905 sizeof(*net->ipv6.ip6_prohibit_entry),
2906 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002907 if (!net->ipv6.ip6_prohibit_entry)
2908 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002909 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002910 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002911 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002912 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2913 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002914
2915 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2916 sizeof(*net->ipv6.ip6_blk_hole_entry),
2917 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002918 if (!net->ipv6.ip6_blk_hole_entry)
2919 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002920 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002921 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002922 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002923 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2924 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002925#endif
2926
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002927 net->ipv6.sysctl.flush_delay = 0;
2928 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2929 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2930 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2931 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2932 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2933 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2934 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2935
Benjamin Thery6891a342008-03-04 13:49:47 -08002936 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2937
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002938 ret = 0;
2939out:
2940 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002941
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002942#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2943out_ip6_prohibit_entry:
2944 kfree(net->ipv6.ip6_prohibit_entry);
2945out_ip6_null_entry:
2946 kfree(net->ipv6.ip6_null_entry);
2947#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002948out_ip6_dst_entries:
2949 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002950out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002951 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002952}
2953
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002954static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002955{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002956 kfree(net->ipv6.ip6_null_entry);
2957#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2958 kfree(net->ipv6.ip6_prohibit_entry);
2959 kfree(net->ipv6.ip6_blk_hole_entry);
2960#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002961 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002962}
2963
Thomas Grafd1896342012-06-18 12:08:33 +00002964static int __net_init ip6_route_net_init_late(struct net *net)
2965{
2966#ifdef CONFIG_PROC_FS
2967 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2968 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2969#endif
2970 return 0;
2971}
2972
2973static void __net_exit ip6_route_net_exit_late(struct net *net)
2974{
2975#ifdef CONFIG_PROC_FS
2976 proc_net_remove(net, "ipv6_route");
2977 proc_net_remove(net, "rt6_stats");
2978#endif
2979}
2980
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002981static struct pernet_operations ip6_route_net_ops = {
2982 .init = ip6_route_net_init,
2983 .exit = ip6_route_net_exit,
2984};
2985
David S. Millerc3426b42012-06-09 16:27:05 -07002986static int __net_init ipv6_inetpeer_init(struct net *net)
2987{
2988 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2989
2990 if (!bp)
2991 return -ENOMEM;
2992 inet_peer_base_init(bp);
2993 net->ipv6.peers = bp;
2994 return 0;
2995}
2996
2997static void __net_exit ipv6_inetpeer_exit(struct net *net)
2998{
2999 struct inet_peer_base *bp = net->ipv6.peers;
3000
3001 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07003002 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07003003 kfree(bp);
3004}
3005
David S. Miller2b823f72012-06-09 19:00:16 -07003006static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003007 .init = ipv6_inetpeer_init,
3008 .exit = ipv6_inetpeer_exit,
3009};
3010
Thomas Grafd1896342012-06-18 12:08:33 +00003011static struct pernet_operations ip6_route_net_late_ops = {
3012 .init = ip6_route_net_init_late,
3013 .exit = ip6_route_net_exit_late,
3014};
3015
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003016static struct notifier_block ip6_route_dev_notifier = {
3017 .notifier_call = ip6_route_dev_notify,
3018 .priority = 0,
3019};
3020
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003021int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003022{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003023 int ret;
3024
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003025 ret = -ENOMEM;
3026 ip6_dst_ops_template.kmem_cachep =
3027 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3028 SLAB_HWCACHE_ALIGN, NULL);
3029 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003030 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003031
Eric Dumazetfc66f952010-10-08 06:37:34 +00003032 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003033 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003034 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003035
David S. Millerc3426b42012-06-09 16:27:05 -07003036 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3037 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003038 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003039
David S. Miller7e52b332012-06-15 15:51:55 -07003040 ret = register_pernet_subsys(&ip6_route_net_ops);
3041 if (ret)
3042 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003043
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003044 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3045
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003046 /* Registering of the loopback is done before this portion of code,
3047 * the loopback reference in rt6_info will not be taken, do it
3048 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003049 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003050 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3051 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003052 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003053 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003054 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003055 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3056 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003057 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003058 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003059 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003060
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003061 ret = xfrm6_init();
3062 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003063 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003064
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003065 ret = fib6_rules_init();
3066 if (ret)
3067 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003068
Thomas Grafd1896342012-06-18 12:08:33 +00003069 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3070 if (ret)
3071 goto fib6_rules_init;
3072
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003073 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003074 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3075 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3076 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003077 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003078
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003079 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003080 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003081 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003082
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003083out:
3084 return ret;
3085
Thomas Grafd1896342012-06-18 12:08:33 +00003086out_register_late_subsys:
3087 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003088fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003089 fib6_rules_cleanup();
3090xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003091 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003092out_fib6_init:
3093 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003094out_register_subsys:
3095 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003096out_register_inetpeer:
3097 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003098out_dst_entries:
3099 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003100out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003101 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003102 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003103}
3104
3105void ip6_route_cleanup(void)
3106{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003107 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003108 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003109 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003110 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003111 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003112 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003113 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003114 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003115 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116}