blob: fb29e2215a19db8c8382d07af00afec47017e1fa [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 Harrison09640e632009-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 Harrison09640e632009-02-01 00:45:17 -0800210 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700211 .destroy = ip6_dst_destroy,
212 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000213 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800214 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700215 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700216 .redirect = ip6_rt_blackhole_redirect,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000217 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700218 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700219};
220
David S. Miller62fa8a82011-01-26 20:51:05 -0800221static const u32 ip6_template_metrics[RTAX_MAX] = {
222 [RTAX_HOPLIMIT - 1] = 255,
223};
224
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800225static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700226 .dst = {
227 .__refcnt = ATOMIC_INIT(1),
228 .__use = 1,
229 .obsolete = -1,
230 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700231 .input = ip6_pkt_discard,
232 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 },
234 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700235 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 .rt6i_metric = ~(u32) 0,
237 .rt6i_ref = ATOMIC_INIT(1),
238};
239
Thomas Graf101367c2006-08-04 03:39:02 -0700240#ifdef CONFIG_IPV6_MULTIPLE_TABLES
241
David S. Miller6723ab52006-10-18 21:20:57 -0700242static int ip6_pkt_prohibit(struct sk_buff *skb);
243static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700244
Adrian Bunk280a34c2008-04-21 02:29:32 -0700245static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700246 .dst = {
247 .__refcnt = ATOMIC_INIT(1),
248 .__use = 1,
249 .obsolete = -1,
250 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700251 .input = ip6_pkt_prohibit,
252 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700253 },
254 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700255 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700256 .rt6i_metric = ~(u32) 0,
257 .rt6i_ref = ATOMIC_INIT(1),
258};
259
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800260static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700261 .dst = {
262 .__refcnt = ATOMIC_INIT(1),
263 .__use = 1,
264 .obsolete = -1,
265 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700266 .input = dst_discard,
267 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700268 },
269 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700270 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700271 .rt6i_metric = ~(u32) 0,
272 .rt6i_ref = ATOMIC_INIT(1),
273};
274
275#endif
276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700278static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700279 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700280 int flags,
281 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
David S. Miller97bab732012-06-09 22:36:36 -0700283 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
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 struct rt6_info *ort = NULL;
374
375 if (rt->rt6i_flags & RTF_EXPIRES) {
376 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000377 return true;
Gao feng1716a962012-04-06 00:13:10 +0000378 } else if (rt->dst.from) {
379 ort = (struct rt6_info *) rt->dst.from;
380 return (ort->rt6i_flags & RTF_EXPIRES) &&
381 time_after(jiffies, ort->dst.expires);
382 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000383 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384}
385
Eric Dumazeta50feda2012-05-18 18:57:34 +0000386static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700387{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000388 return ipv6_addr_type(daddr) &
389 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700390}
391
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700393 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 */
395
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800396static inline struct rt6_info *rt6_device_match(struct net *net,
397 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000398 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700400 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
402 struct rt6_info *local = NULL;
403 struct rt6_info *sprt;
404
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900405 if (!oif && ipv6_addr_any(saddr))
406 goto out;
407
Changli Gaod8d1f302010-06-10 23:31:35 -0700408 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500409 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900410
411 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 if (dev->ifindex == oif)
413 return sprt;
414 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500415 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700417 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900419 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 local->rt6i_idev->dev->ifindex == oif))
421 continue;
422 }
423 local = sprt;
424 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900425 } else {
426 if (ipv6_chk_addr(net, saddr, dev,
427 flags & RT6_LOOKUP_F_IFACE))
428 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900430 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900432 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 if (local)
434 return local;
435
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700436 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800437 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900439out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 return rt;
441}
442
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800443#ifdef CONFIG_IPV6_ROUTER_PREF
444static void rt6_probe(struct rt6_info *rt)
445{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000446 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800447 /*
448 * Okay, this does not seem to be appropriate
449 * for now, however, we need to check if it
450 * is really so; aka Router Reachability Probing.
451 *
452 * Router Reachability Probe MUST be rate-limited
453 * to no more than one per minute.
454 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000455 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700456 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800457 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000458 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800459 read_lock_bh(&neigh->lock);
460 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e163562006-03-20 17:05:47 -0800461 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800462 struct in6_addr mcaddr;
463 struct in6_addr *target;
464
465 neigh->updated = jiffies;
466 read_unlock_bh(&neigh->lock);
467
468 target = (struct in6_addr *)&neigh->primary_key;
469 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500470 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000471 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800472 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000473 }
474out:
475 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800476}
477#else
478static inline void rt6_probe(struct rt6_info *rt)
479{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800480}
481#endif
482
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800484 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700486static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
David S. Millerd1918542011-12-28 20:19:20 -0500488 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700489 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800490 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700491 if ((dev->flags & IFF_LOOPBACK) &&
492 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
493 return 1;
494 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495}
496
Dave Jonesb6f99a22007-03-22 12:27:49 -0700497static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000499 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800500 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000501
502 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700503 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700504 if (rt->rt6i_flags & RTF_NONEXTHOP ||
505 !(rt->rt6i_flags & RTF_GATEWAY))
506 m = 1;
507 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800508 read_lock_bh(&neigh->lock);
509 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700510 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800511#ifdef CONFIG_IPV6_ROUTER_PREF
512 else if (neigh->nud_state & NUD_FAILED)
513 m = 0;
514#endif
515 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800516 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800517 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800518 } else
519 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000520 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800521 return m;
522}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800524static int rt6_score_route(struct rt6_info *rt, int oif,
525 int strict)
526{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700527 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900528
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700529 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700530 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800531 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800532#ifdef CONFIG_IPV6_ROUTER_PREF
533 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
534#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700535 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800536 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800537 return -1;
538 return m;
539}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
David S. Millerf11e6652007-03-24 20:36:25 -0700541static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
542 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800543{
David S. Millerf11e6652007-03-24 20:36:25 -0700544 int m;
545
546 if (rt6_check_expired(rt))
547 goto out;
548
549 m = rt6_score_route(rt, oif, strict);
550 if (m < 0)
551 goto out;
552
553 if (m > *mpri) {
554 if (strict & RT6_LOOKUP_F_REACHABLE)
555 rt6_probe(match);
556 *mpri = m;
557 match = rt;
558 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
559 rt6_probe(rt);
560 }
561
562out:
563 return match;
564}
565
566static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
567 struct rt6_info *rr_head,
568 u32 metric, int oif, int strict)
569{
570 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800571 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
David S. Millerf11e6652007-03-24 20:36:25 -0700573 match = NULL;
574 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700575 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700576 match = find_match(rt, oif, strict, &mpri, match);
577 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700578 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700579 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800580
David S. Millerf11e6652007-03-24 20:36:25 -0700581 return match;
582}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800583
David S. Millerf11e6652007-03-24 20:36:25 -0700584static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
585{
586 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800587 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
David S. Millerf11e6652007-03-24 20:36:25 -0700589 rt0 = fn->rr_ptr;
590 if (!rt0)
591 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
David S. Millerf11e6652007-03-24 20:36:25 -0700593 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800595 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700596 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700597 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700598
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800599 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700600 if (!next || next->rt6i_metric != rt0->rt6i_metric)
601 next = fn->leaf;
602
603 if (next != rt0)
604 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 }
606
David S. Millerd1918542011-12-28 20:19:20 -0500607 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000608 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609}
610
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800611#ifdef CONFIG_IPV6_ROUTE_INFO
612int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000613 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800614{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900615 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800616 struct route_info *rinfo = (struct route_info *) opt;
617 struct in6_addr prefix_buf, *prefix;
618 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900619 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800620 struct rt6_info *rt;
621
622 if (len < sizeof(struct route_info)) {
623 return -EINVAL;
624 }
625
626 /* Sanity check for prefix_len and length */
627 if (rinfo->length > 3) {
628 return -EINVAL;
629 } else if (rinfo->prefix_len > 128) {
630 return -EINVAL;
631 } else if (rinfo->prefix_len > 64) {
632 if (rinfo->length < 2) {
633 return -EINVAL;
634 }
635 } else if (rinfo->prefix_len > 0) {
636 if (rinfo->length < 1) {
637 return -EINVAL;
638 }
639 }
640
641 pref = rinfo->route_pref;
642 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000643 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800644
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900645 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800646
647 if (rinfo->length == 3)
648 prefix = (struct in6_addr *)rinfo->prefix;
649 else {
650 /* this function is safe */
651 ipv6_addr_prefix(&prefix_buf,
652 (struct in6_addr *)rinfo->prefix,
653 rinfo->prefix_len);
654 prefix = &prefix_buf;
655 }
656
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800657 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
658 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800659
660 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700661 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800662 rt = NULL;
663 }
664
665 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800666 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800667 pref);
668 else if (rt)
669 rt->rt6i_flags = RTF_ROUTEINFO |
670 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
671
672 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000673 if (!addrconf_finite_timeout(lifetime))
674 rt6_clean_expires(rt);
675 else
676 rt6_set_expires(rt, jiffies + HZ * lifetime);
677
Changli Gaod8d1f302010-06-10 23:31:35 -0700678 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800679 }
680 return 0;
681}
682#endif
683
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800684#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700685do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800686 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700687 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700688 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700689 if (fn->fn_flags & RTN_TL_ROOT) \
690 goto out; \
691 pn = fn->parent; \
692 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800693 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700694 else \
695 fn = pn; \
696 if (fn->fn_flags & RTN_RTINFO) \
697 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700698 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700699 } \
David S. Miller38308472011-12-03 18:02:47 -0500700} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700701
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800702static struct rt6_info *ip6_pol_route_lookup(struct net *net,
703 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500704 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705{
706 struct fib6_node *fn;
707 struct rt6_info *rt;
708
Thomas Grafc71099a2006-08-04 23:20:06 -0700709 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500710 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700711restart:
712 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500713 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
714 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700715out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700716 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700717 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700718 return rt;
719
720}
721
Florian Westphalea6e5742011-09-05 16:05:44 +0200722struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
723 int flags)
724{
725 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
726}
727EXPORT_SYMBOL_GPL(ip6_route_lookup);
728
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900729struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
730 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700731{
David S. Miller4c9483b2011-03-12 16:22:43 -0500732 struct flowi6 fl6 = {
733 .flowi6_oif = oif,
734 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700735 };
736 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700737 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700738
Thomas Grafadaa70b2006-10-13 15:01:03 -0700739 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500740 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700741 flags |= RT6_LOOKUP_F_HAS_SADDR;
742 }
743
David S. Miller4c9483b2011-03-12 16:22:43 -0500744 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700745 if (dst->error == 0)
746 return (struct rt6_info *) dst;
747
748 dst_release(dst);
749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 return NULL;
751}
752
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900753EXPORT_SYMBOL(rt6_lookup);
754
Thomas Grafc71099a2006-08-04 23:20:06 -0700755/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 It takes new route entry, the addition fails by any reason the
757 route is freed. In any case, if caller does not hold it, it may
758 be destroyed.
759 */
760
Thomas Graf86872cb2006-08-22 00:01:08 -0700761static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762{
763 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700764 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Thomas Grafc71099a2006-08-04 23:20:06 -0700766 table = rt->rt6i_table;
767 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700768 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700769 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
771 return err;
772}
773
Thomas Graf40e22e82006-08-22 00:00:45 -0700774int ip6_ins_rt(struct rt6_info *rt)
775{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800776 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500777 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800778 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800779 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700780}
781
Gao feng1716a962012-04-06 00:13:10 +0000782static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000783 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000784 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 struct rt6_info *rt;
787
788 /*
789 * Clone the route.
790 */
791
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000792 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
794 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800795 int attempts = !in_softirq();
796
David S. Miller38308472011-12-03 18:02:47 -0500797 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500798 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000799 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900800 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000801 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900802 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806#ifdef CONFIG_IPV6_SUBTREES
807 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000808 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 rt->rt6i_src.plen = 128;
810 }
811#endif
812
David S. Miller14deae42009-01-04 16:04:39 -0800813 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500814 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500815 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800816 int saved_rt_min_interval =
817 net->ipv6.sysctl.ip6_rt_gc_min_interval;
818 int saved_rt_elasticity =
819 net->ipv6.sysctl.ip6_rt_gc_elasticity;
820
821 if (attempts-- > 0) {
822 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
823 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
824
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000825 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800826
827 net->ipv6.sysctl.ip6_rt_gc_elasticity =
828 saved_rt_elasticity;
829 net->ipv6.sysctl.ip6_rt_gc_min_interval =
830 saved_rt_min_interval;
831 goto retry;
832 }
833
Joe Perchesf3213832012-05-15 14:11:53 +0000834 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700835 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800836 return NULL;
837 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800838 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800840 return rt;
841}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000843static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
844 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800845{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000846 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
847
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800848 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800849 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700850 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800851 }
852 return rt;
853}
854
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800855static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500856 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857{
858 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800859 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700860 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800862 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700863 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700865 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700868 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800870restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500871 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
873restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700874 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800875
David S. Miller4c9483b2011-03-12 16:22:43 -0500876 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800877 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800878 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800879 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Changli Gaod8d1f302010-06-10 23:31:35 -0700881 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700882 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800883
David S. Miller97cac082012-07-02 22:43:47 -0700884 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500885 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800886 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500887 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800888 else
889 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800890
Changli Gaod8d1f302010-06-10 23:31:35 -0700891 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800892 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800893
Changli Gaod8d1f302010-06-10 23:31:35 -0700894 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800895 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700896 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800897 if (!err)
898 goto out2;
899 }
900
901 if (--attempts <= 0)
902 goto out2;
903
904 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700905 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800906 * released someone could insert this route. Relookup.
907 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700908 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800909 goto relookup;
910
911out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800912 if (reachable) {
913 reachable = 0;
914 goto restart_2;
915 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700916 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700917 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700919 rt->dst.lastuse = jiffies;
920 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700921
922 return rt;
923}
924
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800925static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500926 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700927{
David S. Miller4c9483b2011-03-12 16:22:43 -0500928 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700929}
930
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000931static struct dst_entry *ip6_route_input_lookup(struct net *net,
932 struct net_device *dev,
933 struct flowi6 *fl6, int flags)
934{
935 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
936 flags |= RT6_LOOKUP_F_IFACE;
937
938 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
939}
940
Thomas Grafc71099a2006-08-04 23:20:06 -0700941void ip6_route_input(struct sk_buff *skb)
942{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000943 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900944 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700945 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500946 struct flowi6 fl6 = {
947 .flowi6_iif = skb->dev->ifindex,
948 .daddr = iph->daddr,
949 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500950 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500951 .flowi6_mark = skb->mark,
952 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700953 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700954
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000955 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700956}
957
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800958static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500959 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700960{
David S. Miller4c9483b2011-03-12 16:22:43 -0500961 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700962}
963
Florian Westphal9c7a4f9c2011-03-22 19:17:36 -0700964struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500965 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700966{
967 int flags = 0;
968
David McCullough4dc27d1c2012-06-25 15:42:26 +0000969 fl6->flowi6_iif = net->loopback_dev->ifindex;
970
David S. Miller4c9483b2011-03-12 16:22:43 -0500971 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700972 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700973
David S. Miller4c9483b2011-03-12 16:22:43 -0500974 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700975 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000976 else if (sk)
977 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700978
David S. Miller4c9483b2011-03-12 16:22:43 -0500979 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980}
981
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900982EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983
David S. Miller2774c132011-03-01 14:59:04 -0800984struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700985{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700986 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700987 struct dst_entry *new = NULL;
988
David S. Millerf5b0a872012-07-19 12:31:33 -0700989 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700990 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700991 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700992
Steffen Klassert81048912012-07-05 23:37:09 +0000993 memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
994 rt6_init_peer(rt, net->ipv6.peers);
995
David S. Miller14e50e52007-05-24 18:17:54 -0700996 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800997 new->input = dst_discard;
998 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700999
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001000 if (dst_metrics_read_only(&ort->dst))
1001 new->_metrics = ort->dst._metrics;
1002 else
1003 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07001004 rt->rt6i_idev = ort->rt6i_idev;
1005 if (rt->rt6i_idev)
1006 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -07001007
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001008 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001009 rt->rt6i_flags = ort->rt6i_flags;
1010 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001011 rt->rt6i_metric = 0;
1012
1013 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1014#ifdef CONFIG_IPV6_SUBTREES
1015 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1016#endif
1017
1018 dst_free(new);
1019 }
1020
David S. Miller69ead7a2011-03-01 14:45:33 -08001021 dst_release(dst_orig);
1022 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001023}
David S. Miller14e50e52007-05-24 18:17:54 -07001024
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025/*
1026 * Destination cache support functions
1027 */
1028
1029static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1030{
1031 struct rt6_info *rt;
1032
1033 rt = (struct rt6_info *) dst;
1034
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00001035 /* All IPV6 dsts are created with ->obsolete set to the value
1036 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
1037 * into this function always.
1038 */
1039 if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
1040 return NULL;
1041
David S. Miller6431cbc2011-02-07 20:38:06 -08001042 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1043 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001044 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001045 rt6_bind_peer(rt, 0);
1046 rt->rt6i_peer_genid = rt6_peer_genid();
1047 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001049 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 return NULL;
1051}
1052
1053static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1054{
1055 struct rt6_info *rt = (struct rt6_info *) dst;
1056
1057 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001058 if (rt->rt6i_flags & RTF_CACHE) {
1059 if (rt6_check_expired(rt)) {
1060 ip6_del_rt(rt);
1061 dst = NULL;
1062 }
1063 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001065 dst = NULL;
1066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001068 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069}
1070
1071static void ip6_link_failure(struct sk_buff *skb)
1072{
1073 struct rt6_info *rt;
1074
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001075 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
Eric Dumazetadf30902009-06-02 05:19:30 +00001077 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001079 if (rt->rt6i_flags & RTF_CACHE)
1080 rt6_update_expires(rt, 0);
1081 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 rt->rt6i_node->fn_sernum = -1;
1083 }
1084}
1085
David S. Miller6700c272012-07-17 03:29:28 -07001086static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
1087 struct sk_buff *skb, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088{
1089 struct rt6_info *rt6 = (struct rt6_info*)dst;
1090
David S. Miller81aded22012-06-15 14:54:11 -07001091 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001093 struct net *net = dev_net(dst->dev);
1094
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 rt6->rt6i_flags |= RTF_MODIFIED;
1096 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001097 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001099 features |= RTAX_FEATURE_ALLFRAG;
1100 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 }
David S. Millerdefb3512010-12-08 21:16:57 -08001102 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001103 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 }
1105}
1106
David S. Miller42ae66c2012-06-15 20:01:57 -07001107void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1108 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001109{
1110 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1111 struct dst_entry *dst;
1112 struct flowi6 fl6;
1113
1114 memset(&fl6, 0, sizeof(fl6));
1115 fl6.flowi6_oif = oif;
1116 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001117 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001118 fl6.daddr = iph->daddr;
1119 fl6.saddr = iph->saddr;
1120 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1121
1122 dst = ip6_route_output(net, NULL, &fl6);
1123 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001124 ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07001125 dst_release(dst);
1126}
1127EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1128
1129void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1130{
1131 ip6_update_pmtu(skb, sock_net(sk), mtu,
1132 sk->sk_bound_dev_if, sk->sk_mark);
1133}
1134EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1135
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001136void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1137{
1138 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1139 struct dst_entry *dst;
1140 struct flowi6 fl6;
1141
1142 memset(&fl6, 0, sizeof(fl6));
1143 fl6.flowi6_oif = oif;
1144 fl6.flowi6_mark = mark;
1145 fl6.flowi6_flags = 0;
1146 fl6.daddr = iph->daddr;
1147 fl6.saddr = iph->saddr;
1148 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1149
1150 dst = ip6_route_output(net, NULL, &fl6);
1151 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001152 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001153 dst_release(dst);
1154}
1155EXPORT_SYMBOL_GPL(ip6_redirect);
1156
1157void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1158{
1159 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1160}
1161EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1162
David S. Miller0dbaee32010-12-13 12:52:14 -08001163static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164{
David S. Miller0dbaee32010-12-13 12:52:14 -08001165 struct net_device *dev = dst->dev;
1166 unsigned int mtu = dst_mtu(dst);
1167 struct net *net = dev_net(dev);
1168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1170
Daniel Lezcano55786892008-03-04 13:47:47 -08001171 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1172 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173
1174 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001175 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1176 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1177 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 * rely only on pmtu discovery"
1179 */
1180 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1181 mtu = IPV6_MAXPLEN;
1182 return mtu;
1183}
1184
Steffen Klassertebb762f2011-11-23 02:12:51 +00001185static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001186{
David S. Millerd33e4552010-12-14 13:01:14 -08001187 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001188 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1189
1190 if (mtu)
1191 return mtu;
1192
1193 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001194
1195 rcu_read_lock();
1196 idev = __in6_dev_get(dst->dev);
1197 if (idev)
1198 mtu = idev->cnf.mtu6;
1199 rcu_read_unlock();
1200
1201 return mtu;
1202}
1203
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001204static struct dst_entry *icmp6_dst_gc_list;
1205static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001206
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001207struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001209 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210{
David S. Miller87a11572011-12-06 17:04:13 -05001211 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 struct rt6_info *rt;
1213 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001214 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
David S. Miller38308472011-12-03 18:02:47 -05001216 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001217 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
David S. Miller8b96d222012-06-11 02:01:56 -07001219 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001220 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001222 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 goto out;
1224 }
1225
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 if (neigh)
1227 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001228 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001229 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001230 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001231 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001232 dst_free(&rt->dst);
1233 return ERR_CAST(neigh);
1234 }
David S. Miller14deae42009-01-04 16:04:39 -08001235 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001237 rt->dst.flags |= DST_HOST;
1238 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001239 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001240 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001241 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001242 rt->rt6i_dst.plen = 128;
1243 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001244 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001246 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001247 rt->dst.next = icmp6_dst_gc_list;
1248 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001249 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250
Daniel Lezcano55786892008-03-04 13:47:47 -08001251 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
David S. Miller87a11572011-12-06 17:04:13 -05001253 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255out:
David S. Miller87a11572011-12-06 17:04:13 -05001256 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257}
1258
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001259int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260{
Hagen Paul Pfeifere9476e952011-02-25 05:45:19 +00001261 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001262 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001264 spin_lock_bh(&icmp6_dst_lock);
1265 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001266
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 while ((dst = *pprev) != NULL) {
1268 if (!atomic_read(&dst->__refcnt)) {
1269 *pprev = dst->next;
1270 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 } else {
1272 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001273 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 }
1275 }
1276
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001277 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001278
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001279 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280}
1281
David S. Miller1e493d12008-09-10 17:27:15 -07001282static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1283 void *arg)
1284{
1285 struct dst_entry *dst, **pprev;
1286
1287 spin_lock_bh(&icmp6_dst_lock);
1288 pprev = &icmp6_dst_gc_list;
1289 while ((dst = *pprev) != NULL) {
1290 struct rt6_info *rt = (struct rt6_info *) dst;
1291 if (func(rt, arg)) {
1292 *pprev = dst->next;
1293 dst_free(dst);
1294 } else {
1295 pprev = &dst->next;
1296 }
1297 }
1298 spin_unlock_bh(&icmp6_dst_lock);
1299}
1300
Daniel Lezcano569d3642008-01-18 03:56:57 -08001301static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001304 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001305 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1306 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1307 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1308 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1309 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001310 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311
Eric Dumazetfc66f952010-10-08 06:37:34 +00001312 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001313 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001314 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 goto out;
1316
Benjamin Thery6891a342008-03-04 13:49:47 -08001317 net->ipv6.ip6_rt_gc_expire++;
1318 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1319 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001320 entries = dst_entries_get_slow(ops);
1321 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001322 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001324 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001325 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326}
1327
1328/* Clean host part of a prefix. Not necessary in radix tree,
1329 but results in cleaner routing tables.
1330
1331 Remove it only when all the things will work!
1332 */
1333
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001334int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335{
David S. Miller5170ae82010-12-12 21:35:57 -08001336 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001337 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001338 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001339 struct inet6_dev *idev;
1340
1341 rcu_read_lock();
1342 idev = __in6_dev_get(dev);
1343 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001344 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001345 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001346 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001347 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 }
1349 return hoplimit;
1350}
David S. Millerabbf46a2010-12-12 21:14:46 -08001351EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
1353/*
1354 *
1355 */
1356
Thomas Graf86872cb2006-08-22 00:01:08 -07001357int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358{
1359 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001360 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 struct rt6_info *rt = NULL;
1362 struct net_device *dev = NULL;
1363 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001364 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 int addr_type;
1366
Thomas Graf86872cb2006-08-22 00:01:08 -07001367 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 return -EINVAL;
1369#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001370 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 return -EINVAL;
1372#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001373 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001375 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 if (!dev)
1377 goto out;
1378 idev = in6_dev_get(dev);
1379 if (!idev)
1380 goto out;
1381 }
1382
Thomas Graf86872cb2006-08-22 00:01:08 -07001383 if (cfg->fc_metric == 0)
1384 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
Matti Vaittinend71314b2011-11-14 00:14:49 +00001386 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001387 if (cfg->fc_nlinfo.nlh &&
1388 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001389 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001390 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001391 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001392 table = fib6_new_table(net, cfg->fc_table);
1393 }
1394 } else {
1395 table = fib6_new_table(net, cfg->fc_table);
1396 }
David S. Miller38308472011-12-03 18:02:47 -05001397
1398 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001399 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001400
David S. Miller8b96d222012-06-11 02:01:56 -07001401 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
David S. Miller38308472011-12-03 18:02:47 -05001403 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 err = -ENOMEM;
1405 goto out;
1406 }
1407
Gao feng1716a962012-04-06 00:13:10 +00001408 if (cfg->fc_flags & RTF_EXPIRES)
1409 rt6_set_expires(rt, jiffies +
1410 clock_t_to_jiffies(cfg->fc_expires));
1411 else
1412 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
Thomas Graf86872cb2006-08-22 00:01:08 -07001414 if (cfg->fc_protocol == RTPROT_UNSPEC)
1415 cfg->fc_protocol = RTPROT_BOOT;
1416 rt->rt6i_protocol = cfg->fc_protocol;
1417
1418 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
1420 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001421 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001422 else if (cfg->fc_flags & RTF_LOCAL)
1423 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001425 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426
Changli Gaod8d1f302010-06-10 23:31:35 -07001427 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
Thomas Graf86872cb2006-08-22 00:01:08 -07001429 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1430 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001432 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001434 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1435 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1436 if (!metrics) {
1437 err = -ENOMEM;
1438 goto out;
1439 }
1440 dst_init_metrics(&rt->dst, metrics, 0);
1441 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001443 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1444 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445#endif
1446
Thomas Graf86872cb2006-08-22 00:01:08 -07001447 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
1449 /* We cannot add true routes via loopback here,
1450 they would result in kernel looping; promote them to reject routes
1451 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001452 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001453 (dev && (dev->flags & IFF_LOOPBACK) &&
1454 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1455 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001457 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 if (dev) {
1459 dev_put(dev);
1460 in6_dev_put(idev);
1461 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001462 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 dev_hold(dev);
1464 idev = in6_dev_get(dev);
1465 if (!idev) {
1466 err = -ENODEV;
1467 goto out;
1468 }
1469 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001470 rt->dst.output = ip6_pkt_discard_out;
1471 rt->dst.input = ip6_pkt_discard;
1472 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1474 goto install_route;
1475 }
1476
Thomas Graf86872cb2006-08-22 00:01:08 -07001477 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001478 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 int gwa_type;
1480
Thomas Graf86872cb2006-08-22 00:01:08 -07001481 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001482 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 gwa_type = ipv6_addr_type(gw_addr);
1484
1485 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1486 struct rt6_info *grt;
1487
1488 /* IPv6 strictly inhibits using not link-local
1489 addresses as nexthop address.
1490 Otherwise, router will not able to send redirects.
1491 It is very good, but in some (rare!) circumstances
1492 (SIT, PtP, NBMA NOARP links) it is handy to allow
1493 some exceptions. --ANK
1494 */
1495 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001496 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 goto out;
1498
Daniel Lezcano55786892008-03-04 13:47:47 -08001499 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
1501 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001502 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 goto out;
1504 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001505 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001506 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 goto out;
1508 }
1509 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001510 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 idev = grt->rt6i_idev;
1512 dev_hold(dev);
1513 in6_dev_hold(grt->rt6i_idev);
1514 }
David S. Miller38308472011-12-03 18:02:47 -05001515 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001517 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
1519 if (err)
1520 goto out;
1521 }
1522 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001523 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 goto out;
1525 }
1526
1527 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001528 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 goto out;
1530
Daniel Walterc3968a82011-04-13 21:10:57 +00001531 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1532 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1533 err = -EINVAL;
1534 goto out;
1535 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001536 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001537 rt->rt6i_prefsrc.plen = 128;
1538 } else
1539 rt->rt6i_prefsrc.plen = 0;
1540
Thomas Graf86872cb2006-08-22 00:01:08 -07001541 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001542 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001543 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 }
1546
Thomas Graf86872cb2006-08-22 00:01:08 -07001547 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548
1549install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001550 if (cfg->fc_mx) {
1551 struct nlattr *nla;
1552 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Thomas Graf86872cb2006-08-22 00:01:08 -07001554 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001555 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001556
1557 if (type) {
1558 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 err = -EINVAL;
1560 goto out;
1561 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001562
David S. Millerdefb3512010-12-08 21:16:57 -08001563 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 }
1566 }
1567
Changli Gaod8d1f302010-06-10 23:31:35 -07001568 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001570 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001571
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001572 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001573
Thomas Graf86872cb2006-08-22 00:01:08 -07001574 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
1576out:
1577 if (dev)
1578 dev_put(dev);
1579 if (idev)
1580 in6_dev_put(idev);
1581 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001582 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 return err;
1584}
1585
Thomas Graf86872cb2006-08-22 00:01:08 -07001586static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587{
1588 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001589 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001590 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001592 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001593 return -ENOENT;
1594
Thomas Grafc71099a2006-08-04 23:20:06 -07001595 table = rt->rt6i_table;
1596 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
Thomas Graf86872cb2006-08-22 00:01:08 -07001598 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001599 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600
Thomas Grafc71099a2006-08-04 23:20:06 -07001601 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603 return err;
1604}
1605
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001606int ip6_del_rt(struct rt6_info *rt)
1607{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001608 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001609 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001610 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001611 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001612}
1613
Thomas Graf86872cb2006-08-22 00:01:08 -07001614static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615{
Thomas Grafc71099a2006-08-04 23:20:06 -07001616 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 struct fib6_node *fn;
1618 struct rt6_info *rt;
1619 int err = -ESRCH;
1620
Daniel Lezcano55786892008-03-04 13:47:47 -08001621 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001622 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001623 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
Thomas Grafc71099a2006-08-04 23:20:06 -07001625 read_lock_bh(&table->tb6_lock);
1626
1627 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001628 &cfg->fc_dst, cfg->fc_dst_len,
1629 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001630
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001632 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001633 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001634 (!rt->dst.dev ||
1635 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001637 if (cfg->fc_flags & RTF_GATEWAY &&
1638 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001640 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001642 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001643 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
Thomas Graf86872cb2006-08-22 00:01:08 -07001645 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 }
1647 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001648 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
1650 return err;
1651}
1652
David S. Miller6700c272012-07-17 03:29:28 -07001653static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001654{
David S. Millere8599ff2012-07-11 23:43:53 -07001655 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001656 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001657 struct rt6_info *rt, *nrt = NULL;
1658 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001659 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001660 const struct in6_addr *dest;
1661 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001662 struct inet6_dev *in6_dev;
1663 struct neighbour *neigh;
1664 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001665 int optlen, on_link;
1666 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001667
1668 optlen = skb->tail - skb->transport_header;
1669 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1670
1671 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001672 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001673 return;
1674 }
1675
1676 icmph = icmp6_hdr(skb);
1677 target = (const struct in6_addr *) (icmph + 1);
1678 dest = target + 1;
1679
1680 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001681 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001682 return;
1683 }
1684
David S. Miller6e157b62012-07-12 00:05:02 -07001685 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001686 if (ipv6_addr_equal(dest, target)) {
1687 on_link = 1;
1688 } else if (ipv6_addr_type(target) !=
1689 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001690 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001691 return;
1692 }
1693
1694 in6_dev = __in6_dev_get(skb->dev);
1695 if (!in6_dev)
1696 return;
1697 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1698 return;
1699
1700 /* RFC2461 8.1:
1701 * The IP source address of the Redirect MUST be the same as the current
1702 * first-hop router for the specified ICMP Destination Address.
1703 */
1704
1705 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1706 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1707 return;
1708 }
David S. Miller6e157b62012-07-12 00:05:02 -07001709
1710 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001711 if (ndopts.nd_opts_tgt_lladdr) {
1712 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1713 skb->dev);
1714 if (!lladdr) {
1715 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1716 return;
1717 }
1718 }
1719
David S. Miller6e157b62012-07-12 00:05:02 -07001720 rt = (struct rt6_info *) dst;
1721 if (rt == net->ipv6.ip6_null_entry) {
1722 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1723 return;
1724 }
1725
1726 /* Redirect received -> path was valid.
1727 * Look, redirects are sent only in response to data packets,
1728 * so that this nexthop apparently is reachable. --ANK
1729 */
1730 dst_confirm(&rt->dst);
1731
David S. Millere8599ff2012-07-11 23:43:53 -07001732 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1733 if (!neigh)
1734 return;
1735
David S. Miller6e157b62012-07-12 00:05:02 -07001736 /* Duplicate redirect: silently ignore. */
1737 old_neigh = rt->n;
1738 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001739 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 /*
1742 * We have finally decided to accept it.
1743 */
1744
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001745 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1747 NEIGH_UPDATE_F_OVERRIDE|
1748 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1749 NEIGH_UPDATE_F_ISROUTER))
1750 );
1751
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001752 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001753 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 goto out;
1755
1756 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1757 if (on_link)
1758 nrt->rt6i_flags &= ~RTF_GATEWAY;
1759
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001760 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001761 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762
Thomas Graf40e22e82006-08-22 00:00:45 -07001763 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 goto out;
1765
Changli Gaod8d1f302010-06-10 23:31:35 -07001766 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001767 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001768 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001769 netevent.new_neigh = neigh;
1770 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001771 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1772
David S. Miller38308472011-12-03 18:02:47 -05001773 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001774 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001775 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 }
1777
1778out:
David S. Millere8599ff2012-07-11 23:43:53 -07001779 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001780}
1781
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 * Misc support functions
1784 */
1785
Gao feng1716a962012-04-06 00:13:10 +00001786static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001787 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788{
David S. Millerd1918542011-12-28 20:19:20 -05001789 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001790 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1791 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
1793 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001794 rt->dst.input = ort->dst.input;
1795 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001796 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001798 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001799 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001800 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001801 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 rt->rt6i_idev = ort->rt6i_idev;
1803 if (rt->rt6i_idev)
1804 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001805 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001807 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001808 rt->rt6i_flags = ort->rt6i_flags;
1809 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1810 (RTF_DEFAULT | RTF_ADDRCONF))
1811 rt6_set_from(rt, ort);
1812 else
1813 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 rt->rt6i_metric = 0;
1815
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816#ifdef CONFIG_IPV6_SUBTREES
1817 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1818#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001819 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001820 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 }
1822 return rt;
1823}
1824
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001825#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001826static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001827 const struct in6_addr *prefix, int prefixlen,
1828 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001829{
1830 struct fib6_node *fn;
1831 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001832 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001833
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001834 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001835 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001836 return NULL;
1837
1838 write_lock_bh(&table->tb6_lock);
1839 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001840 if (!fn)
1841 goto out;
1842
Changli Gaod8d1f302010-06-10 23:31:35 -07001843 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001844 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001845 continue;
1846 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1847 continue;
1848 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1849 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001850 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001851 break;
1852 }
1853out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001854 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001855 return rt;
1856}
1857
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001858static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001859 const struct in6_addr *prefix, int prefixlen,
1860 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001861 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001862{
Thomas Graf86872cb2006-08-22 00:01:08 -07001863 struct fib6_config cfg = {
1864 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001865 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001866 .fc_ifindex = ifindex,
1867 .fc_dst_len = prefixlen,
1868 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1869 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001870 .fc_nlinfo.pid = 0,
1871 .fc_nlinfo.nlh = NULL,
1872 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001873 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001874
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001875 cfg.fc_dst = *prefix;
1876 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001877
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001878 /* We should treat it as a default route if prefix length is 0. */
1879 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001880 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001881
Thomas Graf86872cb2006-08-22 00:01:08 -07001882 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001883
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001884 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001885}
1886#endif
1887
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001888struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001889{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001891 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001893 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001894 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001895 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
Thomas Grafc71099a2006-08-04 23:20:06 -07001897 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001898 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001899 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001900 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1902 break;
1903 }
1904 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001905 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001906 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 return rt;
1908}
1909
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001910struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001911 struct net_device *dev,
1912 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913{
Thomas Graf86872cb2006-08-22 00:01:08 -07001914 struct fib6_config cfg = {
1915 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001916 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001917 .fc_ifindex = dev->ifindex,
1918 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1919 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001920 .fc_nlinfo.pid = 0,
1921 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001922 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001923 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001925 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926
Thomas Graf86872cb2006-08-22 00:01:08 -07001927 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 return rt6_get_dflt_router(gwaddr, dev);
1930}
1931
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001932void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933{
1934 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001935 struct fib6_table *table;
1936
1937 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001938 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001939 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001940 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941
1942restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001943 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001944 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001946 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001947 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001948 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 goto restart;
1950 }
1951 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001952 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953}
1954
Daniel Lezcano55786892008-03-04 13:47:47 -08001955static void rtmsg_to_fib6_config(struct net *net,
1956 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001957 struct fib6_config *cfg)
1958{
1959 memset(cfg, 0, sizeof(*cfg));
1960
1961 cfg->fc_table = RT6_TABLE_MAIN;
1962 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1963 cfg->fc_metric = rtmsg->rtmsg_metric;
1964 cfg->fc_expires = rtmsg->rtmsg_info;
1965 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1966 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1967 cfg->fc_flags = rtmsg->rtmsg_flags;
1968
Daniel Lezcano55786892008-03-04 13:47:47 -08001969 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001970
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001971 cfg->fc_dst = rtmsg->rtmsg_dst;
1972 cfg->fc_src = rtmsg->rtmsg_src;
1973 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001974}
1975
Daniel Lezcano55786892008-03-04 13:47:47 -08001976int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977{
Thomas Graf86872cb2006-08-22 00:01:08 -07001978 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 struct in6_rtmsg rtmsg;
1980 int err;
1981
1982 switch(cmd) {
1983 case SIOCADDRT: /* Add a route */
1984 case SIOCDELRT: /* Delete a route */
1985 if (!capable(CAP_NET_ADMIN))
1986 return -EPERM;
1987 err = copy_from_user(&rtmsg, arg,
1988 sizeof(struct in6_rtmsg));
1989 if (err)
1990 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001991
Daniel Lezcano55786892008-03-04 13:47:47 -08001992 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001993
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 rtnl_lock();
1995 switch (cmd) {
1996 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001997 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 break;
1999 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002000 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 break;
2002 default:
2003 err = -EINVAL;
2004 }
2005 rtnl_unlock();
2006
2007 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002008 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009
2010 return -EINVAL;
2011}
2012
2013/*
2014 * Drop the packet on the floor
2015 */
2016
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002017static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002019 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002020 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002021 switch (ipstats_mib_noroutes) {
2022 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002023 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002024 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002025 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2026 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002027 break;
2028 }
2029 /* FALLTHROUGH */
2030 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002031 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2032 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002033 break;
2034 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002035 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 kfree_skb(skb);
2037 return 0;
2038}
2039
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002040static int ip6_pkt_discard(struct sk_buff *skb)
2041{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002042 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002043}
2044
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002045static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046{
Eric Dumazetadf30902009-06-02 05:19:30 +00002047 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002048 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049}
2050
David S. Miller6723ab52006-10-18 21:20:57 -07002051#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2052
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002053static int ip6_pkt_prohibit(struct sk_buff *skb)
2054{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002055 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002056}
2057
2058static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2059{
Eric Dumazetadf30902009-06-02 05:19:30 +00002060 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002061 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002062}
2063
David S. Miller6723ab52006-10-18 21:20:57 -07002064#endif
2065
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066/*
2067 * Allocate a dst for local (unicast / anycast) address.
2068 */
2069
2070struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2071 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002072 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002074 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002075 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002076 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
David S. Miller38308472011-12-03 18:02:47 -05002078 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002079 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002081 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 in6_dev_hold(idev);
2084
David S. Miller11d53b42011-06-24 15:23:34 -07002085 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002086 rt->dst.input = ip6_input;
2087 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 rt->rt6i_idev = idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089
2090 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002091 if (anycast)
2092 rt->rt6i_flags |= RTF_ANYCAST;
2093 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002095 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002096 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002097 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002098 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 }
2100
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002101 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002103 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104
Changli Gaod8d1f302010-06-10 23:31:35 -07002105 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106
2107 return rt;
2108}
2109
Daniel Walterc3968a82011-04-13 21:10:57 +00002110int ip6_route_get_saddr(struct net *net,
2111 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002112 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002113 unsigned int prefs,
2114 struct in6_addr *saddr)
2115{
2116 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2117 int err = 0;
2118 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002119 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002120 else
2121 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2122 daddr, prefs, saddr);
2123 return err;
2124}
2125
2126/* remove deleted ip from prefsrc entries */
2127struct arg_dev_net_ip {
2128 struct net_device *dev;
2129 struct net *net;
2130 struct in6_addr *addr;
2131};
2132
2133static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2134{
2135 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2136 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2137 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2138
David S. Millerd1918542011-12-28 20:19:20 -05002139 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002140 rt != net->ipv6.ip6_null_entry &&
2141 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2142 /* remove prefsrc entry */
2143 rt->rt6i_prefsrc.plen = 0;
2144 }
2145 return 0;
2146}
2147
2148void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2149{
2150 struct net *net = dev_net(ifp->idev->dev);
2151 struct arg_dev_net_ip adni = {
2152 .dev = ifp->idev->dev,
2153 .net = net,
2154 .addr = &ifp->addr,
2155 };
2156 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2157}
2158
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002159struct arg_dev_net {
2160 struct net_device *dev;
2161 struct net *net;
2162};
2163
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164static int fib6_ifdown(struct rt6_info *rt, void *arg)
2165{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002166 const struct arg_dev_net *adn = arg;
2167 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002168
David S. Millerd1918542011-12-28 20:19:20 -05002169 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002170 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002172
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 return 0;
2174}
2175
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002176void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002178 struct arg_dev_net adn = {
2179 .dev = dev,
2180 .net = net,
2181 };
2182
2183 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002184 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185}
2186
Eric Dumazet95c96172012-04-15 05:58:06 +00002187struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002189 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190};
2191
2192static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2193{
2194 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2195 struct inet6_dev *idev;
2196
2197 /* In IPv6 pmtu discovery is not optional,
2198 so that RTAX_MTU lock cannot disable it.
2199 We still use this lock to block changes
2200 caused by addrconf/ndisc.
2201 */
2202
2203 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002204 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 return 0;
2206
2207 /* For administrative MTU increase, there is no way to discover
2208 IPv6 PMTU increase, so PMTU increase should be updated here.
2209 Since RFC 1981 doesn't include administrative MTU increase
2210 update PMTU increase is a MUST. (i.e. jumbo frame)
2211 */
2212 /*
2213 If new MTU is less than route PMTU, this new MTU will be the
2214 lowest MTU in the path, update the route PMTU to reflect PMTU
2215 decreases; if new MTU is greater than route PMTU, and the
2216 old MTU is the lowest MTU in the path, update the route PMTU
2217 to reflect the increase. In this case if the other nodes' MTU
2218 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2219 PMTU discouvery.
2220 */
David S. Millerd1918542011-12-28 20:19:20 -05002221 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002222 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2223 (dst_mtu(&rt->dst) >= arg->mtu ||
2224 (dst_mtu(&rt->dst) < arg->mtu &&
2225 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002226 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 return 0;
2229}
2230
Eric Dumazet95c96172012-04-15 05:58:06 +00002231void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232{
Thomas Grafc71099a2006-08-04 23:20:06 -07002233 struct rt6_mtu_change_arg arg = {
2234 .dev = dev,
2235 .mtu = mtu,
2236 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002238 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239}
2240
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002241static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002242 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002243 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002244 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002245 [RTA_PRIORITY] = { .type = NLA_U32 },
2246 [RTA_METRICS] = { .type = NLA_NESTED },
2247};
2248
2249static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2250 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251{
Thomas Graf86872cb2006-08-22 00:01:08 -07002252 struct rtmsg *rtm;
2253 struct nlattr *tb[RTA_MAX+1];
2254 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255
Thomas Graf86872cb2006-08-22 00:01:08 -07002256 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2257 if (err < 0)
2258 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259
Thomas Graf86872cb2006-08-22 00:01:08 -07002260 err = -EINVAL;
2261 rtm = nlmsg_data(nlh);
2262 memset(cfg, 0, sizeof(*cfg));
2263
2264 cfg->fc_table = rtm->rtm_table;
2265 cfg->fc_dst_len = rtm->rtm_dst_len;
2266 cfg->fc_src_len = rtm->rtm_src_len;
2267 cfg->fc_flags = RTF_UP;
2268 cfg->fc_protocol = rtm->rtm_protocol;
2269
2270 if (rtm->rtm_type == RTN_UNREACHABLE)
2271 cfg->fc_flags |= RTF_REJECT;
2272
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002273 if (rtm->rtm_type == RTN_LOCAL)
2274 cfg->fc_flags |= RTF_LOCAL;
2275
Thomas Graf86872cb2006-08-22 00:01:08 -07002276 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2277 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002278 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002279
2280 if (tb[RTA_GATEWAY]) {
2281 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2282 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002284
2285 if (tb[RTA_DST]) {
2286 int plen = (rtm->rtm_dst_len + 7) >> 3;
2287
2288 if (nla_len(tb[RTA_DST]) < plen)
2289 goto errout;
2290
2291 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002293
2294 if (tb[RTA_SRC]) {
2295 int plen = (rtm->rtm_src_len + 7) >> 3;
2296
2297 if (nla_len(tb[RTA_SRC]) < plen)
2298 goto errout;
2299
2300 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002302
Daniel Walterc3968a82011-04-13 21:10:57 +00002303 if (tb[RTA_PREFSRC])
2304 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2305
Thomas Graf86872cb2006-08-22 00:01:08 -07002306 if (tb[RTA_OIF])
2307 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2308
2309 if (tb[RTA_PRIORITY])
2310 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2311
2312 if (tb[RTA_METRICS]) {
2313 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2314 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002316
2317 if (tb[RTA_TABLE])
2318 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2319
2320 err = 0;
2321errout:
2322 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323}
2324
Thomas Grafc127ea22007-03-22 11:58:32 -07002325static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326{
Thomas Graf86872cb2006-08-22 00:01:08 -07002327 struct fib6_config cfg;
2328 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329
Thomas Graf86872cb2006-08-22 00:01:08 -07002330 err = rtm_to_fib6_config(skb, nlh, &cfg);
2331 if (err < 0)
2332 return err;
2333
2334 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335}
2336
Thomas Grafc127ea22007-03-22 11:58:32 -07002337static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338{
Thomas Graf86872cb2006-08-22 00:01:08 -07002339 struct fib6_config cfg;
2340 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341
Thomas Graf86872cb2006-08-22 00:01:08 -07002342 err = rtm_to_fib6_config(skb, nlh, &cfg);
2343 if (err < 0)
2344 return err;
2345
2346 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347}
2348
Thomas Graf339bf982006-11-10 14:10:15 -08002349static inline size_t rt6_nlmsg_size(void)
2350{
2351 return NLMSG_ALIGN(sizeof(struct rtmsg))
2352 + nla_total_size(16) /* RTA_SRC */
2353 + nla_total_size(16) /* RTA_DST */
2354 + nla_total_size(16) /* RTA_GATEWAY */
2355 + nla_total_size(16) /* RTA_PREFSRC */
2356 + nla_total_size(4) /* RTA_TABLE */
2357 + nla_total_size(4) /* RTA_IIF */
2358 + nla_total_size(4) /* RTA_OIF */
2359 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002360 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002361 + nla_total_size(sizeof(struct rta_cacheinfo));
2362}
2363
Brian Haley191cd582008-08-14 15:33:21 -07002364static int rt6_fill_node(struct net *net,
2365 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002366 struct in6_addr *dst, struct in6_addr *src,
2367 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002368 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369{
2370 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002371 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002372 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002373 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002374 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375
2376 if (prefix) { /* user wants prefix routes only */
2377 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2378 /* success since this is not a prefix route */
2379 return 1;
2380 }
2381 }
2382
Thomas Graf2d7202b2006-08-22 00:01:27 -07002383 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002384 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002385 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002386
2387 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 rtm->rtm_family = AF_INET6;
2389 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2390 rtm->rtm_src_len = rt->rt6i_src.plen;
2391 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002392 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002393 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002394 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002395 table = RT6_TABLE_UNSPEC;
2396 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002397 if (nla_put_u32(skb, RTA_TABLE, table))
2398 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002399 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002401 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002402 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002403 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 rtm->rtm_type = RTN_LOCAL;
2405 else
2406 rtm->rtm_type = RTN_UNICAST;
2407 rtm->rtm_flags = 0;
2408 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2409 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002410 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411 rtm->rtm_protocol = RTPROT_REDIRECT;
Denis Ovsienkof0396f602012-07-10 04:45:50 +00002412 else if (rt->rt6i_flags & RTF_ADDRCONF) {
2413 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
2414 rtm->rtm_protocol = RTPROT_RA;
2415 else
2416 rtm->rtm_protocol = RTPROT_KERNEL;
2417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418
David S. Miller38308472011-12-03 18:02:47 -05002419 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 rtm->rtm_flags |= RTM_F_CLONED;
2421
2422 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002423 if (nla_put(skb, RTA_DST, 16, dst))
2424 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002425 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002427 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2428 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429#ifdef CONFIG_IPV6_SUBTREES
2430 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002431 if (nla_put(skb, RTA_SRC, 16, src))
2432 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002433 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002434 } else if (rtm->rtm_src_len &&
2435 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2436 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002438 if (iif) {
2439#ifdef CONFIG_IPV6_MROUTE
2440 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002441 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002442 if (err <= 0) {
2443 if (!nowait) {
2444 if (err == 0)
2445 return 0;
2446 goto nla_put_failure;
2447 } else {
2448 if (err == -EMSGSIZE)
2449 goto nla_put_failure;
2450 }
2451 }
2452 } else
2453#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002454 if (nla_put_u32(skb, RTA_IIF, iif))
2455 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002456 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002458 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2459 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2460 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002462
Daniel Walterc3968a82011-04-13 21:10:57 +00002463 if (rt->rt6i_prefsrc.plen) {
2464 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002465 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002466 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2467 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002468 }
2469
David S. Millerdefb3512010-12-08 21:16:57 -08002470 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002471 goto nla_put_failure;
2472
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002473 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002474 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002475 if (n) {
2476 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2477 rcu_read_unlock();
2478 goto nla_put_failure;
2479 }
2480 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002481 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002482
David S. Millerc78679e2012-04-01 20:27:33 -04002483 if (rt->dst.dev &&
2484 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2485 goto nla_put_failure;
2486 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2487 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00002488
2489 expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002490
David S. Miller87a50692012-07-10 05:06:14 -07002491 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002492 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493
Thomas Graf2d7202b2006-08-22 00:01:27 -07002494 return nlmsg_end(skb, nlh);
2495
2496nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002497 nlmsg_cancel(skb, nlh);
2498 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499}
2500
Patrick McHardy1b43af52006-08-10 23:11:17 -07002501int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502{
2503 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2504 int prefix;
2505
Thomas Graf2d7202b2006-08-22 00:01:27 -07002506 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2507 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2509 } else
2510 prefix = 0;
2511
Brian Haley191cd582008-08-14 15:33:21 -07002512 return rt6_fill_node(arg->net,
2513 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002515 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516}
2517
Thomas Grafc127ea22007-03-22 11:58:32 -07002518static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002520 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002521 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002523 struct sk_buff *skb;
2524 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002525 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002526 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002527
2528 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2529 if (err < 0)
2530 goto errout;
2531
2532 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002533 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002534
2535 if (tb[RTA_SRC]) {
2536 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2537 goto errout;
2538
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002539 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002540 }
2541
2542 if (tb[RTA_DST]) {
2543 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2544 goto errout;
2545
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002546 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002547 }
2548
2549 if (tb[RTA_IIF])
2550 iif = nla_get_u32(tb[RTA_IIF]);
2551
2552 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002553 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002554
2555 if (iif) {
2556 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002557 int flags = 0;
2558
Daniel Lezcano55786892008-03-04 13:47:47 -08002559 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002560 if (!dev) {
2561 err = -ENODEV;
2562 goto errout;
2563 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002564
2565 fl6.flowi6_iif = iif;
2566
2567 if (!ipv6_addr_any(&fl6.saddr))
2568 flags |= RT6_LOOKUP_F_HAS_SADDR;
2569
2570 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2571 flags);
2572 } else {
2573 fl6.flowi6_oif = oif;
2574
2575 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002576 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577
2578 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002579 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002580 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002581 err = -ENOBUFS;
2582 goto errout;
2583 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584
2585 /* Reserve room for dummy headers, this skb can pass
2586 through good chunk of routing engine.
2587 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002588 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2590
Changli Gaod8d1f302010-06-10 23:31:35 -07002591 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592
David S. Miller4c9483b2011-03-12 16:22:43 -05002593 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002595 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002597 kfree_skb(skb);
2598 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599 }
2600
Daniel Lezcano55786892008-03-04 13:47:47 -08002601 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002602errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604}
2605
Thomas Graf86872cb2006-08-22 00:01:08 -07002606void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607{
2608 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002609 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002610 u32 seq;
2611 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002613 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002614 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002615
Thomas Graf339bf982006-11-10 14:10:15 -08002616 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002617 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002618 goto errout;
2619
Brian Haley191cd582008-08-14 15:33:21 -07002620 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002621 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002622 if (err < 0) {
2623 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2624 WARN_ON(err == -EMSGSIZE);
2625 kfree_skb(skb);
2626 goto errout;
2627 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002628 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2629 info->nlh, gfp_any());
2630 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002631errout:
2632 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002633 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634}
2635
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002636static int ip6_route_dev_notify(struct notifier_block *this,
2637 unsigned long event, void *data)
2638{
2639 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002640 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002641
2642 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002643 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002644 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2645#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002646 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002647 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002648 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002649 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2650#endif
2651 }
2652
2653 return NOTIFY_OK;
2654}
2655
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656/*
2657 * /proc
2658 */
2659
2660#ifdef CONFIG_PROC_FS
2661
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662struct rt6_proc_arg
2663{
2664 char *buffer;
2665 int offset;
2666 int length;
2667 int skip;
2668 int len;
2669};
2670
2671static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2672{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002673 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002674 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002676 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677
2678#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002679 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002681 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002683 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002684 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002685 if (n) {
2686 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002688 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002690 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002691 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002692 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2693 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002694 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 return 0;
2696}
2697
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002698static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002700 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002701 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002702 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703}
2704
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002705static int ipv6_route_open(struct inode *inode, struct file *file)
2706{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002707 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002708}
2709
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002710static const struct file_operations ipv6_route_proc_fops = {
2711 .owner = THIS_MODULE,
2712 .open = ipv6_route_open,
2713 .read = seq_read,
2714 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002715 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002716};
2717
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2719{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002720 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002722 net->ipv6.rt6_stats->fib_nodes,
2723 net->ipv6.rt6_stats->fib_route_nodes,
2724 net->ipv6.rt6_stats->fib_rt_alloc,
2725 net->ipv6.rt6_stats->fib_rt_entries,
2726 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002727 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002728 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729
2730 return 0;
2731}
2732
2733static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2734{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002735 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002736}
2737
Arjan van de Ven9a321442007-02-12 00:55:35 -08002738static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739 .owner = THIS_MODULE,
2740 .open = rt6_stats_seq_open,
2741 .read = seq_read,
2742 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002743 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744};
2745#endif /* CONFIG_PROC_FS */
2746
2747#ifdef CONFIG_SYSCTL
2748
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002750int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 void __user *buffer, size_t *lenp, loff_t *ppos)
2752{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002753 struct net *net;
2754 int delay;
2755 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002757
2758 net = (struct net *)ctl->extra1;
2759 delay = net->ipv6.sysctl.flush_delay;
2760 proc_dointvec(ctl, write, buffer, lenp, ppos);
2761 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2762 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763}
2764
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002765ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002766 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002768 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002770 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002771 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 },
2773 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002775 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 .maxlen = sizeof(int),
2777 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002778 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 },
2780 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002782 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 .maxlen = sizeof(int),
2784 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002785 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 },
2787 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002789 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 .maxlen = sizeof(int),
2791 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002792 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 },
2794 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002796 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 .maxlen = sizeof(int),
2798 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002799 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 },
2801 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002803 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 .maxlen = sizeof(int),
2805 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002806 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 },
2808 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002810 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 .maxlen = sizeof(int),
2812 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002813 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 },
2815 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002817 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818 .maxlen = sizeof(int),
2819 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002820 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821 },
2822 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002824 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 .maxlen = sizeof(int),
2826 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002827 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 },
2829 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002831 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 .maxlen = sizeof(int),
2833 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002834 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002836 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837};
2838
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002839struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002840{
2841 struct ctl_table *table;
2842
2843 table = kmemdup(ipv6_route_table_template,
2844 sizeof(ipv6_route_table_template),
2845 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002846
2847 if (table) {
2848 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002849 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002850 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002851 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2852 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2853 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2854 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2855 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2856 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2857 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002858 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002859 }
2860
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002861 return table;
2862}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863#endif
2864
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002865static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002866{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002867 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002868
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002869 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2870 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002871
Eric Dumazetfc66f952010-10-08 06:37:34 +00002872 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2873 goto out_ip6_dst_ops;
2874
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002875 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2876 sizeof(*net->ipv6.ip6_null_entry),
2877 GFP_KERNEL);
2878 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002879 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002880 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002881 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002882 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002883 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2884 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002885
2886#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2887 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2888 sizeof(*net->ipv6.ip6_prohibit_entry),
2889 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002890 if (!net->ipv6.ip6_prohibit_entry)
2891 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002892 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002893 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002894 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002895 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2896 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002897
2898 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2899 sizeof(*net->ipv6.ip6_blk_hole_entry),
2900 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002901 if (!net->ipv6.ip6_blk_hole_entry)
2902 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002903 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002904 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002905 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002906 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2907 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002908#endif
2909
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002910 net->ipv6.sysctl.flush_delay = 0;
2911 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2912 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2913 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2914 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2915 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2916 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2917 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2918
Benjamin Thery6891a342008-03-04 13:49:47 -08002919 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2920
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002921 ret = 0;
2922out:
2923 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002924
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002925#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2926out_ip6_prohibit_entry:
2927 kfree(net->ipv6.ip6_prohibit_entry);
2928out_ip6_null_entry:
2929 kfree(net->ipv6.ip6_null_entry);
2930#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002931out_ip6_dst_entries:
2932 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002933out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002934 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002935}
2936
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002937static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002938{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002939 kfree(net->ipv6.ip6_null_entry);
2940#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2941 kfree(net->ipv6.ip6_prohibit_entry);
2942 kfree(net->ipv6.ip6_blk_hole_entry);
2943#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002944 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002945}
2946
Thomas Grafd1896342012-06-18 12:08:33 +00002947static int __net_init ip6_route_net_init_late(struct net *net)
2948{
2949#ifdef CONFIG_PROC_FS
2950 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2951 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2952#endif
2953 return 0;
2954}
2955
2956static void __net_exit ip6_route_net_exit_late(struct net *net)
2957{
2958#ifdef CONFIG_PROC_FS
2959 proc_net_remove(net, "ipv6_route");
2960 proc_net_remove(net, "rt6_stats");
2961#endif
2962}
2963
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002964static struct pernet_operations ip6_route_net_ops = {
2965 .init = ip6_route_net_init,
2966 .exit = ip6_route_net_exit,
2967};
2968
David S. Millerc3426b42012-06-09 16:27:05 -07002969static int __net_init ipv6_inetpeer_init(struct net *net)
2970{
2971 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2972
2973 if (!bp)
2974 return -ENOMEM;
2975 inet_peer_base_init(bp);
2976 net->ipv6.peers = bp;
2977 return 0;
2978}
2979
2980static void __net_exit ipv6_inetpeer_exit(struct net *net)
2981{
2982 struct inet_peer_base *bp = net->ipv6.peers;
2983
2984 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07002985 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07002986 kfree(bp);
2987}
2988
David S. Miller2b823f72012-06-09 19:00:16 -07002989static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07002990 .init = ipv6_inetpeer_init,
2991 .exit = ipv6_inetpeer_exit,
2992};
2993
Thomas Grafd1896342012-06-18 12:08:33 +00002994static struct pernet_operations ip6_route_net_late_ops = {
2995 .init = ip6_route_net_init_late,
2996 .exit = ip6_route_net_exit_late,
2997};
2998
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002999static struct notifier_block ip6_route_dev_notifier = {
3000 .notifier_call = ip6_route_dev_notify,
3001 .priority = 0,
3002};
3003
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003004int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003006 int ret;
3007
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003008 ret = -ENOMEM;
3009 ip6_dst_ops_template.kmem_cachep =
3010 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3011 SLAB_HWCACHE_ALIGN, NULL);
3012 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003013 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003014
Eric Dumazetfc66f952010-10-08 06:37:34 +00003015 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003016 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003017 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003018
David S. Millerc3426b42012-06-09 16:27:05 -07003019 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3020 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003021 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003022
David S. Miller7e52b332012-06-15 15:51:55 -07003023 ret = register_pernet_subsys(&ip6_route_net_ops);
3024 if (ret)
3025 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003026
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003027 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3028
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003029 /* Registering of the loopback is done before this portion of code,
3030 * the loopback reference in rt6_info will not be taken, do it
3031 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003032 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003033 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3034 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003035 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003036 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003037 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003038 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3039 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003040 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003041 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003042 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003043
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003044 ret = xfrm6_init();
3045 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003046 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003047
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003048 ret = fib6_rules_init();
3049 if (ret)
3050 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003051
Thomas Grafd1896342012-06-18 12:08:33 +00003052 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3053 if (ret)
3054 goto fib6_rules_init;
3055
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003056 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003057 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3058 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3059 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003060 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003061
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003062 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003063 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003064 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003065
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003066out:
3067 return ret;
3068
Thomas Grafd1896342012-06-18 12:08:33 +00003069out_register_late_subsys:
3070 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003071fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003072 fib6_rules_cleanup();
3073xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003074 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003075out_fib6_init:
3076 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003077out_register_subsys:
3078 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003079out_register_inetpeer:
3080 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003081out_dst_entries:
3082 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003083out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003084 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003085 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086}
3087
3088void ip6_route_cleanup(void)
3089{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003090 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003091 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003092 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003094 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003095 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003096 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003097 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003098 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003099}