blob: 412fad809a3bfe9f452d6c86c23f426e718e3ea2 [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);
81static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
David S. Miller6e157b62012-07-12 00:05:02 -070082static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080084#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080085static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000086 const struct in6_addr *prefix, int prefixlen,
87 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +000088 unsigned int pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080089static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000090 const struct in6_addr *prefix, int prefixlen,
91 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080092#endif
93
David S. Miller06582542011-01-27 14:58:42 -080094static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
95{
96 struct rt6_info *rt = (struct rt6_info *) dst;
97 struct inet_peer *peer;
98 u32 *p = NULL;
99
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000100 if (!(rt->dst.flags & DST_HOST))
101 return NULL;
102
David S. Millerfbfe95a2012-06-08 23:24:18 -0700103 peer = rt6_get_peer_create(rt);
David S. Miller06582542011-01-27 14:58:42 -0800104 if (peer) {
105 u32 *old_p = __DST_METRICS_PTR(old);
106 unsigned long prev, new;
107
108 p = peer->metrics;
109 if (inet_metrics_new(peer))
110 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
111
112 new = (unsigned long) p;
113 prev = cmpxchg(&dst->_metrics, old, new);
114
115 if (prev != old) {
116 p = __DST_METRICS_PTR(prev);
117 if (prev & DST_METRICS_READ_ONLY)
118 p = NULL;
119 }
120 }
121 return p;
122}
123
David S. Millerf894cbf2012-07-02 21:52:24 -0700124static inline const void *choose_neigh_daddr(struct rt6_info *rt,
125 struct sk_buff *skb,
126 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500127{
128 struct in6_addr *p = &rt->rt6i_gateway;
129
David S. Millera7563f32012-01-26 16:29:16 -0500130 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500131 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700132 else if (skb)
133 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500134 return daddr;
135}
136
David S. Millerf894cbf2012-07-02 21:52:24 -0700137static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
138 struct sk_buff *skb,
139 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700140{
David S. Miller39232972012-01-26 15:22:32 -0500141 struct rt6_info *rt = (struct rt6_info *) dst;
142 struct neighbour *n;
143
David S. Millerf894cbf2012-07-02 21:52:24 -0700144 daddr = choose_neigh_daddr(rt, skb, daddr);
David S. Miller39232972012-01-26 15:22:32 -0500145 n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500146 if (n)
147 return n;
148 return neigh_create(&nd_tbl, daddr, dst->dev);
149}
150
David S. Miller8ade06c2011-12-29 18:51:57 -0500151static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
David S. Millerf83c7792011-12-28 15:41:23 -0500152{
David S. Miller8ade06c2011-12-29 18:51:57 -0500153 struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
154 if (!n) {
155 n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
156 if (IS_ERR(n))
157 return PTR_ERR(n);
158 }
David S. Miller97cac082012-07-02 22:43:47 -0700159 rt->n = n;
David S. Millerf83c7792011-12-28 15:41:23 -0500160
161 return 0;
David S. Millerd3aaeb32011-07-18 00:40:17 -0700162}
163
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800164static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800166 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 .gc = ip6_dst_gc,
168 .gc_thresh = 1024,
169 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800170 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000171 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800172 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .destroy = ip6_dst_destroy,
174 .ifdown = ip6_dst_ifdown,
175 .negative_advice = ip6_negative_advice,
176 .link_failure = ip6_link_failure,
177 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700178 .redirect = rt6_do_redirect,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700179 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700180 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181};
182
Steffen Klassertebb762f2011-11-23 02:12:51 +0000183static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800184{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000185 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
186
187 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800188}
189
David S. Miller14e50e52007-05-24 18:17:54 -0700190static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
191{
192}
193
David S. Millerb587ee32012-07-12 00:39:24 -0700194static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sk_buff *skb)
195{
196}
197
Held Bernhard0972ddb2011-04-24 22:07:32 +0000198static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
199 unsigned long old)
200{
201 return NULL;
202}
203
David S. Miller14e50e52007-05-24 18:17:54 -0700204static struct dst_ops ip6_dst_blackhole_ops = {
205 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800206 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700207 .destroy = ip6_dst_destroy,
208 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000209 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800210 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700211 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700212 .redirect = ip6_rt_blackhole_redirect,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000213 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700214 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700215};
216
David S. Miller62fa8a82011-01-26 20:51:05 -0800217static const u32 ip6_template_metrics[RTAX_MAX] = {
218 [RTAX_HOPLIMIT - 1] = 255,
219};
220
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800221static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700222 .dst = {
223 .__refcnt = ATOMIC_INIT(1),
224 .__use = 1,
225 .obsolete = -1,
226 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700227 .input = ip6_pkt_discard,
228 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 },
230 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700231 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 .rt6i_metric = ~(u32) 0,
233 .rt6i_ref = ATOMIC_INIT(1),
234};
235
Thomas Graf101367c2006-08-04 03:39:02 -0700236#ifdef CONFIG_IPV6_MULTIPLE_TABLES
237
David S. Miller6723ab52006-10-18 21:20:57 -0700238static int ip6_pkt_prohibit(struct sk_buff *skb);
239static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700240
Adrian Bunk280a34c2008-04-21 02:29:32 -0700241static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700242 .dst = {
243 .__refcnt = ATOMIC_INIT(1),
244 .__use = 1,
245 .obsolete = -1,
246 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700247 .input = ip6_pkt_prohibit,
248 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700249 },
250 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700251 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700252 .rt6i_metric = ~(u32) 0,
253 .rt6i_ref = ATOMIC_INIT(1),
254};
255
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800256static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700257 .dst = {
258 .__refcnt = ATOMIC_INIT(1),
259 .__use = 1,
260 .obsolete = -1,
261 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700262 .input = dst_discard,
263 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700264 },
265 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700266 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700267 .rt6i_metric = ~(u32) 0,
268 .rt6i_ref = ATOMIC_INIT(1),
269};
270
271#endif
272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700274static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700275 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700276 int flags,
277 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278{
David S. Miller97bab732012-06-09 22:36:36 -0700279 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
280 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700281
David S. Miller97bab732012-06-09 22:36:36 -0700282 if (rt) {
Steffen Klassert81048912012-07-05 23:37:09 +0000283 struct dst_entry *dst = &rt->dst;
284
285 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
David S. Miller8b96d222012-06-11 02:01:56 -0700286 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
David S. Miller97bab732012-06-09 22:36:36 -0700287 }
David S. Millercf911662011-04-28 14:31:47 -0700288 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
291static void ip6_dst_destroy(struct dst_entry *dst)
292{
293 struct rt6_info *rt = (struct rt6_info *)dst;
294 struct inet6_dev *idev = rt->rt6i_idev;
295
David S. Miller97cac082012-07-02 22:43:47 -0700296 if (rt->n)
297 neigh_release(rt->n);
298
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000299 if (!(rt->dst.flags & DST_HOST))
300 dst_destroy_metrics_generic(dst);
301
David S. Miller38308472011-12-03 18:02:47 -0500302 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 rt->rt6i_idev = NULL;
304 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900305 }
Gao feng1716a962012-04-06 00:13:10 +0000306
307 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
308 dst_release(dst->from);
309
David S. Miller97bab732012-06-09 22:36:36 -0700310 if (rt6_has_peer(rt)) {
311 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800312 inet_putpeer(peer);
313 }
314}
315
David S. Miller6431cbc2011-02-07 20:38:06 -0800316static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
317
318static u32 rt6_peer_genid(void)
319{
320 return atomic_read(&__rt6_peer_genid);
321}
322
David S. Millerb3419362010-11-30 12:27:11 -0800323void rt6_bind_peer(struct rt6_info *rt, int create)
324{
David S. Miller97bab732012-06-09 22:36:36 -0700325 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800326 struct inet_peer *peer;
327
David S. Miller97bab732012-06-09 22:36:36 -0700328 base = inetpeer_base_ptr(rt->_rt6i_peer);
329 if (!base)
330 return;
331
332 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700333 if (peer) {
334 if (!rt6_set_peer(rt, peer))
335 inet_putpeer(peer);
336 else
337 rt->rt6i_peer_genid = rt6_peer_genid();
338 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339}
340
341static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
342 int how)
343{
344 struct rt6_info *rt = (struct rt6_info *)dst;
345 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800346 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900347 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
David S. Miller97cac082012-07-02 22:43:47 -0700349 if (dev != loopback_dev) {
350 if (idev && idev->dev == dev) {
351 struct inet6_dev *loopback_idev =
352 in6_dev_get(loopback_dev);
353 if (loopback_idev) {
354 rt->rt6i_idev = loopback_idev;
355 in6_dev_put(idev);
356 }
357 }
358 if (rt->n && rt->n->dev == dev) {
359 rt->n->dev = loopback_dev;
360 dev_hold(loopback_dev);
361 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
363 }
364}
365
Eric Dumazeta50feda2012-05-18 18:57:34 +0000366static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367{
Gao feng1716a962012-04-06 00:13:10 +0000368 struct rt6_info *ort = NULL;
369
370 if (rt->rt6i_flags & RTF_EXPIRES) {
371 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000372 return true;
Gao feng1716a962012-04-06 00:13:10 +0000373 } else if (rt->dst.from) {
374 ort = (struct rt6_info *) rt->dst.from;
375 return (ort->rt6i_flags & RTF_EXPIRES) &&
376 time_after(jiffies, ort->dst.expires);
377 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000378 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
Eric Dumazeta50feda2012-05-18 18:57:34 +0000381static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700382{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000383 return ipv6_addr_type(daddr) &
384 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700385}
386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700388 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 */
390
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800391static inline struct rt6_info *rt6_device_match(struct net *net,
392 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000393 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700395 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396{
397 struct rt6_info *local = NULL;
398 struct rt6_info *sprt;
399
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900400 if (!oif && ipv6_addr_any(saddr))
401 goto out;
402
Changli Gaod8d1f302010-06-10 23:31:35 -0700403 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500404 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900405
406 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 if (dev->ifindex == oif)
408 return sprt;
409 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500410 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700412 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900414 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 local->rt6i_idev->dev->ifindex == oif))
416 continue;
417 }
418 local = sprt;
419 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900420 } else {
421 if (ipv6_chk_addr(net, saddr, dev,
422 flags & RT6_LOOKUP_F_IFACE))
423 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900427 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 if (local)
429 return local;
430
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700431 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800432 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900434out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 return rt;
436}
437
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800438#ifdef CONFIG_IPV6_ROUTER_PREF
439static void rt6_probe(struct rt6_info *rt)
440{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000441 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800442 /*
443 * Okay, this does not seem to be appropriate
444 * for now, however, we need to check if it
445 * is really so; aka Router Reachability Probing.
446 *
447 * Router Reachability Probe MUST be rate-limited
448 * to no more than one per minute.
449 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000450 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700451 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800452 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000453 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800454 read_lock_bh(&neigh->lock);
455 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800456 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800457 struct in6_addr mcaddr;
458 struct in6_addr *target;
459
460 neigh->updated = jiffies;
461 read_unlock_bh(&neigh->lock);
462
463 target = (struct in6_addr *)&neigh->primary_key;
464 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500465 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000466 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800467 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000468 }
469out:
470 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800471}
472#else
473static inline void rt6_probe(struct rt6_info *rt)
474{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800475}
476#endif
477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800479 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700481static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
David S. Millerd1918542011-12-28 20:19:20 -0500483 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700484 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800485 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700486 if ((dev->flags & IFF_LOOPBACK) &&
487 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
488 return 1;
489 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490}
491
Dave Jonesb6f99a22007-03-22 12:27:49 -0700492static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000494 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800495 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000496
497 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700498 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700499 if (rt->rt6i_flags & RTF_NONEXTHOP ||
500 !(rt->rt6i_flags & RTF_GATEWAY))
501 m = 1;
502 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800503 read_lock_bh(&neigh->lock);
504 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700505 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800506#ifdef CONFIG_IPV6_ROUTER_PREF
507 else if (neigh->nud_state & NUD_FAILED)
508 m = 0;
509#endif
510 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800511 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800512 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800513 } else
514 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000515 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800516 return m;
517}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800519static int rt6_score_route(struct rt6_info *rt, int oif,
520 int strict)
521{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700522 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900523
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700524 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700525 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800526 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800527#ifdef CONFIG_IPV6_ROUTER_PREF
528 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
529#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700530 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800531 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800532 return -1;
533 return m;
534}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
David S. Millerf11e6652007-03-24 20:36:25 -0700536static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
537 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800538{
David S. Millerf11e6652007-03-24 20:36:25 -0700539 int m;
540
541 if (rt6_check_expired(rt))
542 goto out;
543
544 m = rt6_score_route(rt, oif, strict);
545 if (m < 0)
546 goto out;
547
548 if (m > *mpri) {
549 if (strict & RT6_LOOKUP_F_REACHABLE)
550 rt6_probe(match);
551 *mpri = m;
552 match = rt;
553 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
554 rt6_probe(rt);
555 }
556
557out:
558 return match;
559}
560
561static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
562 struct rt6_info *rr_head,
563 u32 metric, int oif, int strict)
564{
565 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800566 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
David S. Millerf11e6652007-03-24 20:36:25 -0700568 match = NULL;
569 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700570 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700571 match = find_match(rt, oif, strict, &mpri, match);
572 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700573 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700574 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800575
David S. Millerf11e6652007-03-24 20:36:25 -0700576 return match;
577}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800578
David S. Millerf11e6652007-03-24 20:36:25 -0700579static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
580{
581 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800582 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
David S. Millerf11e6652007-03-24 20:36:25 -0700584 rt0 = fn->rr_ptr;
585 if (!rt0)
586 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
David S. Millerf11e6652007-03-24 20:36:25 -0700588 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800590 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700591 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700592 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700593
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800594 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700595 if (!next || next->rt6i_metric != rt0->rt6i_metric)
596 next = fn->leaf;
597
598 if (next != rt0)
599 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 }
601
David S. Millerd1918542011-12-28 20:19:20 -0500602 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000603 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604}
605
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800606#ifdef CONFIG_IPV6_ROUTE_INFO
607int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000608 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800609{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900610 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800611 struct route_info *rinfo = (struct route_info *) opt;
612 struct in6_addr prefix_buf, *prefix;
613 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900614 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800615 struct rt6_info *rt;
616
617 if (len < sizeof(struct route_info)) {
618 return -EINVAL;
619 }
620
621 /* Sanity check for prefix_len and length */
622 if (rinfo->length > 3) {
623 return -EINVAL;
624 } else if (rinfo->prefix_len > 128) {
625 return -EINVAL;
626 } else if (rinfo->prefix_len > 64) {
627 if (rinfo->length < 2) {
628 return -EINVAL;
629 }
630 } else if (rinfo->prefix_len > 0) {
631 if (rinfo->length < 1) {
632 return -EINVAL;
633 }
634 }
635
636 pref = rinfo->route_pref;
637 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000638 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800639
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900640 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800641
642 if (rinfo->length == 3)
643 prefix = (struct in6_addr *)rinfo->prefix;
644 else {
645 /* this function is safe */
646 ipv6_addr_prefix(&prefix_buf,
647 (struct in6_addr *)rinfo->prefix,
648 rinfo->prefix_len);
649 prefix = &prefix_buf;
650 }
651
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800652 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
653 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800654
655 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700656 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800657 rt = NULL;
658 }
659
660 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800661 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800662 pref);
663 else if (rt)
664 rt->rt6i_flags = RTF_ROUTEINFO |
665 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
666
667 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000668 if (!addrconf_finite_timeout(lifetime))
669 rt6_clean_expires(rt);
670 else
671 rt6_set_expires(rt, jiffies + HZ * lifetime);
672
Changli Gaod8d1f302010-06-10 23:31:35 -0700673 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800674 }
675 return 0;
676}
677#endif
678
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800679#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700680do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800681 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700682 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700683 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700684 if (fn->fn_flags & RTN_TL_ROOT) \
685 goto out; \
686 pn = fn->parent; \
687 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800688 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700689 else \
690 fn = pn; \
691 if (fn->fn_flags & RTN_RTINFO) \
692 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700693 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700694 } \
David S. Miller38308472011-12-03 18:02:47 -0500695} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700696
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800697static struct rt6_info *ip6_pol_route_lookup(struct net *net,
698 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500699 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700{
701 struct fib6_node *fn;
702 struct rt6_info *rt;
703
Thomas Grafc71099a2006-08-04 23:20:06 -0700704 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500705 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700706restart:
707 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500708 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
709 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700710out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700711 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700712 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700713 return rt;
714
715}
716
Florian Westphalea6e5742011-09-05 16:05:44 +0200717struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
718 int flags)
719{
720 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
721}
722EXPORT_SYMBOL_GPL(ip6_route_lookup);
723
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900724struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
725 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700726{
David S. Miller4c9483b2011-03-12 16:22:43 -0500727 struct flowi6 fl6 = {
728 .flowi6_oif = oif,
729 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700730 };
731 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700732 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700733
Thomas Grafadaa70b2006-10-13 15:01:03 -0700734 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500735 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700736 flags |= RT6_LOOKUP_F_HAS_SADDR;
737 }
738
David S. Miller4c9483b2011-03-12 16:22:43 -0500739 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700740 if (dst->error == 0)
741 return (struct rt6_info *) dst;
742
743 dst_release(dst);
744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 return NULL;
746}
747
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900748EXPORT_SYMBOL(rt6_lookup);
749
Thomas Grafc71099a2006-08-04 23:20:06 -0700750/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 It takes new route entry, the addition fails by any reason the
752 route is freed. In any case, if caller does not hold it, it may
753 be destroyed.
754 */
755
Thomas Graf86872cb2006-08-22 00:01:08 -0700756static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
758 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700759 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Thomas Grafc71099a2006-08-04 23:20:06 -0700761 table = rt->rt6i_table;
762 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700763 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700764 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 return err;
767}
768
Thomas Graf40e22e82006-08-22 00:00:45 -0700769int ip6_ins_rt(struct rt6_info *rt)
770{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800771 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500772 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800773 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800774 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700775}
776
Gao feng1716a962012-04-06 00:13:10 +0000777static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000778 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000779 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 struct rt6_info *rt;
782
783 /*
784 * Clone the route.
785 */
786
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000787 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
789 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800790 int attempts = !in_softirq();
791
David S. Miller38308472011-12-03 18:02:47 -0500792 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500793 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000794 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900795 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000796 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900797 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801#ifdef CONFIG_IPV6_SUBTREES
802 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000803 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 rt->rt6i_src.plen = 128;
805 }
806#endif
807
David S. Miller14deae42009-01-04 16:04:39 -0800808 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500809 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500810 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800811 int saved_rt_min_interval =
812 net->ipv6.sysctl.ip6_rt_gc_min_interval;
813 int saved_rt_elasticity =
814 net->ipv6.sysctl.ip6_rt_gc_elasticity;
815
816 if (attempts-- > 0) {
817 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
818 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
819
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000820 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800821
822 net->ipv6.sysctl.ip6_rt_gc_elasticity =
823 saved_rt_elasticity;
824 net->ipv6.sysctl.ip6_rt_gc_min_interval =
825 saved_rt_min_interval;
826 goto retry;
827 }
828
Joe Perchesf3213832012-05-15 14:11:53 +0000829 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700830 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800831 return NULL;
832 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800833 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800835 return rt;
836}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000838static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
839 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800840{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000841 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
842
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800843 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800844 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700845 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800846 }
847 return rt;
848}
849
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800850static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500851 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852{
853 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800854 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700855 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800857 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700858 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700860 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
862relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700863 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800865restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500866 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
868restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700869 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800870
David S. Miller4c9483b2011-03-12 16:22:43 -0500871 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800872 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800873 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800874 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
Changli Gaod8d1f302010-06-10 23:31:35 -0700876 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700877 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800878
David S. Miller97cac082012-07-02 22:43:47 -0700879 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500880 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800881 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500882 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800883 else
884 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800885
Changli Gaod8d1f302010-06-10 23:31:35 -0700886 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800887 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800888
Changli Gaod8d1f302010-06-10 23:31:35 -0700889 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800890 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700891 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800892 if (!err)
893 goto out2;
894 }
895
896 if (--attempts <= 0)
897 goto out2;
898
899 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700900 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800901 * released someone could insert this route. Relookup.
902 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700903 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800904 goto relookup;
905
906out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800907 if (reachable) {
908 reachable = 0;
909 goto restart_2;
910 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700911 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700912 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700914 rt->dst.lastuse = jiffies;
915 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700916
917 return rt;
918}
919
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800920static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500921 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700922{
David S. Miller4c9483b2011-03-12 16:22:43 -0500923 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700924}
925
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000926static struct dst_entry *ip6_route_input_lookup(struct net *net,
927 struct net_device *dev,
928 struct flowi6 *fl6, int flags)
929{
930 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
931 flags |= RT6_LOOKUP_F_IFACE;
932
933 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
934}
935
Thomas Grafc71099a2006-08-04 23:20:06 -0700936void ip6_route_input(struct sk_buff *skb)
937{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000938 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900939 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700940 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500941 struct flowi6 fl6 = {
942 .flowi6_iif = skb->dev->ifindex,
943 .daddr = iph->daddr,
944 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500945 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500946 .flowi6_mark = skb->mark,
947 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700948 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700949
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000950 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700951}
952
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800953static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500954 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700955{
David S. Miller4c9483b2011-03-12 16:22:43 -0500956 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700957}
958
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700959struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500960 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700961{
962 int flags = 0;
963
David McCullough4dc27d1c2012-06-25 15:42:26 +0000964 fl6->flowi6_iif = net->loopback_dev->ifindex;
965
David S. Miller4c9483b2011-03-12 16:22:43 -0500966 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700967 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700968
David S. Miller4c9483b2011-03-12 16:22:43 -0500969 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700970 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000971 else if (sk)
972 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700973
David S. Miller4c9483b2011-03-12 16:22:43 -0500974 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975}
976
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900977EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978
David S. Miller2774c132011-03-01 14:59:04 -0800979struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700980{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700981 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700982 struct dst_entry *new = NULL;
983
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700984 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700985 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700986 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700987
Steffen Klassert81048912012-07-05 23:37:09 +0000988 memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
989 rt6_init_peer(rt, net->ipv6.peers);
990
David S. Miller14e50e52007-05-24 18:17:54 -0700991 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800992 new->input = dst_discard;
993 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700994
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000995 if (dst_metrics_read_only(&ort->dst))
996 new->_metrics = ort->dst._metrics;
997 else
998 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700999 rt->rt6i_idev = ort->rt6i_idev;
1000 if (rt->rt6i_idev)
1001 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -07001002
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001003 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001004 rt->rt6i_flags = ort->rt6i_flags;
1005 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001006 rt->rt6i_metric = 0;
1007
1008 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1009#ifdef CONFIG_IPV6_SUBTREES
1010 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1011#endif
1012
1013 dst_free(new);
1014 }
1015
David S. Miller69ead7a2011-03-01 14:45:33 -08001016 dst_release(dst_orig);
1017 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001018}
David S. Miller14e50e52007-05-24 18:17:54 -07001019
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020/*
1021 * Destination cache support functions
1022 */
1023
1024static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1025{
1026 struct rt6_info *rt;
1027
1028 rt = (struct rt6_info *) dst;
1029
David S. Miller6431cbc2011-02-07 20:38:06 -08001030 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1031 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001032 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001033 rt6_bind_peer(rt, 0);
1034 rt->rt6i_peer_genid = rt6_peer_genid();
1035 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001037 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 return NULL;
1039}
1040
1041static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1042{
1043 struct rt6_info *rt = (struct rt6_info *) dst;
1044
1045 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001046 if (rt->rt6i_flags & RTF_CACHE) {
1047 if (rt6_check_expired(rt)) {
1048 ip6_del_rt(rt);
1049 dst = NULL;
1050 }
1051 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001053 dst = NULL;
1054 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001056 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057}
1058
1059static void ip6_link_failure(struct sk_buff *skb)
1060{
1061 struct rt6_info *rt;
1062
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001063 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064
Eric Dumazetadf30902009-06-02 05:19:30 +00001065 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001067 if (rt->rt6i_flags & RTF_CACHE)
1068 rt6_update_expires(rt, 0);
1069 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 rt->rt6i_node->fn_sernum = -1;
1071 }
1072}
1073
1074static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1075{
1076 struct rt6_info *rt6 = (struct rt6_info*)dst;
1077
David S. Miller81aded22012-06-15 14:54:11 -07001078 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001080 struct net *net = dev_net(dst->dev);
1081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 rt6->rt6i_flags |= RTF_MODIFIED;
1083 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001084 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001086 features |= RTAX_FEATURE_ALLFRAG;
1087 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 }
David S. Millerdefb3512010-12-08 21:16:57 -08001089 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001090 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 }
1092}
1093
David S. Miller42ae66c2012-06-15 20:01:57 -07001094void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1095 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001096{
1097 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1098 struct dst_entry *dst;
1099 struct flowi6 fl6;
1100
1101 memset(&fl6, 0, sizeof(fl6));
1102 fl6.flowi6_oif = oif;
1103 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001104 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001105 fl6.daddr = iph->daddr;
1106 fl6.saddr = iph->saddr;
1107 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1108
1109 dst = ip6_route_output(net, NULL, &fl6);
1110 if (!dst->error)
1111 ip6_rt_update_pmtu(dst, ntohl(mtu));
1112 dst_release(dst);
1113}
1114EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1115
1116void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1117{
1118 ip6_update_pmtu(skb, sock_net(sk), mtu,
1119 sk->sk_bound_dev_if, sk->sk_mark);
1120}
1121EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1122
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001123void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1124{
1125 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1126 struct dst_entry *dst;
1127 struct flowi6 fl6;
1128
1129 memset(&fl6, 0, sizeof(fl6));
1130 fl6.flowi6_oif = oif;
1131 fl6.flowi6_mark = mark;
1132 fl6.flowi6_flags = 0;
1133 fl6.daddr = iph->daddr;
1134 fl6.saddr = iph->saddr;
1135 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1136
1137 dst = ip6_route_output(net, NULL, &fl6);
1138 if (!dst->error)
1139 rt6_do_redirect(dst, skb);
1140 dst_release(dst);
1141}
1142EXPORT_SYMBOL_GPL(ip6_redirect);
1143
1144void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1145{
1146 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1147}
1148EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1149
David S. Miller0dbaee32010-12-13 12:52:14 -08001150static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151{
David S. Miller0dbaee32010-12-13 12:52:14 -08001152 struct net_device *dev = dst->dev;
1153 unsigned int mtu = dst_mtu(dst);
1154 struct net *net = dev_net(dev);
1155
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1157
Daniel Lezcano55786892008-03-04 13:47:47 -08001158 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1159 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
1161 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001162 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1163 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1164 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 * rely only on pmtu discovery"
1166 */
1167 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1168 mtu = IPV6_MAXPLEN;
1169 return mtu;
1170}
1171
Steffen Klassertebb762f2011-11-23 02:12:51 +00001172static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001173{
David S. Millerd33e4552010-12-14 13:01:14 -08001174 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001175 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1176
1177 if (mtu)
1178 return mtu;
1179
1180 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001181
1182 rcu_read_lock();
1183 idev = __in6_dev_get(dst->dev);
1184 if (idev)
1185 mtu = idev->cnf.mtu6;
1186 rcu_read_unlock();
1187
1188 return mtu;
1189}
1190
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001191static struct dst_entry *icmp6_dst_gc_list;
1192static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001193
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001194struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001196 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197{
David S. Miller87a11572011-12-06 17:04:13 -05001198 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 struct rt6_info *rt;
1200 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001201 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
David S. Miller38308472011-12-03 18:02:47 -05001203 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001204 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
David S. Miller8b96d222012-06-11 02:01:56 -07001206 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001207 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001209 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 goto out;
1211 }
1212
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 if (neigh)
1214 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001215 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001216 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001217 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001218 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001219 dst_free(&rt->dst);
1220 return ERR_CAST(neigh);
1221 }
David S. Miller14deae42009-01-04 16:04:39 -08001222 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001224 rt->dst.flags |= DST_HOST;
1225 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001226 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001227 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001228 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001229 rt->rt6i_dst.plen = 128;
1230 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001231 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001233 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001234 rt->dst.next = icmp6_dst_gc_list;
1235 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001236 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
Daniel Lezcano55786892008-03-04 13:47:47 -08001238 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
David S. Miller87a11572011-12-06 17:04:13 -05001240 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1241
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242out:
David S. Miller87a11572011-12-06 17:04:13 -05001243 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244}
1245
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001246int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001248 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001249 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001251 spin_lock_bh(&icmp6_dst_lock);
1252 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001253
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 while ((dst = *pprev) != NULL) {
1255 if (!atomic_read(&dst->__refcnt)) {
1256 *pprev = dst->next;
1257 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 } else {
1259 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001260 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 }
1262 }
1263
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001264 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001265
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001266 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267}
1268
David S. Miller1e493d12008-09-10 17:27:15 -07001269static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1270 void *arg)
1271{
1272 struct dst_entry *dst, **pprev;
1273
1274 spin_lock_bh(&icmp6_dst_lock);
1275 pprev = &icmp6_dst_gc_list;
1276 while ((dst = *pprev) != NULL) {
1277 struct rt6_info *rt = (struct rt6_info *) dst;
1278 if (func(rt, arg)) {
1279 *pprev = dst->next;
1280 dst_free(dst);
1281 } else {
1282 pprev = &dst->next;
1283 }
1284 }
1285 spin_unlock_bh(&icmp6_dst_lock);
1286}
1287
Daniel Lezcano569d3642008-01-18 03:56:57 -08001288static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001291 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001292 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1293 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1294 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1295 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1296 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001297 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
Eric Dumazetfc66f952010-10-08 06:37:34 +00001299 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001300 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001301 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 goto out;
1303
Benjamin Thery6891a342008-03-04 13:49:47 -08001304 net->ipv6.ip6_rt_gc_expire++;
1305 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1306 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001307 entries = dst_entries_get_slow(ops);
1308 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001309 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001311 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001312 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313}
1314
1315/* Clean host part of a prefix. Not necessary in radix tree,
1316 but results in cleaner routing tables.
1317
1318 Remove it only when all the things will work!
1319 */
1320
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001321int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322{
David S. Miller5170ae82010-12-12 21:35:57 -08001323 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001324 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001325 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001326 struct inet6_dev *idev;
1327
1328 rcu_read_lock();
1329 idev = __in6_dev_get(dev);
1330 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001331 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001332 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001333 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001334 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 }
1336 return hoplimit;
1337}
David S. Millerabbf46a2010-12-12 21:14:46 -08001338EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340/*
1341 *
1342 */
1343
Thomas Graf86872cb2006-08-22 00:01:08 -07001344int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345{
1346 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001347 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 struct rt6_info *rt = NULL;
1349 struct net_device *dev = NULL;
1350 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001351 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 int addr_type;
1353
Thomas Graf86872cb2006-08-22 00:01:08 -07001354 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 return -EINVAL;
1356#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001357 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 return -EINVAL;
1359#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001360 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001362 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 if (!dev)
1364 goto out;
1365 idev = in6_dev_get(dev);
1366 if (!idev)
1367 goto out;
1368 }
1369
Thomas Graf86872cb2006-08-22 00:01:08 -07001370 if (cfg->fc_metric == 0)
1371 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Matti Vaittinend71314b2011-11-14 00:14:49 +00001373 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001374 if (cfg->fc_nlinfo.nlh &&
1375 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001376 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001377 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001378 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001379 table = fib6_new_table(net, cfg->fc_table);
1380 }
1381 } else {
1382 table = fib6_new_table(net, cfg->fc_table);
1383 }
David S. Miller38308472011-12-03 18:02:47 -05001384
1385 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001386 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001387
David S. Miller8b96d222012-06-11 02:01:56 -07001388 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
David S. Miller38308472011-12-03 18:02:47 -05001390 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 err = -ENOMEM;
1392 goto out;
1393 }
1394
Changli Gaod8d1f302010-06-10 23:31:35 -07001395 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001396
1397 if (cfg->fc_flags & RTF_EXPIRES)
1398 rt6_set_expires(rt, jiffies +
1399 clock_t_to_jiffies(cfg->fc_expires));
1400 else
1401 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
Thomas Graf86872cb2006-08-22 00:01:08 -07001403 if (cfg->fc_protocol == RTPROT_UNSPEC)
1404 cfg->fc_protocol = RTPROT_BOOT;
1405 rt->rt6i_protocol = cfg->fc_protocol;
1406
1407 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
1409 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001410 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001411 else if (cfg->fc_flags & RTF_LOCAL)
1412 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001414 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415
Changli Gaod8d1f302010-06-10 23:31:35 -07001416 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
Thomas Graf86872cb2006-08-22 00:01:08 -07001418 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1419 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001421 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001423 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1424 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1425 if (!metrics) {
1426 err = -ENOMEM;
1427 goto out;
1428 }
1429 dst_init_metrics(&rt->dst, metrics, 0);
1430 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001432 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1433 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434#endif
1435
Thomas Graf86872cb2006-08-22 00:01:08 -07001436 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437
1438 /* We cannot add true routes via loopback here,
1439 they would result in kernel looping; promote them to reject routes
1440 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001441 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001442 (dev && (dev->flags & IFF_LOOPBACK) &&
1443 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1444 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001446 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 if (dev) {
1448 dev_put(dev);
1449 in6_dev_put(idev);
1450 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001451 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 dev_hold(dev);
1453 idev = in6_dev_get(dev);
1454 if (!idev) {
1455 err = -ENODEV;
1456 goto out;
1457 }
1458 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001459 rt->dst.output = ip6_pkt_discard_out;
1460 rt->dst.input = ip6_pkt_discard;
1461 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1463 goto install_route;
1464 }
1465
Thomas Graf86872cb2006-08-22 00:01:08 -07001466 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001467 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 int gwa_type;
1469
Thomas Graf86872cb2006-08-22 00:01:08 -07001470 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001471 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 gwa_type = ipv6_addr_type(gw_addr);
1473
1474 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1475 struct rt6_info *grt;
1476
1477 /* IPv6 strictly inhibits using not link-local
1478 addresses as nexthop address.
1479 Otherwise, router will not able to send redirects.
1480 It is very good, but in some (rare!) circumstances
1481 (SIT, PtP, NBMA NOARP links) it is handy to allow
1482 some exceptions. --ANK
1483 */
1484 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001485 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 goto out;
1487
Daniel Lezcano55786892008-03-04 13:47:47 -08001488 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489
1490 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001491 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 goto out;
1493 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001494 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001495 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 goto out;
1497 }
1498 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001499 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 idev = grt->rt6i_idev;
1501 dev_hold(dev);
1502 in6_dev_hold(grt->rt6i_idev);
1503 }
David S. Miller38308472011-12-03 18:02:47 -05001504 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001506 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507
1508 if (err)
1509 goto out;
1510 }
1511 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001512 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 goto out;
1514 }
1515
1516 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001517 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 goto out;
1519
Daniel Walterc3968a82011-04-13 21:10:57 +00001520 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1521 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1522 err = -EINVAL;
1523 goto out;
1524 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001525 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001526 rt->rt6i_prefsrc.plen = 128;
1527 } else
1528 rt->rt6i_prefsrc.plen = 0;
1529
Thomas Graf86872cb2006-08-22 00:01:08 -07001530 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001531 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001532 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 }
1535
Thomas Graf86872cb2006-08-22 00:01:08 -07001536 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537
1538install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001539 if (cfg->fc_mx) {
1540 struct nlattr *nla;
1541 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542
Thomas Graf86872cb2006-08-22 00:01:08 -07001543 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001544 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001545
1546 if (type) {
1547 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 err = -EINVAL;
1549 goto out;
1550 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001551
David S. Millerdefb3512010-12-08 21:16:57 -08001552 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 }
1555 }
1556
Changli Gaod8d1f302010-06-10 23:31:35 -07001557 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001559 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001560
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001561 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001562
Thomas Graf86872cb2006-08-22 00:01:08 -07001563 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564
1565out:
1566 if (dev)
1567 dev_put(dev);
1568 if (idev)
1569 in6_dev_put(idev);
1570 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001571 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 return err;
1573}
1574
Thomas Graf86872cb2006-08-22 00:01:08 -07001575static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576{
1577 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001578 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001579 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001581 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001582 return -ENOENT;
1583
Thomas Grafc71099a2006-08-04 23:20:06 -07001584 table = rt->rt6i_table;
1585 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Thomas Graf86872cb2006-08-22 00:01:08 -07001587 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001588 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
Thomas Grafc71099a2006-08-04 23:20:06 -07001590 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
1592 return err;
1593}
1594
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001595int ip6_del_rt(struct rt6_info *rt)
1596{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001597 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001598 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001599 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001600 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001601}
1602
Thomas Graf86872cb2006-08-22 00:01:08 -07001603static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604{
Thomas Grafc71099a2006-08-04 23:20:06 -07001605 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 struct fib6_node *fn;
1607 struct rt6_info *rt;
1608 int err = -ESRCH;
1609
Daniel Lezcano55786892008-03-04 13:47:47 -08001610 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001611 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001612 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Thomas Grafc71099a2006-08-04 23:20:06 -07001614 read_lock_bh(&table->tb6_lock);
1615
1616 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001617 &cfg->fc_dst, cfg->fc_dst_len,
1618 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001619
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001621 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001622 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001623 (!rt->dst.dev ||
1624 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001626 if (cfg->fc_flags & RTF_GATEWAY &&
1627 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001629 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001631 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001632 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633
Thomas Graf86872cb2006-08-22 00:01:08 -07001634 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 }
1636 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001637 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
1639 return err;
1640}
1641
David S. Miller6e157b62012-07-12 00:05:02 -07001642static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001643{
David S. Millere8599ff2012-07-11 23:43:53 -07001644 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001645 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001646 struct rt6_info *rt, *nrt = NULL;
1647 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001648 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001649 const struct in6_addr *dest;
1650 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001651 struct inet6_dev *in6_dev;
1652 struct neighbour *neigh;
1653 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001654 int optlen, on_link;
1655 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001656
1657 optlen = skb->tail - skb->transport_header;
1658 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1659
1660 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001661 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001662 return;
1663 }
1664
1665 icmph = icmp6_hdr(skb);
1666 target = (const struct in6_addr *) (icmph + 1);
1667 dest = target + 1;
1668
1669 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001670 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001671 return;
1672 }
1673
David S. Miller6e157b62012-07-12 00:05:02 -07001674 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001675 if (ipv6_addr_equal(dest, target)) {
1676 on_link = 1;
1677 } else if (ipv6_addr_type(target) !=
1678 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001679 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001680 return;
1681 }
1682
1683 in6_dev = __in6_dev_get(skb->dev);
1684 if (!in6_dev)
1685 return;
1686 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1687 return;
1688
1689 /* RFC2461 8.1:
1690 * The IP source address of the Redirect MUST be the same as the current
1691 * first-hop router for the specified ICMP Destination Address.
1692 */
1693
1694 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1695 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1696 return;
1697 }
David S. Miller6e157b62012-07-12 00:05:02 -07001698
1699 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001700 if (ndopts.nd_opts_tgt_lladdr) {
1701 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1702 skb->dev);
1703 if (!lladdr) {
1704 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1705 return;
1706 }
1707 }
1708
David S. Miller6e157b62012-07-12 00:05:02 -07001709 rt = (struct rt6_info *) dst;
1710 if (rt == net->ipv6.ip6_null_entry) {
1711 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1712 return;
1713 }
1714
1715 /* Redirect received -> path was valid.
1716 * Look, redirects are sent only in response to data packets,
1717 * so that this nexthop apparently is reachable. --ANK
1718 */
1719 dst_confirm(&rt->dst);
1720
David S. Millere8599ff2012-07-11 23:43:53 -07001721 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1722 if (!neigh)
1723 return;
1724
David S. Miller6e157b62012-07-12 00:05:02 -07001725 /* Duplicate redirect: silently ignore. */
1726 old_neigh = rt->n;
1727 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001728 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 /*
1731 * We have finally decided to accept it.
1732 */
1733
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001734 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1736 NEIGH_UPDATE_F_OVERRIDE|
1737 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1738 NEIGH_UPDATE_F_ISROUTER))
1739 );
1740
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001741 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001742 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 goto out;
1744
1745 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1746 if (on_link)
1747 nrt->rt6i_flags &= ~RTF_GATEWAY;
1748
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001749 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001750 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751
Thomas Graf40e22e82006-08-22 00:00:45 -07001752 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 goto out;
1754
Changli Gaod8d1f302010-06-10 23:31:35 -07001755 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001756 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001757 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001758 netevent.new_neigh = neigh;
1759 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001760 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1761
David S. Miller38308472011-12-03 18:02:47 -05001762 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001763 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001764 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765 }
1766
1767out:
David S. Millere8599ff2012-07-11 23:43:53 -07001768 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001769}
1770
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 * Misc support functions
1773 */
1774
Gao feng1716a962012-04-06 00:13:10 +00001775static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001776 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777{
David S. Millerd1918542011-12-28 20:19:20 -05001778 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001779 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1780 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
1782 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001783 rt->dst.input = ort->dst.input;
1784 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001785 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001787 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001788 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001789 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001790 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 rt->rt6i_idev = ort->rt6i_idev;
1792 if (rt->rt6i_idev)
1793 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001794 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001796 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001797 rt->rt6i_flags = ort->rt6i_flags;
1798 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1799 (RTF_DEFAULT | RTF_ADDRCONF))
1800 rt6_set_from(rt, ort);
1801 else
1802 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 rt->rt6i_metric = 0;
1804
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805#ifdef CONFIG_IPV6_SUBTREES
1806 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1807#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001808 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001809 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 }
1811 return rt;
1812}
1813
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001814#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001815static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001816 const struct in6_addr *prefix, int prefixlen,
1817 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001818{
1819 struct fib6_node *fn;
1820 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001821 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001822
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001823 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001824 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001825 return NULL;
1826
1827 write_lock_bh(&table->tb6_lock);
1828 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001829 if (!fn)
1830 goto out;
1831
Changli Gaod8d1f302010-06-10 23:31:35 -07001832 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001833 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001834 continue;
1835 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1836 continue;
1837 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1838 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001839 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001840 break;
1841 }
1842out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001843 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001844 return rt;
1845}
1846
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001847static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001848 const struct in6_addr *prefix, int prefixlen,
1849 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001850 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001851{
Thomas Graf86872cb2006-08-22 00:01:08 -07001852 struct fib6_config cfg = {
1853 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001854 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001855 .fc_ifindex = ifindex,
1856 .fc_dst_len = prefixlen,
1857 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1858 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001859 .fc_nlinfo.pid = 0,
1860 .fc_nlinfo.nlh = NULL,
1861 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001862 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001863
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001864 cfg.fc_dst = *prefix;
1865 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001866
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001867 /* We should treat it as a default route if prefix length is 0. */
1868 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001869 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001870
Thomas Graf86872cb2006-08-22 00:01:08 -07001871 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001872
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001873 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001874}
1875#endif
1876
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001877struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001878{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001880 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001882 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001883 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001884 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885
Thomas Grafc71099a2006-08-04 23:20:06 -07001886 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001887 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001888 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001889 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1891 break;
1892 }
1893 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001894 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001895 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 return rt;
1897}
1898
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001899struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001900 struct net_device *dev,
1901 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902{
Thomas Graf86872cb2006-08-22 00:01:08 -07001903 struct fib6_config cfg = {
1904 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001905 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001906 .fc_ifindex = dev->ifindex,
1907 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1908 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001909 .fc_nlinfo.pid = 0,
1910 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001911 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001912 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001914 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915
Thomas Graf86872cb2006-08-22 00:01:08 -07001916 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 return rt6_get_dflt_router(gwaddr, dev);
1919}
1920
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001921void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922{
1923 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001924 struct fib6_table *table;
1925
1926 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001927 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001928 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001929 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
1931restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001932 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001933 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001935 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001936 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001937 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 goto restart;
1939 }
1940 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001941 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942}
1943
Daniel Lezcano55786892008-03-04 13:47:47 -08001944static void rtmsg_to_fib6_config(struct net *net,
1945 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001946 struct fib6_config *cfg)
1947{
1948 memset(cfg, 0, sizeof(*cfg));
1949
1950 cfg->fc_table = RT6_TABLE_MAIN;
1951 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1952 cfg->fc_metric = rtmsg->rtmsg_metric;
1953 cfg->fc_expires = rtmsg->rtmsg_info;
1954 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1955 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1956 cfg->fc_flags = rtmsg->rtmsg_flags;
1957
Daniel Lezcano55786892008-03-04 13:47:47 -08001958 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001959
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001960 cfg->fc_dst = rtmsg->rtmsg_dst;
1961 cfg->fc_src = rtmsg->rtmsg_src;
1962 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001963}
1964
Daniel Lezcano55786892008-03-04 13:47:47 -08001965int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966{
Thomas Graf86872cb2006-08-22 00:01:08 -07001967 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968 struct in6_rtmsg rtmsg;
1969 int err;
1970
1971 switch(cmd) {
1972 case SIOCADDRT: /* Add a route */
1973 case SIOCDELRT: /* Delete a route */
1974 if (!capable(CAP_NET_ADMIN))
1975 return -EPERM;
1976 err = copy_from_user(&rtmsg, arg,
1977 sizeof(struct in6_rtmsg));
1978 if (err)
1979 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001980
Daniel Lezcano55786892008-03-04 13:47:47 -08001981 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001982
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 rtnl_lock();
1984 switch (cmd) {
1985 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001986 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 break;
1988 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001989 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 break;
1991 default:
1992 err = -EINVAL;
1993 }
1994 rtnl_unlock();
1995
1996 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001997 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998
1999 return -EINVAL;
2000}
2001
2002/*
2003 * Drop the packet on the floor
2004 */
2005
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002006static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002008 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002009 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002010 switch (ipstats_mib_noroutes) {
2011 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002012 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002013 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002014 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2015 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002016 break;
2017 }
2018 /* FALLTHROUGH */
2019 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002020 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2021 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002022 break;
2023 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002024 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 kfree_skb(skb);
2026 return 0;
2027}
2028
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002029static int ip6_pkt_discard(struct sk_buff *skb)
2030{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002031 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002032}
2033
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002034static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035{
Eric Dumazetadf30902009-06-02 05:19:30 +00002036 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002037 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038}
2039
David S. Miller6723ab52006-10-18 21:20:57 -07002040#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2041
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002042static int ip6_pkt_prohibit(struct sk_buff *skb)
2043{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002044 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002045}
2046
2047static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2048{
Eric Dumazetadf30902009-06-02 05:19:30 +00002049 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002050 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002051}
2052
David S. Miller6723ab52006-10-18 21:20:57 -07002053#endif
2054
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055/*
2056 * Allocate a dst for local (unicast / anycast) address.
2057 */
2058
2059struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2060 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002061 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002063 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002064 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002065 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066
David S. Miller38308472011-12-03 18:02:47 -05002067 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002068 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002070 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 in6_dev_hold(idev);
2073
David S. Miller11d53b42011-06-24 15:23:34 -07002074 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002075 rt->dst.input = ip6_input;
2076 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002078 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079
2080 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002081 if (anycast)
2082 rt->rt6i_flags |= RTF_ANYCAST;
2083 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002085 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002086 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002087 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002088 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 }
2090
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002091 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002093 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094
Changli Gaod8d1f302010-06-10 23:31:35 -07002095 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096
2097 return rt;
2098}
2099
Daniel Walterc3968a82011-04-13 21:10:57 +00002100int ip6_route_get_saddr(struct net *net,
2101 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002102 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002103 unsigned int prefs,
2104 struct in6_addr *saddr)
2105{
2106 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2107 int err = 0;
2108 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002109 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002110 else
2111 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2112 daddr, prefs, saddr);
2113 return err;
2114}
2115
2116/* remove deleted ip from prefsrc entries */
2117struct arg_dev_net_ip {
2118 struct net_device *dev;
2119 struct net *net;
2120 struct in6_addr *addr;
2121};
2122
2123static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2124{
2125 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2126 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2127 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2128
David S. Millerd1918542011-12-28 20:19:20 -05002129 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002130 rt != net->ipv6.ip6_null_entry &&
2131 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2132 /* remove prefsrc entry */
2133 rt->rt6i_prefsrc.plen = 0;
2134 }
2135 return 0;
2136}
2137
2138void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2139{
2140 struct net *net = dev_net(ifp->idev->dev);
2141 struct arg_dev_net_ip adni = {
2142 .dev = ifp->idev->dev,
2143 .net = net,
2144 .addr = &ifp->addr,
2145 };
2146 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2147}
2148
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002149struct arg_dev_net {
2150 struct net_device *dev;
2151 struct net *net;
2152};
2153
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154static int fib6_ifdown(struct rt6_info *rt, void *arg)
2155{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002156 const struct arg_dev_net *adn = arg;
2157 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002158
David S. Millerd1918542011-12-28 20:19:20 -05002159 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002160 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002162
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 return 0;
2164}
2165
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002166void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002168 struct arg_dev_net adn = {
2169 .dev = dev,
2170 .net = net,
2171 };
2172
2173 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002174 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175}
2176
Eric Dumazet95c96172012-04-15 05:58:06 +00002177struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002179 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180};
2181
2182static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2183{
2184 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2185 struct inet6_dev *idev;
2186
2187 /* In IPv6 pmtu discovery is not optional,
2188 so that RTAX_MTU lock cannot disable it.
2189 We still use this lock to block changes
2190 caused by addrconf/ndisc.
2191 */
2192
2193 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002194 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 return 0;
2196
2197 /* For administrative MTU increase, there is no way to discover
2198 IPv6 PMTU increase, so PMTU increase should be updated here.
2199 Since RFC 1981 doesn't include administrative MTU increase
2200 update PMTU increase is a MUST. (i.e. jumbo frame)
2201 */
2202 /*
2203 If new MTU is less than route PMTU, this new MTU will be the
2204 lowest MTU in the path, update the route PMTU to reflect PMTU
2205 decreases; if new MTU is greater than route PMTU, and the
2206 old MTU is the lowest MTU in the path, update the route PMTU
2207 to reflect the increase. In this case if the other nodes' MTU
2208 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2209 PMTU discouvery.
2210 */
David S. Millerd1918542011-12-28 20:19:20 -05002211 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002212 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2213 (dst_mtu(&rt->dst) >= arg->mtu ||
2214 (dst_mtu(&rt->dst) < arg->mtu &&
2215 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002216 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002217 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 return 0;
2219}
2220
Eric Dumazet95c96172012-04-15 05:58:06 +00002221void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222{
Thomas Grafc71099a2006-08-04 23:20:06 -07002223 struct rt6_mtu_change_arg arg = {
2224 .dev = dev,
2225 .mtu = mtu,
2226 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002228 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229}
2230
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002231static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002232 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002233 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002234 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002235 [RTA_PRIORITY] = { .type = NLA_U32 },
2236 [RTA_METRICS] = { .type = NLA_NESTED },
2237};
2238
2239static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2240 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241{
Thomas Graf86872cb2006-08-22 00:01:08 -07002242 struct rtmsg *rtm;
2243 struct nlattr *tb[RTA_MAX+1];
2244 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245
Thomas Graf86872cb2006-08-22 00:01:08 -07002246 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2247 if (err < 0)
2248 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
Thomas Graf86872cb2006-08-22 00:01:08 -07002250 err = -EINVAL;
2251 rtm = nlmsg_data(nlh);
2252 memset(cfg, 0, sizeof(*cfg));
2253
2254 cfg->fc_table = rtm->rtm_table;
2255 cfg->fc_dst_len = rtm->rtm_dst_len;
2256 cfg->fc_src_len = rtm->rtm_src_len;
2257 cfg->fc_flags = RTF_UP;
2258 cfg->fc_protocol = rtm->rtm_protocol;
2259
2260 if (rtm->rtm_type == RTN_UNREACHABLE)
2261 cfg->fc_flags |= RTF_REJECT;
2262
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002263 if (rtm->rtm_type == RTN_LOCAL)
2264 cfg->fc_flags |= RTF_LOCAL;
2265
Thomas Graf86872cb2006-08-22 00:01:08 -07002266 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2267 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002268 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002269
2270 if (tb[RTA_GATEWAY]) {
2271 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2272 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002274
2275 if (tb[RTA_DST]) {
2276 int plen = (rtm->rtm_dst_len + 7) >> 3;
2277
2278 if (nla_len(tb[RTA_DST]) < plen)
2279 goto errout;
2280
2281 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002283
2284 if (tb[RTA_SRC]) {
2285 int plen = (rtm->rtm_src_len + 7) >> 3;
2286
2287 if (nla_len(tb[RTA_SRC]) < plen)
2288 goto errout;
2289
2290 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002292
Daniel Walterc3968a82011-04-13 21:10:57 +00002293 if (tb[RTA_PREFSRC])
2294 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2295
Thomas Graf86872cb2006-08-22 00:01:08 -07002296 if (tb[RTA_OIF])
2297 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2298
2299 if (tb[RTA_PRIORITY])
2300 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2301
2302 if (tb[RTA_METRICS]) {
2303 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2304 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002306
2307 if (tb[RTA_TABLE])
2308 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2309
2310 err = 0;
2311errout:
2312 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313}
2314
Thomas Grafc127ea22007-03-22 11:58:32 -07002315static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316{
Thomas Graf86872cb2006-08-22 00:01:08 -07002317 struct fib6_config cfg;
2318 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
Thomas Graf86872cb2006-08-22 00:01:08 -07002320 err = rtm_to_fib6_config(skb, nlh, &cfg);
2321 if (err < 0)
2322 return err;
2323
2324 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325}
2326
Thomas Grafc127ea22007-03-22 11:58:32 -07002327static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328{
Thomas Graf86872cb2006-08-22 00:01:08 -07002329 struct fib6_config cfg;
2330 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331
Thomas Graf86872cb2006-08-22 00:01:08 -07002332 err = rtm_to_fib6_config(skb, nlh, &cfg);
2333 if (err < 0)
2334 return err;
2335
2336 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337}
2338
Thomas Graf339bf982006-11-10 14:10:15 -08002339static inline size_t rt6_nlmsg_size(void)
2340{
2341 return NLMSG_ALIGN(sizeof(struct rtmsg))
2342 + nla_total_size(16) /* RTA_SRC */
2343 + nla_total_size(16) /* RTA_DST */
2344 + nla_total_size(16) /* RTA_GATEWAY */
2345 + nla_total_size(16) /* RTA_PREFSRC */
2346 + nla_total_size(4) /* RTA_TABLE */
2347 + nla_total_size(4) /* RTA_IIF */
2348 + nla_total_size(4) /* RTA_OIF */
2349 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002350 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002351 + nla_total_size(sizeof(struct rta_cacheinfo));
2352}
2353
Brian Haley191cd582008-08-14 15:33:21 -07002354static int rt6_fill_node(struct net *net,
2355 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002356 struct in6_addr *dst, struct in6_addr *src,
2357 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002358 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359{
2360 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002361 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002362 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002363 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002364 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365
2366 if (prefix) { /* user wants prefix routes only */
2367 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2368 /* success since this is not a prefix route */
2369 return 1;
2370 }
2371 }
2372
Thomas Graf2d7202b2006-08-22 00:01:27 -07002373 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002374 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002375 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002376
2377 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 rtm->rtm_family = AF_INET6;
2379 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2380 rtm->rtm_src_len = rt->rt6i_src.plen;
2381 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002382 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002383 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002384 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002385 table = RT6_TABLE_UNSPEC;
2386 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002387 if (nla_put_u32(skb, RTA_TABLE, table))
2388 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002389 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002391 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002392 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002393 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 rtm->rtm_type = RTN_LOCAL;
2395 else
2396 rtm->rtm_type = RTN_UNICAST;
2397 rtm->rtm_flags = 0;
2398 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2399 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002400 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 rtm->rtm_protocol = RTPROT_REDIRECT;
Denis Ovsienkof0396f602012-07-10 04:45:50 +00002402 else if (rt->rt6i_flags & RTF_ADDRCONF) {
2403 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
2404 rtm->rtm_protocol = RTPROT_RA;
2405 else
2406 rtm->rtm_protocol = RTPROT_KERNEL;
2407 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408
David S. Miller38308472011-12-03 18:02:47 -05002409 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410 rtm->rtm_flags |= RTM_F_CLONED;
2411
2412 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002413 if (nla_put(skb, RTA_DST, 16, dst))
2414 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002415 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002417 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2418 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419#ifdef CONFIG_IPV6_SUBTREES
2420 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002421 if (nla_put(skb, RTA_SRC, 16, src))
2422 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002423 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002424 } else if (rtm->rtm_src_len &&
2425 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2426 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002428 if (iif) {
2429#ifdef CONFIG_IPV6_MROUTE
2430 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002431 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002432 if (err <= 0) {
2433 if (!nowait) {
2434 if (err == 0)
2435 return 0;
2436 goto nla_put_failure;
2437 } else {
2438 if (err == -EMSGSIZE)
2439 goto nla_put_failure;
2440 }
2441 }
2442 } else
2443#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002444 if (nla_put_u32(skb, RTA_IIF, iif))
2445 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002446 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002448 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2449 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2450 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002452
Daniel Walterc3968a82011-04-13 21:10:57 +00002453 if (rt->rt6i_prefsrc.plen) {
2454 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002455 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002456 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2457 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002458 }
2459
David S. Millerdefb3512010-12-08 21:16:57 -08002460 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002461 goto nla_put_failure;
2462
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002463 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002464 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002465 if (n) {
2466 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2467 rcu_read_unlock();
2468 goto nla_put_failure;
2469 }
2470 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002471 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002472
David S. Millerc78679e2012-04-01 20:27:33 -04002473 if (rt->dst.dev &&
2474 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2475 goto nla_put_failure;
2476 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2477 goto nla_put_failure;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002478 if (!(rt->rt6i_flags & RTF_EXPIRES))
2479 expires = 0;
David S. Millerd1918542011-12-28 20:19:20 -05002480 else if (rt->dst.expires - jiffies < INT_MAX)
2481 expires = rt->dst.expires - jiffies;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002482 else
2483 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002484
David S. Miller87a50692012-07-10 05:06:14 -07002485 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002486 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487
Thomas Graf2d7202b2006-08-22 00:01:27 -07002488 return nlmsg_end(skb, nlh);
2489
2490nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002491 nlmsg_cancel(skb, nlh);
2492 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493}
2494
Patrick McHardy1b43af52006-08-10 23:11:17 -07002495int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496{
2497 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2498 int prefix;
2499
Thomas Graf2d7202b2006-08-22 00:01:27 -07002500 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2501 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2503 } else
2504 prefix = 0;
2505
Brian Haley191cd582008-08-14 15:33:21 -07002506 return rt6_fill_node(arg->net,
2507 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002509 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510}
2511
Thomas Grafc127ea22007-03-22 11:58:32 -07002512static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002514 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002515 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002517 struct sk_buff *skb;
2518 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002519 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002520 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002521
2522 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2523 if (err < 0)
2524 goto errout;
2525
2526 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002527 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002528
2529 if (tb[RTA_SRC]) {
2530 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2531 goto errout;
2532
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002533 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002534 }
2535
2536 if (tb[RTA_DST]) {
2537 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2538 goto errout;
2539
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002540 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002541 }
2542
2543 if (tb[RTA_IIF])
2544 iif = nla_get_u32(tb[RTA_IIF]);
2545
2546 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002547 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002548
2549 if (iif) {
2550 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002551 int flags = 0;
2552
Daniel Lezcano55786892008-03-04 13:47:47 -08002553 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002554 if (!dev) {
2555 err = -ENODEV;
2556 goto errout;
2557 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002558
2559 fl6.flowi6_iif = iif;
2560
2561 if (!ipv6_addr_any(&fl6.saddr))
2562 flags |= RT6_LOOKUP_F_HAS_SADDR;
2563
2564 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2565 flags);
2566 } else {
2567 fl6.flowi6_oif = oif;
2568
2569 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002570 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571
2572 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002573 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002574 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002575 err = -ENOBUFS;
2576 goto errout;
2577 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578
2579 /* Reserve room for dummy headers, this skb can pass
2580 through good chunk of routing engine.
2581 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002582 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2584
Changli Gaod8d1f302010-06-10 23:31:35 -07002585 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586
David S. Miller4c9483b2011-03-12 16:22:43 -05002587 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002589 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002591 kfree_skb(skb);
2592 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 }
2594
Daniel Lezcano55786892008-03-04 13:47:47 -08002595 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002596errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598}
2599
Thomas Graf86872cb2006-08-22 00:01:08 -07002600void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601{
2602 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002603 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002604 u32 seq;
2605 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002607 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002608 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002609
Thomas Graf339bf982006-11-10 14:10:15 -08002610 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002611 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002612 goto errout;
2613
Brian Haley191cd582008-08-14 15:33:21 -07002614 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002615 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002616 if (err < 0) {
2617 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2618 WARN_ON(err == -EMSGSIZE);
2619 kfree_skb(skb);
2620 goto errout;
2621 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002622 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2623 info->nlh, gfp_any());
2624 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002625errout:
2626 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002627 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628}
2629
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002630static int ip6_route_dev_notify(struct notifier_block *this,
2631 unsigned long event, void *data)
2632{
2633 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002634 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002635
2636 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002637 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002638 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2639#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002640 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002641 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002642 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002643 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2644#endif
2645 }
2646
2647 return NOTIFY_OK;
2648}
2649
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650/*
2651 * /proc
2652 */
2653
2654#ifdef CONFIG_PROC_FS
2655
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656struct rt6_proc_arg
2657{
2658 char *buffer;
2659 int offset;
2660 int length;
2661 int skip;
2662 int len;
2663};
2664
2665static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2666{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002667 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002668 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002670 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671
2672#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002673 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002674#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002675 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002677 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002678 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002679 if (n) {
2680 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002682 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002684 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002685 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002686 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2687 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002688 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 return 0;
2690}
2691
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002692static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002694 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002695 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002696 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697}
2698
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002699static int ipv6_route_open(struct inode *inode, struct file *file)
2700{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002701 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002702}
2703
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002704static const struct file_operations ipv6_route_proc_fops = {
2705 .owner = THIS_MODULE,
2706 .open = ipv6_route_open,
2707 .read = seq_read,
2708 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002709 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002710};
2711
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2713{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002714 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002716 net->ipv6.rt6_stats->fib_nodes,
2717 net->ipv6.rt6_stats->fib_route_nodes,
2718 net->ipv6.rt6_stats->fib_rt_alloc,
2719 net->ipv6.rt6_stats->fib_rt_entries,
2720 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002721 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002722 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723
2724 return 0;
2725}
2726
2727static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2728{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002729 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002730}
2731
Arjan van de Ven9a321442007-02-12 00:55:35 -08002732static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733 .owner = THIS_MODULE,
2734 .open = rt6_stats_seq_open,
2735 .read = seq_read,
2736 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002737 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738};
2739#endif /* CONFIG_PROC_FS */
2740
2741#ifdef CONFIG_SYSCTL
2742
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002744int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 void __user *buffer, size_t *lenp, loff_t *ppos)
2746{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002747 struct net *net;
2748 int delay;
2749 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002751
2752 net = (struct net *)ctl->extra1;
2753 delay = net->ipv6.sysctl.flush_delay;
2754 proc_dointvec(ctl, write, buffer, lenp, ppos);
2755 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2756 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757}
2758
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002759ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002760 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002762 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002764 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002765 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 },
2767 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002769 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770 .maxlen = sizeof(int),
2771 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002772 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 },
2774 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002776 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 .maxlen = sizeof(int),
2778 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002779 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 },
2781 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002783 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 .maxlen = sizeof(int),
2785 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002786 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 },
2788 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002790 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 .maxlen = sizeof(int),
2792 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002793 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 },
2795 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002797 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 .maxlen = sizeof(int),
2799 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002800 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 },
2802 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002804 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 .maxlen = sizeof(int),
2806 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002807 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 },
2809 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002811 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 .maxlen = sizeof(int),
2813 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002814 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 },
2816 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002818 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 .maxlen = sizeof(int),
2820 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002821 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 },
2823 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002825 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 .maxlen = sizeof(int),
2827 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002828 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002830 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831};
2832
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002833struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002834{
2835 struct ctl_table *table;
2836
2837 table = kmemdup(ipv6_route_table_template,
2838 sizeof(ipv6_route_table_template),
2839 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002840
2841 if (table) {
2842 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002843 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002844 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002845 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2846 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2847 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2848 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2849 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2850 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2851 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002852 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002853 }
2854
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002855 return table;
2856}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857#endif
2858
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002859static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002860{
Pavel Emelyanov633d4242008-04-21 14:25:23 -07002861 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002862
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002863 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2864 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002865
Eric Dumazetfc66f952010-10-08 06:37:34 +00002866 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2867 goto out_ip6_dst_ops;
2868
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002869 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2870 sizeof(*net->ipv6.ip6_null_entry),
2871 GFP_KERNEL);
2872 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002873 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002874 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002875 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002876 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002877 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2878 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002879
2880#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2881 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2882 sizeof(*net->ipv6.ip6_prohibit_entry),
2883 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002884 if (!net->ipv6.ip6_prohibit_entry)
2885 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002886 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002887 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002888 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002889 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2890 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002891
2892 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2893 sizeof(*net->ipv6.ip6_blk_hole_entry),
2894 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002895 if (!net->ipv6.ip6_blk_hole_entry)
2896 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002897 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002898 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002899 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002900 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2901 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002902#endif
2903
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002904 net->ipv6.sysctl.flush_delay = 0;
2905 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2906 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2907 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2908 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2909 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2910 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2911 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2912
Benjamin Thery6891a342008-03-04 13:49:47 -08002913 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2914
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002915 ret = 0;
2916out:
2917 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002918
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002919#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2920out_ip6_prohibit_entry:
2921 kfree(net->ipv6.ip6_prohibit_entry);
2922out_ip6_null_entry:
2923 kfree(net->ipv6.ip6_null_entry);
2924#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002925out_ip6_dst_entries:
2926 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002927out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002928 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002929}
2930
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002931static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002932{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002933 kfree(net->ipv6.ip6_null_entry);
2934#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2935 kfree(net->ipv6.ip6_prohibit_entry);
2936 kfree(net->ipv6.ip6_blk_hole_entry);
2937#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002938 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002939}
2940
Thomas Grafd1896342012-06-18 12:08:33 +00002941static int __net_init ip6_route_net_init_late(struct net *net)
2942{
2943#ifdef CONFIG_PROC_FS
2944 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2945 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2946#endif
2947 return 0;
2948}
2949
2950static void __net_exit ip6_route_net_exit_late(struct net *net)
2951{
2952#ifdef CONFIG_PROC_FS
2953 proc_net_remove(net, "ipv6_route");
2954 proc_net_remove(net, "rt6_stats");
2955#endif
2956}
2957
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002958static struct pernet_operations ip6_route_net_ops = {
2959 .init = ip6_route_net_init,
2960 .exit = ip6_route_net_exit,
2961};
2962
David S. Millerc3426b42012-06-09 16:27:05 -07002963static int __net_init ipv6_inetpeer_init(struct net *net)
2964{
2965 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2966
2967 if (!bp)
2968 return -ENOMEM;
2969 inet_peer_base_init(bp);
2970 net->ipv6.peers = bp;
2971 return 0;
2972}
2973
2974static void __net_exit ipv6_inetpeer_exit(struct net *net)
2975{
2976 struct inet_peer_base *bp = net->ipv6.peers;
2977
2978 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07002979 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07002980 kfree(bp);
2981}
2982
David S. Miller2b823f72012-06-09 19:00:16 -07002983static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07002984 .init = ipv6_inetpeer_init,
2985 .exit = ipv6_inetpeer_exit,
2986};
2987
Thomas Grafd1896342012-06-18 12:08:33 +00002988static struct pernet_operations ip6_route_net_late_ops = {
2989 .init = ip6_route_net_init_late,
2990 .exit = ip6_route_net_exit_late,
2991};
2992
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002993static struct notifier_block ip6_route_dev_notifier = {
2994 .notifier_call = ip6_route_dev_notify,
2995 .priority = 0,
2996};
2997
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002998int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002999{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003000 int ret;
3001
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003002 ret = -ENOMEM;
3003 ip6_dst_ops_template.kmem_cachep =
3004 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3005 SLAB_HWCACHE_ALIGN, NULL);
3006 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003007 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003008
Eric Dumazetfc66f952010-10-08 06:37:34 +00003009 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003010 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003011 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003012
David S. Millerc3426b42012-06-09 16:27:05 -07003013 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3014 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003015 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003016
David S. Miller7e52b332012-06-15 15:51:55 -07003017 ret = register_pernet_subsys(&ip6_route_net_ops);
3018 if (ret)
3019 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003020
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003021 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3022
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003023 /* Registering of the loopback is done before this portion of code,
3024 * the loopback reference in rt6_info will not be taken, do it
3025 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003026 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003027 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3028 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003029 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003030 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003031 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003032 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3033 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003034 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003035 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003036 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003037
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003038 ret = xfrm6_init();
3039 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003040 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003041
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003042 ret = fib6_rules_init();
3043 if (ret)
3044 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003045
Thomas Grafd1896342012-06-18 12:08:33 +00003046 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3047 if (ret)
3048 goto fib6_rules_init;
3049
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003050 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003051 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3052 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3053 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003054 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003055
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003056 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003057 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003058 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003059
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003060out:
3061 return ret;
3062
Thomas Grafd1896342012-06-18 12:08:33 +00003063out_register_late_subsys:
3064 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003065fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003066 fib6_rules_cleanup();
3067xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003068 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003069out_fib6_init:
3070 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003071out_register_subsys:
3072 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003073out_register_inetpeer:
3074 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003075out_dst_entries:
3076 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003077out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003078 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003079 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080}
3081
3082void ip6_route_cleanup(void)
3083{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003084 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003085 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003086 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003089 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003090 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003091 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003092 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093}