blob: 3151aabff5fd6c1b530d50903a91ef3be27bd750 [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 Harrison09640e632009-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 Harrison09640e632009-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 Klasserta2de86f2012-07-05 03:18:28 +0000283 memset(&rt->n, 0,
David S. Miller38308472011-12-03 18:02:47 -0500284 sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller8b96d222012-06-11 02:01:56 -0700285 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
David S. Miller97bab732012-06-09 22:36:36 -0700286 }
David S. Millercf911662011-04-28 14:31:47 -0700287 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288}
289
290static void ip6_dst_destroy(struct dst_entry *dst)
291{
292 struct rt6_info *rt = (struct rt6_info *)dst;
293 struct inet6_dev *idev = rt->rt6i_idev;
294
David S. Miller97cac082012-07-02 22:43:47 -0700295 if (rt->n)
296 neigh_release(rt->n);
297
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000298 if (!(rt->dst.flags & DST_HOST))
299 dst_destroy_metrics_generic(dst);
300
David S. Miller38308472011-12-03 18:02:47 -0500301 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 rt->rt6i_idev = NULL;
303 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900304 }
Gao feng1716a962012-04-06 00:13:10 +0000305
306 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
307 dst_release(dst->from);
308
David S. Miller97bab732012-06-09 22:36:36 -0700309 if (rt6_has_peer(rt)) {
310 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800311 inet_putpeer(peer);
312 }
313}
314
David S. Miller6431cbc2011-02-07 20:38:06 -0800315static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
316
317static u32 rt6_peer_genid(void)
318{
319 return atomic_read(&__rt6_peer_genid);
320}
321
David S. Millerb3419362010-11-30 12:27:11 -0800322void rt6_bind_peer(struct rt6_info *rt, int create)
323{
David S. Miller97bab732012-06-09 22:36:36 -0700324 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800325 struct inet_peer *peer;
326
David S. Miller97bab732012-06-09 22:36:36 -0700327 base = inetpeer_base_ptr(rt->_rt6i_peer);
328 if (!base)
329 return;
330
331 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700332 if (peer) {
333 if (!rt6_set_peer(rt, peer))
334 inet_putpeer(peer);
335 else
336 rt->rt6i_peer_genid = rt6_peer_genid();
337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
340static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
341 int how)
342{
343 struct rt6_info *rt = (struct rt6_info *)dst;
344 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800345 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900346 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
David S. Miller97cac082012-07-02 22:43:47 -0700348 if (dev != loopback_dev) {
349 if (idev && idev->dev == dev) {
350 struct inet6_dev *loopback_idev =
351 in6_dev_get(loopback_dev);
352 if (loopback_idev) {
353 rt->rt6i_idev = loopback_idev;
354 in6_dev_put(idev);
355 }
356 }
357 if (rt->n && rt->n->dev == dev) {
358 rt->n->dev = loopback_dev;
359 dev_hold(loopback_dev);
360 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 }
362 }
363}
364
Eric Dumazeta50feda2012-05-18 18:57:34 +0000365static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366{
Gao feng1716a962012-04-06 00:13:10 +0000367 struct rt6_info *ort = NULL;
368
369 if (rt->rt6i_flags & RTF_EXPIRES) {
370 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000371 return true;
Gao feng1716a962012-04-06 00:13:10 +0000372 } else if (rt->dst.from) {
373 ort = (struct rt6_info *) rt->dst.from;
374 return (ort->rt6i_flags & RTF_EXPIRES) &&
375 time_after(jiffies, ort->dst.expires);
376 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000377 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378}
379
Eric Dumazeta50feda2012-05-18 18:57:34 +0000380static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700381{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000382 return ipv6_addr_type(daddr) &
383 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700384}
385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700387 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 */
389
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800390static inline struct rt6_info *rt6_device_match(struct net *net,
391 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000392 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700394 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395{
396 struct rt6_info *local = NULL;
397 struct rt6_info *sprt;
398
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900399 if (!oif && ipv6_addr_any(saddr))
400 goto out;
401
Changli Gaod8d1f302010-06-10 23:31:35 -0700402 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500403 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900404
405 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 if (dev->ifindex == oif)
407 return sprt;
408 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500409 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700411 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900413 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 local->rt6i_idev->dev->ifindex == oif))
415 continue;
416 }
417 local = sprt;
418 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900419 } else {
420 if (ipv6_chk_addr(net, saddr, dev,
421 flags & RT6_LOOKUP_F_IFACE))
422 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900424 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900426 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 if (local)
428 return local;
429
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700430 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800431 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900433out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 return rt;
435}
436
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800437#ifdef CONFIG_IPV6_ROUTER_PREF
438static void rt6_probe(struct rt6_info *rt)
439{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000440 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800441 /*
442 * Okay, this does not seem to be appropriate
443 * for now, however, we need to check if it
444 * is really so; aka Router Reachability Probing.
445 *
446 * Router Reachability Probe MUST be rate-limited
447 * to no more than one per minute.
448 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000449 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700450 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800451 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000452 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800453 read_lock_bh(&neigh->lock);
454 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e163562006-03-20 17:05:47 -0800455 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800456 struct in6_addr mcaddr;
457 struct in6_addr *target;
458
459 neigh->updated = jiffies;
460 read_unlock_bh(&neigh->lock);
461
462 target = (struct in6_addr *)&neigh->primary_key;
463 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500464 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000465 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800466 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000467 }
468out:
469 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800470}
471#else
472static inline void rt6_probe(struct rt6_info *rt)
473{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800474}
475#endif
476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800478 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700480static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481{
David S. Millerd1918542011-12-28 20:19:20 -0500482 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700483 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800484 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700485 if ((dev->flags & IFF_LOOPBACK) &&
486 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
487 return 1;
488 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
Dave Jonesb6f99a22007-03-22 12:27:49 -0700491static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000493 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800494 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000495
496 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700497 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700498 if (rt->rt6i_flags & RTF_NONEXTHOP ||
499 !(rt->rt6i_flags & RTF_GATEWAY))
500 m = 1;
501 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800502 read_lock_bh(&neigh->lock);
503 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700504 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800505#ifdef CONFIG_IPV6_ROUTER_PREF
506 else if (neigh->nud_state & NUD_FAILED)
507 m = 0;
508#endif
509 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800510 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800511 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800512 } else
513 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000514 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800515 return m;
516}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800518static int rt6_score_route(struct rt6_info *rt, int oif,
519 int strict)
520{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700521 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900522
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700523 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700524 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800525 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800526#ifdef CONFIG_IPV6_ROUTER_PREF
527 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
528#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700529 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800530 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800531 return -1;
532 return m;
533}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
David S. Millerf11e6652007-03-24 20:36:25 -0700535static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
536 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800537{
David S. Millerf11e6652007-03-24 20:36:25 -0700538 int m;
539
540 if (rt6_check_expired(rt))
541 goto out;
542
543 m = rt6_score_route(rt, oif, strict);
544 if (m < 0)
545 goto out;
546
547 if (m > *mpri) {
548 if (strict & RT6_LOOKUP_F_REACHABLE)
549 rt6_probe(match);
550 *mpri = m;
551 match = rt;
552 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
553 rt6_probe(rt);
554 }
555
556out:
557 return match;
558}
559
560static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
561 struct rt6_info *rr_head,
562 u32 metric, int oif, int strict)
563{
564 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800565 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
David S. Millerf11e6652007-03-24 20:36:25 -0700567 match = NULL;
568 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700569 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700570 match = find_match(rt, oif, strict, &mpri, match);
571 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700572 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700573 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800574
David S. Millerf11e6652007-03-24 20:36:25 -0700575 return match;
576}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800577
David S. Millerf11e6652007-03-24 20:36:25 -0700578static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
579{
580 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800581 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
David S. Millerf11e6652007-03-24 20:36:25 -0700583 rt0 = fn->rr_ptr;
584 if (!rt0)
585 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
David S. Millerf11e6652007-03-24 20:36:25 -0700587 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800589 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700590 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700591 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700592
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800593 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700594 if (!next || next->rt6i_metric != rt0->rt6i_metric)
595 next = fn->leaf;
596
597 if (next != rt0)
598 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600
David S. Millerd1918542011-12-28 20:19:20 -0500601 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000602 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603}
604
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800605#ifdef CONFIG_IPV6_ROUTE_INFO
606int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000607 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800608{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900609 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800610 struct route_info *rinfo = (struct route_info *) opt;
611 struct in6_addr prefix_buf, *prefix;
612 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900613 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800614 struct rt6_info *rt;
615
616 if (len < sizeof(struct route_info)) {
617 return -EINVAL;
618 }
619
620 /* Sanity check for prefix_len and length */
621 if (rinfo->length > 3) {
622 return -EINVAL;
623 } else if (rinfo->prefix_len > 128) {
624 return -EINVAL;
625 } else if (rinfo->prefix_len > 64) {
626 if (rinfo->length < 2) {
627 return -EINVAL;
628 }
629 } else if (rinfo->prefix_len > 0) {
630 if (rinfo->length < 1) {
631 return -EINVAL;
632 }
633 }
634
635 pref = rinfo->route_pref;
636 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000637 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800638
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900639 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800640
641 if (rinfo->length == 3)
642 prefix = (struct in6_addr *)rinfo->prefix;
643 else {
644 /* this function is safe */
645 ipv6_addr_prefix(&prefix_buf,
646 (struct in6_addr *)rinfo->prefix,
647 rinfo->prefix_len);
648 prefix = &prefix_buf;
649 }
650
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800651 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
652 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800653
654 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700655 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800656 rt = NULL;
657 }
658
659 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800660 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800661 pref);
662 else if (rt)
663 rt->rt6i_flags = RTF_ROUTEINFO |
664 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
665
666 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000667 if (!addrconf_finite_timeout(lifetime))
668 rt6_clean_expires(rt);
669 else
670 rt6_set_expires(rt, jiffies + HZ * lifetime);
671
Changli Gaod8d1f302010-06-10 23:31:35 -0700672 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800673 }
674 return 0;
675}
676#endif
677
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800678#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700679do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800680 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700681 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700682 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700683 if (fn->fn_flags & RTN_TL_ROOT) \
684 goto out; \
685 pn = fn->parent; \
686 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800687 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700688 else \
689 fn = pn; \
690 if (fn->fn_flags & RTN_RTINFO) \
691 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700692 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700693 } \
David S. Miller38308472011-12-03 18:02:47 -0500694} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700695
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800696static struct rt6_info *ip6_pol_route_lookup(struct net *net,
697 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500698 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699{
700 struct fib6_node *fn;
701 struct rt6_info *rt;
702
Thomas Grafc71099a2006-08-04 23:20:06 -0700703 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500704 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700705restart:
706 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500707 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
708 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700709out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700710 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700711 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700712 return rt;
713
714}
715
Florian Westphalea6e5742011-09-05 16:05:44 +0200716struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
717 int flags)
718{
719 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
720}
721EXPORT_SYMBOL_GPL(ip6_route_lookup);
722
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900723struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
724 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700725{
David S. Miller4c9483b2011-03-12 16:22:43 -0500726 struct flowi6 fl6 = {
727 .flowi6_oif = oif,
728 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700729 };
730 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700731 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700732
Thomas Grafadaa70b2006-10-13 15:01:03 -0700733 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500734 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700735 flags |= RT6_LOOKUP_F_HAS_SADDR;
736 }
737
David S. Miller4c9483b2011-03-12 16:22:43 -0500738 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700739 if (dst->error == 0)
740 return (struct rt6_info *) dst;
741
742 dst_release(dst);
743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 return NULL;
745}
746
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900747EXPORT_SYMBOL(rt6_lookup);
748
Thomas Grafc71099a2006-08-04 23:20:06 -0700749/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 It takes new route entry, the addition fails by any reason the
751 route is freed. In any case, if caller does not hold it, it may
752 be destroyed.
753 */
754
Thomas Graf86872cb2006-08-22 00:01:08 -0700755static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756{
757 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700758 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Thomas Grafc71099a2006-08-04 23:20:06 -0700760 table = rt->rt6i_table;
761 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700762 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700763 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
765 return err;
766}
767
Thomas Graf40e22e82006-08-22 00:00:45 -0700768int ip6_ins_rt(struct rt6_info *rt)
769{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800770 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500771 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800772 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800773 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700774}
775
Gao feng1716a962012-04-06 00:13:10 +0000776static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000777 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000778 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 struct rt6_info *rt;
781
782 /*
783 * Clone the route.
784 */
785
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000786 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
788 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800789 int attempts = !in_softirq();
790
David S. Miller38308472011-12-03 18:02:47 -0500791 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500792 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000793 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900794 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000795 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900796 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800#ifdef CONFIG_IPV6_SUBTREES
801 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000802 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 rt->rt6i_src.plen = 128;
804 }
805#endif
806
David S. Miller14deae42009-01-04 16:04:39 -0800807 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500808 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500809 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800810 int saved_rt_min_interval =
811 net->ipv6.sysctl.ip6_rt_gc_min_interval;
812 int saved_rt_elasticity =
813 net->ipv6.sysctl.ip6_rt_gc_elasticity;
814
815 if (attempts-- > 0) {
816 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
817 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
818
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000819 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800820
821 net->ipv6.sysctl.ip6_rt_gc_elasticity =
822 saved_rt_elasticity;
823 net->ipv6.sysctl.ip6_rt_gc_min_interval =
824 saved_rt_min_interval;
825 goto retry;
826 }
827
Joe Perchesf3213832012-05-15 14:11:53 +0000828 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700829 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800830 return NULL;
831 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800832 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800834 return rt;
835}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000837static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
838 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800839{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000840 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
841
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800842 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800843 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700844 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800845 }
846 return rt;
847}
848
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800849static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500850 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851{
852 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800853 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700854 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800856 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700857 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700859 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
861relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700862 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800864restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500865 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700868 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800869
David S. Miller4c9483b2011-03-12 16:22:43 -0500870 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800871 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800872 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800873 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Changli Gaod8d1f302010-06-10 23:31:35 -0700875 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700876 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800877
David S. Miller97cac082012-07-02 22:43:47 -0700878 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500879 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800880 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500881 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800882 else
883 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800884
Changli Gaod8d1f302010-06-10 23:31:35 -0700885 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800886 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800887
Changli Gaod8d1f302010-06-10 23:31:35 -0700888 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800889 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700890 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800891 if (!err)
892 goto out2;
893 }
894
895 if (--attempts <= 0)
896 goto out2;
897
898 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700899 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800900 * released someone could insert this route. Relookup.
901 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700902 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800903 goto relookup;
904
905out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800906 if (reachable) {
907 reachable = 0;
908 goto restart_2;
909 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700910 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700911 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700913 rt->dst.lastuse = jiffies;
914 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700915
916 return rt;
917}
918
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800919static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500920 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700921{
David S. Miller4c9483b2011-03-12 16:22:43 -0500922 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700923}
924
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000925static struct dst_entry *ip6_route_input_lookup(struct net *net,
926 struct net_device *dev,
927 struct flowi6 *fl6, int flags)
928{
929 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
930 flags |= RT6_LOOKUP_F_IFACE;
931
932 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
933}
934
Thomas Grafc71099a2006-08-04 23:20:06 -0700935void ip6_route_input(struct sk_buff *skb)
936{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000937 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900938 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700939 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500940 struct flowi6 fl6 = {
941 .flowi6_iif = skb->dev->ifindex,
942 .daddr = iph->daddr,
943 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500944 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500945 .flowi6_mark = skb->mark,
946 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700947 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700948
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000949 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700950}
951
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800952static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500953 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700954{
David S. Miller4c9483b2011-03-12 16:22:43 -0500955 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700956}
957
Florian Westphal9c7a4f9c2011-03-22 19:17:36 -0700958struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500959 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700960{
961 int flags = 0;
962
David McCullough4dc27d1c2012-06-25 15:42:26 +0000963 fl6->flowi6_iif = net->loopback_dev->ifindex;
964
David S. Miller4c9483b2011-03-12 16:22:43 -0500965 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700966 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700967
David S. Miller4c9483b2011-03-12 16:22:43 -0500968 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700969 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000970 else if (sk)
971 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700972
David S. Miller4c9483b2011-03-12 16:22:43 -0500973 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974}
975
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900976EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
David S. Miller2774c132011-03-01 14:59:04 -0800978struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700979{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700980 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700981 struct dst_entry *new = NULL;
982
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700983 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700984 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700985 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller97bab732012-06-09 22:36:36 -0700986 rt6_init_peer(rt, net->ipv6.peers);
David S. Millercf911662011-04-28 14:31:47 -0700987
Changli Gaod8d1f302010-06-10 23:31:35 -0700988 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700989
David S. Miller14e50e52007-05-24 18:17:54 -0700990 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800991 new->input = dst_discard;
992 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700993
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000994 if (dst_metrics_read_only(&ort->dst))
995 new->_metrics = ort->dst._metrics;
996 else
997 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700998 rt->rt6i_idev = ort->rt6i_idev;
999 if (rt->rt6i_idev)
1000 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -07001001
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001002 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001003 rt->rt6i_flags = ort->rt6i_flags;
1004 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001005 rt->rt6i_metric = 0;
1006
1007 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1008#ifdef CONFIG_IPV6_SUBTREES
1009 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1010#endif
1011
1012 dst_free(new);
1013 }
1014
David S. Miller69ead7a2011-03-01 14:45:33 -08001015 dst_release(dst_orig);
1016 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001017}
David S. Miller14e50e52007-05-24 18:17:54 -07001018
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019/*
1020 * Destination cache support functions
1021 */
1022
1023static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1024{
1025 struct rt6_info *rt;
1026
1027 rt = (struct rt6_info *) dst;
1028
David S. Miller6431cbc2011-02-07 20:38:06 -08001029 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1030 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001031 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001032 rt6_bind_peer(rt, 0);
1033 rt->rt6i_peer_genid = rt6_peer_genid();
1034 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001036 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 return NULL;
1038}
1039
1040static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1041{
1042 struct rt6_info *rt = (struct rt6_info *) dst;
1043
1044 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001045 if (rt->rt6i_flags & RTF_CACHE) {
1046 if (rt6_check_expired(rt)) {
1047 ip6_del_rt(rt);
1048 dst = NULL;
1049 }
1050 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001052 dst = NULL;
1053 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001055 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056}
1057
1058static void ip6_link_failure(struct sk_buff *skb)
1059{
1060 struct rt6_info *rt;
1061
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001062 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
Eric Dumazetadf30902009-06-02 05:19:30 +00001064 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001066 if (rt->rt6i_flags & RTF_CACHE)
1067 rt6_update_expires(rt, 0);
1068 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 rt->rt6i_node->fn_sernum = -1;
1070 }
1071}
1072
1073static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1074{
1075 struct rt6_info *rt6 = (struct rt6_info*)dst;
1076
David S. Miller81aded22012-06-15 14:54:11 -07001077 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001079 struct net *net = dev_net(dst->dev);
1080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 rt6->rt6i_flags |= RTF_MODIFIED;
1082 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001083 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001085 features |= RTAX_FEATURE_ALLFRAG;
1086 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 }
David S. Millerdefb3512010-12-08 21:16:57 -08001088 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001089 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 }
1091}
1092
David S. Miller42ae66c2012-06-15 20:01:57 -07001093void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1094 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001095{
1096 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1097 struct dst_entry *dst;
1098 struct flowi6 fl6;
1099
1100 memset(&fl6, 0, sizeof(fl6));
1101 fl6.flowi6_oif = oif;
1102 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001103 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001104 fl6.daddr = iph->daddr;
1105 fl6.saddr = iph->saddr;
1106 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1107
1108 dst = ip6_route_output(net, NULL, &fl6);
1109 if (!dst->error)
1110 ip6_rt_update_pmtu(dst, ntohl(mtu));
1111 dst_release(dst);
1112}
1113EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1114
1115void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1116{
1117 ip6_update_pmtu(skb, sock_net(sk), mtu,
1118 sk->sk_bound_dev_if, sk->sk_mark);
1119}
1120EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1121
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001122void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1123{
1124 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1125 struct dst_entry *dst;
1126 struct flowi6 fl6;
1127
1128 memset(&fl6, 0, sizeof(fl6));
1129 fl6.flowi6_oif = oif;
1130 fl6.flowi6_mark = mark;
1131 fl6.flowi6_flags = 0;
1132 fl6.daddr = iph->daddr;
1133 fl6.saddr = iph->saddr;
1134 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1135
1136 dst = ip6_route_output(net, NULL, &fl6);
1137 if (!dst->error)
1138 rt6_do_redirect(dst, skb);
1139 dst_release(dst);
1140}
1141EXPORT_SYMBOL_GPL(ip6_redirect);
1142
1143void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1144{
1145 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1146}
1147EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1148
David S. Miller0dbaee32010-12-13 12:52:14 -08001149static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150{
David S. Miller0dbaee32010-12-13 12:52:14 -08001151 struct net_device *dev = dst->dev;
1152 unsigned int mtu = dst_mtu(dst);
1153 struct net *net = dev_net(dev);
1154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1156
Daniel Lezcano55786892008-03-04 13:47:47 -08001157 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1158 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159
1160 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001161 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1162 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1163 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 * rely only on pmtu discovery"
1165 */
1166 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1167 mtu = IPV6_MAXPLEN;
1168 return mtu;
1169}
1170
Steffen Klassertebb762f2011-11-23 02:12:51 +00001171static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001172{
David S. Millerd33e4552010-12-14 13:01:14 -08001173 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001174 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1175
1176 if (mtu)
1177 return mtu;
1178
1179 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001180
1181 rcu_read_lock();
1182 idev = __in6_dev_get(dst->dev);
1183 if (idev)
1184 mtu = idev->cnf.mtu6;
1185 rcu_read_unlock();
1186
1187 return mtu;
1188}
1189
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001190static struct dst_entry *icmp6_dst_gc_list;
1191static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001192
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001193struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001195 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196{
David S. Miller87a11572011-12-06 17:04:13 -05001197 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 struct rt6_info *rt;
1199 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001200 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201
David S. Miller38308472011-12-03 18:02:47 -05001202 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001203 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
David S. Miller8b96d222012-06-11 02:01:56 -07001205 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001206 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001208 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 goto out;
1210 }
1211
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 if (neigh)
1213 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001214 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001215 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001216 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001217 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001218 dst_free(&rt->dst);
1219 return ERR_CAST(neigh);
1220 }
David S. Miller14deae42009-01-04 16:04:39 -08001221 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001223 rt->dst.flags |= DST_HOST;
1224 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001225 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001226 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001227 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001228 rt->rt6i_dst.plen = 128;
1229 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001230 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001232 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001233 rt->dst.next = icmp6_dst_gc_list;
1234 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001235 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
Daniel Lezcano55786892008-03-04 13:47:47 -08001237 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
David S. Miller87a11572011-12-06 17:04:13 -05001239 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1240
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241out:
David S. Miller87a11572011-12-06 17:04:13 -05001242 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243}
1244
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001245int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246{
Hagen Paul Pfeifere9476e952011-02-25 05:45:19 +00001247 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001248 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001250 spin_lock_bh(&icmp6_dst_lock);
1251 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001252
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 while ((dst = *pprev) != NULL) {
1254 if (!atomic_read(&dst->__refcnt)) {
1255 *pprev = dst->next;
1256 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 } else {
1258 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001259 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 }
1261 }
1262
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001263 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001264
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001265 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266}
1267
David S. Miller1e493d12008-09-10 17:27:15 -07001268static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1269 void *arg)
1270{
1271 struct dst_entry *dst, **pprev;
1272
1273 spin_lock_bh(&icmp6_dst_lock);
1274 pprev = &icmp6_dst_gc_list;
1275 while ((dst = *pprev) != NULL) {
1276 struct rt6_info *rt = (struct rt6_info *) dst;
1277 if (func(rt, arg)) {
1278 *pprev = dst->next;
1279 dst_free(dst);
1280 } else {
1281 pprev = &dst->next;
1282 }
1283 }
1284 spin_unlock_bh(&icmp6_dst_lock);
1285}
1286
Daniel Lezcano569d3642008-01-18 03:56:57 -08001287static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001290 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001291 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1292 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1293 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1294 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1295 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001296 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297
Eric Dumazetfc66f952010-10-08 06:37:34 +00001298 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001299 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001300 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 goto out;
1302
Benjamin Thery6891a342008-03-04 13:49:47 -08001303 net->ipv6.ip6_rt_gc_expire++;
1304 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1305 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001306 entries = dst_entries_get_slow(ops);
1307 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001308 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001310 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001311 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312}
1313
1314/* Clean host part of a prefix. Not necessary in radix tree,
1315 but results in cleaner routing tables.
1316
1317 Remove it only when all the things will work!
1318 */
1319
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001320int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321{
David S. Miller5170ae82010-12-12 21:35:57 -08001322 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001323 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001324 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001325 struct inet6_dev *idev;
1326
1327 rcu_read_lock();
1328 idev = __in6_dev_get(dev);
1329 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001330 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001331 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001332 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001333 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 }
1335 return hoplimit;
1336}
David S. Millerabbf46a2010-12-12 21:14:46 -08001337EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
1339/*
1340 *
1341 */
1342
Thomas Graf86872cb2006-08-22 00:01:08 -07001343int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344{
1345 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001346 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 struct rt6_info *rt = NULL;
1348 struct net_device *dev = NULL;
1349 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001350 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 int addr_type;
1352
Thomas Graf86872cb2006-08-22 00:01:08 -07001353 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 return -EINVAL;
1355#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001356 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 return -EINVAL;
1358#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001359 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001361 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 if (!dev)
1363 goto out;
1364 idev = in6_dev_get(dev);
1365 if (!idev)
1366 goto out;
1367 }
1368
Thomas Graf86872cb2006-08-22 00:01:08 -07001369 if (cfg->fc_metric == 0)
1370 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Matti Vaittinend71314b2011-11-14 00:14:49 +00001372 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001373 if (cfg->fc_nlinfo.nlh &&
1374 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001375 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001376 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001377 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001378 table = fib6_new_table(net, cfg->fc_table);
1379 }
1380 } else {
1381 table = fib6_new_table(net, cfg->fc_table);
1382 }
David S. Miller38308472011-12-03 18:02:47 -05001383
1384 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001385 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001386
David S. Miller8b96d222012-06-11 02:01:56 -07001387 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
David S. Miller38308472011-12-03 18:02:47 -05001389 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 err = -ENOMEM;
1391 goto out;
1392 }
1393
Changli Gaod8d1f302010-06-10 23:31:35 -07001394 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001395
1396 if (cfg->fc_flags & RTF_EXPIRES)
1397 rt6_set_expires(rt, jiffies +
1398 clock_t_to_jiffies(cfg->fc_expires));
1399 else
1400 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
Thomas Graf86872cb2006-08-22 00:01:08 -07001402 if (cfg->fc_protocol == RTPROT_UNSPEC)
1403 cfg->fc_protocol = RTPROT_BOOT;
1404 rt->rt6i_protocol = cfg->fc_protocol;
1405
1406 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
1408 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001409 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001410 else if (cfg->fc_flags & RTF_LOCAL)
1411 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001413 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
Changli Gaod8d1f302010-06-10 23:31:35 -07001415 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
Thomas Graf86872cb2006-08-22 00:01:08 -07001417 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1418 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001420 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001422 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1423 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1424 if (!metrics) {
1425 err = -ENOMEM;
1426 goto out;
1427 }
1428 dst_init_metrics(&rt->dst, metrics, 0);
1429 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001431 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1432 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433#endif
1434
Thomas Graf86872cb2006-08-22 00:01:08 -07001435 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
1437 /* We cannot add true routes via loopback here,
1438 they would result in kernel looping; promote them to reject routes
1439 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001440 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001441 (dev && (dev->flags & IFF_LOOPBACK) &&
1442 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1443 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001445 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 if (dev) {
1447 dev_put(dev);
1448 in6_dev_put(idev);
1449 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001450 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 dev_hold(dev);
1452 idev = in6_dev_get(dev);
1453 if (!idev) {
1454 err = -ENODEV;
1455 goto out;
1456 }
1457 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001458 rt->dst.output = ip6_pkt_discard_out;
1459 rt->dst.input = ip6_pkt_discard;
1460 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1462 goto install_route;
1463 }
1464
Thomas Graf86872cb2006-08-22 00:01:08 -07001465 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001466 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 int gwa_type;
1468
Thomas Graf86872cb2006-08-22 00:01:08 -07001469 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001470 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 gwa_type = ipv6_addr_type(gw_addr);
1472
1473 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1474 struct rt6_info *grt;
1475
1476 /* IPv6 strictly inhibits using not link-local
1477 addresses as nexthop address.
1478 Otherwise, router will not able to send redirects.
1479 It is very good, but in some (rare!) circumstances
1480 (SIT, PtP, NBMA NOARP links) it is handy to allow
1481 some exceptions. --ANK
1482 */
1483 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001484 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 goto out;
1486
Daniel Lezcano55786892008-03-04 13:47:47 -08001487 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
1489 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001490 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 goto out;
1492 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001493 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001494 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 goto out;
1496 }
1497 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001498 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 idev = grt->rt6i_idev;
1500 dev_hold(dev);
1501 in6_dev_hold(grt->rt6i_idev);
1502 }
David S. Miller38308472011-12-03 18:02:47 -05001503 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001505 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
1507 if (err)
1508 goto out;
1509 }
1510 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001511 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 goto out;
1513 }
1514
1515 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001516 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 goto out;
1518
Daniel Walterc3968a82011-04-13 21:10:57 +00001519 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1520 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1521 err = -EINVAL;
1522 goto out;
1523 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001524 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001525 rt->rt6i_prefsrc.plen = 128;
1526 } else
1527 rt->rt6i_prefsrc.plen = 0;
1528
Thomas Graf86872cb2006-08-22 00:01:08 -07001529 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001530 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001531 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 }
1534
Thomas Graf86872cb2006-08-22 00:01:08 -07001535 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
1537install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001538 if (cfg->fc_mx) {
1539 struct nlattr *nla;
1540 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
Thomas Graf86872cb2006-08-22 00:01:08 -07001542 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001543 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001544
1545 if (type) {
1546 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 err = -EINVAL;
1548 goto out;
1549 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001550
David S. Millerdefb3512010-12-08 21:16:57 -08001551 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 }
1554 }
1555
Changli Gaod8d1f302010-06-10 23:31:35 -07001556 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001558 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001559
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001560 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001561
Thomas Graf86872cb2006-08-22 00:01:08 -07001562 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
1564out:
1565 if (dev)
1566 dev_put(dev);
1567 if (idev)
1568 in6_dev_put(idev);
1569 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001570 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 return err;
1572}
1573
Thomas Graf86872cb2006-08-22 00:01:08 -07001574static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575{
1576 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001577 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001578 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001580 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001581 return -ENOENT;
1582
Thomas Grafc71099a2006-08-04 23:20:06 -07001583 table = rt->rt6i_table;
1584 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585
Thomas Graf86872cb2006-08-22 00:01:08 -07001586 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001587 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
Thomas Grafc71099a2006-08-04 23:20:06 -07001589 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590
1591 return err;
1592}
1593
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001594int ip6_del_rt(struct rt6_info *rt)
1595{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001596 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001597 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001598 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001599 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001600}
1601
Thomas Graf86872cb2006-08-22 00:01:08 -07001602static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603{
Thomas Grafc71099a2006-08-04 23:20:06 -07001604 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 struct fib6_node *fn;
1606 struct rt6_info *rt;
1607 int err = -ESRCH;
1608
Daniel Lezcano55786892008-03-04 13:47:47 -08001609 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001610 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001611 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612
Thomas Grafc71099a2006-08-04 23:20:06 -07001613 read_lock_bh(&table->tb6_lock);
1614
1615 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001616 &cfg->fc_dst, cfg->fc_dst_len,
1617 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001618
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001620 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001621 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001622 (!rt->dst.dev ||
1623 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001625 if (cfg->fc_flags & RTF_GATEWAY &&
1626 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001628 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001630 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001631 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
Thomas Graf86872cb2006-08-22 00:01:08 -07001633 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 }
1635 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001636 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
1638 return err;
1639}
1640
David S. Miller6e157b62012-07-12 00:05:02 -07001641static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001642{
David S. Millere8599ff2012-07-11 23:43:53 -07001643 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001644 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001645 struct rt6_info *rt, *nrt = NULL;
1646 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001647 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001648 const struct in6_addr *dest;
1649 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001650 struct inet6_dev *in6_dev;
1651 struct neighbour *neigh;
1652 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001653 int optlen, on_link;
1654 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001655
1656 optlen = skb->tail - skb->transport_header;
1657 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1658
1659 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001660 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001661 return;
1662 }
1663
1664 icmph = icmp6_hdr(skb);
1665 target = (const struct in6_addr *) (icmph + 1);
1666 dest = target + 1;
1667
1668 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001669 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001670 return;
1671 }
1672
David S. Miller6e157b62012-07-12 00:05:02 -07001673 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001674 if (ipv6_addr_equal(dest, target)) {
1675 on_link = 1;
1676 } else if (ipv6_addr_type(target) !=
1677 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001678 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001679 return;
1680 }
1681
1682 in6_dev = __in6_dev_get(skb->dev);
1683 if (!in6_dev)
1684 return;
1685 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1686 return;
1687
1688 /* RFC2461 8.1:
1689 * The IP source address of the Redirect MUST be the same as the current
1690 * first-hop router for the specified ICMP Destination Address.
1691 */
1692
1693 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1694 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1695 return;
1696 }
David S. Miller6e157b62012-07-12 00:05:02 -07001697
1698 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001699 if (ndopts.nd_opts_tgt_lladdr) {
1700 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1701 skb->dev);
1702 if (!lladdr) {
1703 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1704 return;
1705 }
1706 }
1707
David S. Miller6e157b62012-07-12 00:05:02 -07001708 rt = (struct rt6_info *) dst;
1709 if (rt == net->ipv6.ip6_null_entry) {
1710 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1711 return;
1712 }
1713
1714 /* Redirect received -> path was valid.
1715 * Look, redirects are sent only in response to data packets,
1716 * so that this nexthop apparently is reachable. --ANK
1717 */
1718 dst_confirm(&rt->dst);
1719
David S. Millere8599ff2012-07-11 23:43:53 -07001720 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1721 if (!neigh)
1722 return;
1723
David S. Miller6e157b62012-07-12 00:05:02 -07001724 /* Duplicate redirect: silently ignore. */
1725 old_neigh = rt->n;
1726 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001727 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 /*
1730 * We have finally decided to accept it.
1731 */
1732
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001733 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1735 NEIGH_UPDATE_F_OVERRIDE|
1736 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1737 NEIGH_UPDATE_F_ISROUTER))
1738 );
1739
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001740 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001741 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 goto out;
1743
1744 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1745 if (on_link)
1746 nrt->rt6i_flags &= ~RTF_GATEWAY;
1747
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001748 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001749 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750
Thomas Graf40e22e82006-08-22 00:00:45 -07001751 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 goto out;
1753
Changli Gaod8d1f302010-06-10 23:31:35 -07001754 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001755 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001756 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001757 netevent.new_neigh = neigh;
1758 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001759 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1760
David S. Miller38308472011-12-03 18:02:47 -05001761 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001762 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001763 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 }
1765
1766out:
David S. Millere8599ff2012-07-11 23:43:53 -07001767 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001768}
1769
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 * Misc support functions
1772 */
1773
Gao feng1716a962012-04-06 00:13:10 +00001774static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001775 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776{
David S. Millerd1918542011-12-28 20:19:20 -05001777 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001778 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1779 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780
1781 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001782 rt->dst.input = ort->dst.input;
1783 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001784 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001786 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001787 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001788 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001789 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 rt->rt6i_idev = ort->rt6i_idev;
1791 if (rt->rt6i_idev)
1792 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001793 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001795 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001796 rt->rt6i_flags = ort->rt6i_flags;
1797 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1798 (RTF_DEFAULT | RTF_ADDRCONF))
1799 rt6_set_from(rt, ort);
1800 else
1801 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 rt->rt6i_metric = 0;
1803
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804#ifdef CONFIG_IPV6_SUBTREES
1805 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1806#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001807 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001808 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 }
1810 return rt;
1811}
1812
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001813#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001814static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001815 const struct in6_addr *prefix, int prefixlen,
1816 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001817{
1818 struct fib6_node *fn;
1819 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001820 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001821
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001822 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001823 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001824 return NULL;
1825
1826 write_lock_bh(&table->tb6_lock);
1827 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001828 if (!fn)
1829 goto out;
1830
Changli Gaod8d1f302010-06-10 23:31:35 -07001831 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001832 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001833 continue;
1834 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1835 continue;
1836 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1837 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001838 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001839 break;
1840 }
1841out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001842 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001843 return rt;
1844}
1845
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001846static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001847 const struct in6_addr *prefix, int prefixlen,
1848 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001849 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001850{
Thomas Graf86872cb2006-08-22 00:01:08 -07001851 struct fib6_config cfg = {
1852 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001853 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001854 .fc_ifindex = ifindex,
1855 .fc_dst_len = prefixlen,
1856 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1857 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001858 .fc_nlinfo.pid = 0,
1859 .fc_nlinfo.nlh = NULL,
1860 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001861 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001862
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001863 cfg.fc_dst = *prefix;
1864 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001865
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001866 /* We should treat it as a default route if prefix length is 0. */
1867 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001868 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001869
Thomas Graf86872cb2006-08-22 00:01:08 -07001870 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001871
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001872 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001873}
1874#endif
1875
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001876struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001877{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001879 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001881 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001882 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001883 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884
Thomas Grafc71099a2006-08-04 23:20:06 -07001885 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001886 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001887 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001888 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1890 break;
1891 }
1892 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001893 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001894 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 return rt;
1896}
1897
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001898struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001899 struct net_device *dev,
1900 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901{
Thomas Graf86872cb2006-08-22 00:01:08 -07001902 struct fib6_config cfg = {
1903 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001904 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001905 .fc_ifindex = dev->ifindex,
1906 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1907 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001908 .fc_nlinfo.pid = 0,
1909 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001910 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001911 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001913 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914
Thomas Graf86872cb2006-08-22 00:01:08 -07001915 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 return rt6_get_dflt_router(gwaddr, dev);
1918}
1919
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001920void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921{
1922 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001923 struct fib6_table *table;
1924
1925 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001926 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001927 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001928 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929
1930restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001931 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001932 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001934 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001935 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001936 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 goto restart;
1938 }
1939 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001940 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941}
1942
Daniel Lezcano55786892008-03-04 13:47:47 -08001943static void rtmsg_to_fib6_config(struct net *net,
1944 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001945 struct fib6_config *cfg)
1946{
1947 memset(cfg, 0, sizeof(*cfg));
1948
1949 cfg->fc_table = RT6_TABLE_MAIN;
1950 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1951 cfg->fc_metric = rtmsg->rtmsg_metric;
1952 cfg->fc_expires = rtmsg->rtmsg_info;
1953 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1954 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1955 cfg->fc_flags = rtmsg->rtmsg_flags;
1956
Daniel Lezcano55786892008-03-04 13:47:47 -08001957 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001958
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001959 cfg->fc_dst = rtmsg->rtmsg_dst;
1960 cfg->fc_src = rtmsg->rtmsg_src;
1961 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001962}
1963
Daniel Lezcano55786892008-03-04 13:47:47 -08001964int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965{
Thomas Graf86872cb2006-08-22 00:01:08 -07001966 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 struct in6_rtmsg rtmsg;
1968 int err;
1969
1970 switch(cmd) {
1971 case SIOCADDRT: /* Add a route */
1972 case SIOCDELRT: /* Delete a route */
1973 if (!capable(CAP_NET_ADMIN))
1974 return -EPERM;
1975 err = copy_from_user(&rtmsg, arg,
1976 sizeof(struct in6_rtmsg));
1977 if (err)
1978 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001979
Daniel Lezcano55786892008-03-04 13:47:47 -08001980 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001981
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 rtnl_lock();
1983 switch (cmd) {
1984 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001985 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 break;
1987 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001988 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 break;
1990 default:
1991 err = -EINVAL;
1992 }
1993 rtnl_unlock();
1994
1995 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001996 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997
1998 return -EINVAL;
1999}
2000
2001/*
2002 * Drop the packet on the floor
2003 */
2004
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002005static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002007 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002008 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002009 switch (ipstats_mib_noroutes) {
2010 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002011 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002012 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002013 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2014 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002015 break;
2016 }
2017 /* FALLTHROUGH */
2018 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002019 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2020 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002021 break;
2022 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002023 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 kfree_skb(skb);
2025 return 0;
2026}
2027
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002028static int ip6_pkt_discard(struct sk_buff *skb)
2029{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002030 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002031}
2032
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002033static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034{
Eric Dumazetadf30902009-06-02 05:19:30 +00002035 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002036 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037}
2038
David S. Miller6723ab52006-10-18 21:20:57 -07002039#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2040
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002041static int ip6_pkt_prohibit(struct sk_buff *skb)
2042{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002043 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002044}
2045
2046static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2047{
Eric Dumazetadf30902009-06-02 05:19:30 +00002048 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002049 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002050}
2051
David S. Miller6723ab52006-10-18 21:20:57 -07002052#endif
2053
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054/*
2055 * Allocate a dst for local (unicast / anycast) address.
2056 */
2057
2058struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2059 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002060 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002062 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002063 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002064 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065
David S. Miller38308472011-12-03 18:02:47 -05002066 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002067 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002069 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 in6_dev_hold(idev);
2072
David S. Miller11d53b42011-06-24 15:23:34 -07002073 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002074 rt->dst.input = ip6_input;
2075 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002077 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078
2079 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002080 if (anycast)
2081 rt->rt6i_flags |= RTF_ANYCAST;
2082 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002084 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002085 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002086 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002087 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 }
2089
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002090 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002092 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093
Changli Gaod8d1f302010-06-10 23:31:35 -07002094 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095
2096 return rt;
2097}
2098
Daniel Walterc3968a82011-04-13 21:10:57 +00002099int ip6_route_get_saddr(struct net *net,
2100 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002101 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002102 unsigned int prefs,
2103 struct in6_addr *saddr)
2104{
2105 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2106 int err = 0;
2107 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002108 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002109 else
2110 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2111 daddr, prefs, saddr);
2112 return err;
2113}
2114
2115/* remove deleted ip from prefsrc entries */
2116struct arg_dev_net_ip {
2117 struct net_device *dev;
2118 struct net *net;
2119 struct in6_addr *addr;
2120};
2121
2122static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2123{
2124 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2125 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2126 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2127
David S. Millerd1918542011-12-28 20:19:20 -05002128 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002129 rt != net->ipv6.ip6_null_entry &&
2130 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2131 /* remove prefsrc entry */
2132 rt->rt6i_prefsrc.plen = 0;
2133 }
2134 return 0;
2135}
2136
2137void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2138{
2139 struct net *net = dev_net(ifp->idev->dev);
2140 struct arg_dev_net_ip adni = {
2141 .dev = ifp->idev->dev,
2142 .net = net,
2143 .addr = &ifp->addr,
2144 };
2145 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2146}
2147
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002148struct arg_dev_net {
2149 struct net_device *dev;
2150 struct net *net;
2151};
2152
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153static int fib6_ifdown(struct rt6_info *rt, void *arg)
2154{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002155 const struct arg_dev_net *adn = arg;
2156 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002157
David S. Millerd1918542011-12-28 20:19:20 -05002158 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002159 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002161
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 return 0;
2163}
2164
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002165void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002167 struct arg_dev_net adn = {
2168 .dev = dev,
2169 .net = net,
2170 };
2171
2172 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002173 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174}
2175
Eric Dumazet95c96172012-04-15 05:58:06 +00002176struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002178 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179};
2180
2181static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2182{
2183 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2184 struct inet6_dev *idev;
2185
2186 /* In IPv6 pmtu discovery is not optional,
2187 so that RTAX_MTU lock cannot disable it.
2188 We still use this lock to block changes
2189 caused by addrconf/ndisc.
2190 */
2191
2192 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002193 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 return 0;
2195
2196 /* For administrative MTU increase, there is no way to discover
2197 IPv6 PMTU increase, so PMTU increase should be updated here.
2198 Since RFC 1981 doesn't include administrative MTU increase
2199 update PMTU increase is a MUST. (i.e. jumbo frame)
2200 */
2201 /*
2202 If new MTU is less than route PMTU, this new MTU will be the
2203 lowest MTU in the path, update the route PMTU to reflect PMTU
2204 decreases; if new MTU is greater than route PMTU, and the
2205 old MTU is the lowest MTU in the path, update the route PMTU
2206 to reflect the increase. In this case if the other nodes' MTU
2207 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2208 PMTU discouvery.
2209 */
David S. Millerd1918542011-12-28 20:19:20 -05002210 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002211 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2212 (dst_mtu(&rt->dst) >= arg->mtu ||
2213 (dst_mtu(&rt->dst) < arg->mtu &&
2214 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002215 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002216 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217 return 0;
2218}
2219
Eric Dumazet95c96172012-04-15 05:58:06 +00002220void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221{
Thomas Grafc71099a2006-08-04 23:20:06 -07002222 struct rt6_mtu_change_arg arg = {
2223 .dev = dev,
2224 .mtu = mtu,
2225 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002227 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228}
2229
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002230static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002231 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002232 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002233 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002234 [RTA_PRIORITY] = { .type = NLA_U32 },
2235 [RTA_METRICS] = { .type = NLA_NESTED },
2236};
2237
2238static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2239 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240{
Thomas Graf86872cb2006-08-22 00:01:08 -07002241 struct rtmsg *rtm;
2242 struct nlattr *tb[RTA_MAX+1];
2243 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244
Thomas Graf86872cb2006-08-22 00:01:08 -07002245 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2246 if (err < 0)
2247 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248
Thomas Graf86872cb2006-08-22 00:01:08 -07002249 err = -EINVAL;
2250 rtm = nlmsg_data(nlh);
2251 memset(cfg, 0, sizeof(*cfg));
2252
2253 cfg->fc_table = rtm->rtm_table;
2254 cfg->fc_dst_len = rtm->rtm_dst_len;
2255 cfg->fc_src_len = rtm->rtm_src_len;
2256 cfg->fc_flags = RTF_UP;
2257 cfg->fc_protocol = rtm->rtm_protocol;
2258
2259 if (rtm->rtm_type == RTN_UNREACHABLE)
2260 cfg->fc_flags |= RTF_REJECT;
2261
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002262 if (rtm->rtm_type == RTN_LOCAL)
2263 cfg->fc_flags |= RTF_LOCAL;
2264
Thomas Graf86872cb2006-08-22 00:01:08 -07002265 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2266 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002267 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002268
2269 if (tb[RTA_GATEWAY]) {
2270 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2271 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002273
2274 if (tb[RTA_DST]) {
2275 int plen = (rtm->rtm_dst_len + 7) >> 3;
2276
2277 if (nla_len(tb[RTA_DST]) < plen)
2278 goto errout;
2279
2280 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002282
2283 if (tb[RTA_SRC]) {
2284 int plen = (rtm->rtm_src_len + 7) >> 3;
2285
2286 if (nla_len(tb[RTA_SRC]) < plen)
2287 goto errout;
2288
2289 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002291
Daniel Walterc3968a82011-04-13 21:10:57 +00002292 if (tb[RTA_PREFSRC])
2293 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2294
Thomas Graf86872cb2006-08-22 00:01:08 -07002295 if (tb[RTA_OIF])
2296 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2297
2298 if (tb[RTA_PRIORITY])
2299 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2300
2301 if (tb[RTA_METRICS]) {
2302 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2303 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002305
2306 if (tb[RTA_TABLE])
2307 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2308
2309 err = 0;
2310errout:
2311 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312}
2313
Thomas Grafc127ea22007-03-22 11:58:32 -07002314static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315{
Thomas Graf86872cb2006-08-22 00:01:08 -07002316 struct fib6_config cfg;
2317 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
Thomas Graf86872cb2006-08-22 00:01:08 -07002319 err = rtm_to_fib6_config(skb, nlh, &cfg);
2320 if (err < 0)
2321 return err;
2322
2323 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324}
2325
Thomas Grafc127ea22007-03-22 11:58:32 -07002326static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327{
Thomas Graf86872cb2006-08-22 00:01:08 -07002328 struct fib6_config cfg;
2329 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330
Thomas Graf86872cb2006-08-22 00:01:08 -07002331 err = rtm_to_fib6_config(skb, nlh, &cfg);
2332 if (err < 0)
2333 return err;
2334
2335 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336}
2337
Thomas Graf339bf982006-11-10 14:10:15 -08002338static inline size_t rt6_nlmsg_size(void)
2339{
2340 return NLMSG_ALIGN(sizeof(struct rtmsg))
2341 + nla_total_size(16) /* RTA_SRC */
2342 + nla_total_size(16) /* RTA_DST */
2343 + nla_total_size(16) /* RTA_GATEWAY */
2344 + nla_total_size(16) /* RTA_PREFSRC */
2345 + nla_total_size(4) /* RTA_TABLE */
2346 + nla_total_size(4) /* RTA_IIF */
2347 + nla_total_size(4) /* RTA_OIF */
2348 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002349 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002350 + nla_total_size(sizeof(struct rta_cacheinfo));
2351}
2352
Brian Haley191cd582008-08-14 15:33:21 -07002353static int rt6_fill_node(struct net *net,
2354 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002355 struct in6_addr *dst, struct in6_addr *src,
2356 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002357 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358{
2359 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002360 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002361 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002362 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002363 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364
2365 if (prefix) { /* user wants prefix routes only */
2366 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2367 /* success since this is not a prefix route */
2368 return 1;
2369 }
2370 }
2371
Thomas Graf2d7202b2006-08-22 00:01:27 -07002372 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002373 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002374 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002375
2376 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 rtm->rtm_family = AF_INET6;
2378 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2379 rtm->rtm_src_len = rt->rt6i_src.plen;
2380 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002381 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002382 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002383 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002384 table = RT6_TABLE_UNSPEC;
2385 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002386 if (nla_put_u32(skb, RTA_TABLE, table))
2387 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002388 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002390 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002391 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002392 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 rtm->rtm_type = RTN_LOCAL;
2394 else
2395 rtm->rtm_type = RTN_UNICAST;
2396 rtm->rtm_flags = 0;
2397 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2398 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002399 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 rtm->rtm_protocol = RTPROT_REDIRECT;
2401 else if (rt->rt6i_flags & RTF_ADDRCONF)
2402 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002403 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 rtm->rtm_protocol = RTPROT_RA;
2405
David S. Miller38308472011-12-03 18:02:47 -05002406 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 rtm->rtm_flags |= RTM_F_CLONED;
2408
2409 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002410 if (nla_put(skb, RTA_DST, 16, dst))
2411 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002412 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002414 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2415 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416#ifdef CONFIG_IPV6_SUBTREES
2417 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002418 if (nla_put(skb, RTA_SRC, 16, src))
2419 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002420 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002421 } else if (rtm->rtm_src_len &&
2422 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2423 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002425 if (iif) {
2426#ifdef CONFIG_IPV6_MROUTE
2427 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002428 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002429 if (err <= 0) {
2430 if (!nowait) {
2431 if (err == 0)
2432 return 0;
2433 goto nla_put_failure;
2434 } else {
2435 if (err == -EMSGSIZE)
2436 goto nla_put_failure;
2437 }
2438 }
2439 } else
2440#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002441 if (nla_put_u32(skb, RTA_IIF, iif))
2442 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002443 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002445 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2446 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2447 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002449
Daniel Walterc3968a82011-04-13 21:10:57 +00002450 if (rt->rt6i_prefsrc.plen) {
2451 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002452 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002453 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2454 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002455 }
2456
David S. Millerdefb3512010-12-08 21:16:57 -08002457 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002458 goto nla_put_failure;
2459
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002460 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002461 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002462 if (n) {
2463 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2464 rcu_read_unlock();
2465 goto nla_put_failure;
2466 }
2467 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002468 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002469
David S. Millerc78679e2012-04-01 20:27:33 -04002470 if (rt->dst.dev &&
2471 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2472 goto nla_put_failure;
2473 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2474 goto nla_put_failure;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002475 if (!(rt->rt6i_flags & RTF_EXPIRES))
2476 expires = 0;
David S. Millerd1918542011-12-28 20:19:20 -05002477 else if (rt->dst.expires - jiffies < INT_MAX)
2478 expires = rt->dst.expires - jiffies;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002479 else
2480 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002481
David S. Miller87a50692012-07-10 05:06:14 -07002482 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002483 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484
Thomas Graf2d7202b2006-08-22 00:01:27 -07002485 return nlmsg_end(skb, nlh);
2486
2487nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002488 nlmsg_cancel(skb, nlh);
2489 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490}
2491
Patrick McHardy1b43af52006-08-10 23:11:17 -07002492int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493{
2494 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2495 int prefix;
2496
Thomas Graf2d7202b2006-08-22 00:01:27 -07002497 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2498 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2500 } else
2501 prefix = 0;
2502
Brian Haley191cd582008-08-14 15:33:21 -07002503 return rt6_fill_node(arg->net,
2504 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002506 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507}
2508
Thomas Grafc127ea22007-03-22 11:58:32 -07002509static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002511 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002512 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002514 struct sk_buff *skb;
2515 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002516 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002517 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002518
2519 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2520 if (err < 0)
2521 goto errout;
2522
2523 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002524 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002525
2526 if (tb[RTA_SRC]) {
2527 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2528 goto errout;
2529
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002530 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002531 }
2532
2533 if (tb[RTA_DST]) {
2534 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2535 goto errout;
2536
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002537 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002538 }
2539
2540 if (tb[RTA_IIF])
2541 iif = nla_get_u32(tb[RTA_IIF]);
2542
2543 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002544 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002545
2546 if (iif) {
2547 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002548 int flags = 0;
2549
Daniel Lezcano55786892008-03-04 13:47:47 -08002550 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002551 if (!dev) {
2552 err = -ENODEV;
2553 goto errout;
2554 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002555
2556 fl6.flowi6_iif = iif;
2557
2558 if (!ipv6_addr_any(&fl6.saddr))
2559 flags |= RT6_LOOKUP_F_HAS_SADDR;
2560
2561 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2562 flags);
2563 } else {
2564 fl6.flowi6_oif = oif;
2565
2566 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002567 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568
2569 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002570 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002571 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002572 err = -ENOBUFS;
2573 goto errout;
2574 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
2576 /* Reserve room for dummy headers, this skb can pass
2577 through good chunk of routing engine.
2578 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002579 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2581
Changli Gaod8d1f302010-06-10 23:31:35 -07002582 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583
David S. Miller4c9483b2011-03-12 16:22:43 -05002584 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002586 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002588 kfree_skb(skb);
2589 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 }
2591
Daniel Lezcano55786892008-03-04 13:47:47 -08002592 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002593errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595}
2596
Thomas Graf86872cb2006-08-22 00:01:08 -07002597void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598{
2599 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002600 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002601 u32 seq;
2602 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002604 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002605 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002606
Thomas Graf339bf982006-11-10 14:10:15 -08002607 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002608 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002609 goto errout;
2610
Brian Haley191cd582008-08-14 15:33:21 -07002611 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002612 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002613 if (err < 0) {
2614 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2615 WARN_ON(err == -EMSGSIZE);
2616 kfree_skb(skb);
2617 goto errout;
2618 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002619 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2620 info->nlh, gfp_any());
2621 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002622errout:
2623 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002624 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625}
2626
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002627static int ip6_route_dev_notify(struct notifier_block *this,
2628 unsigned long event, void *data)
2629{
2630 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002631 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002632
2633 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002634 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002635 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2636#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002637 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002638 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002639 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002640 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2641#endif
2642 }
2643
2644 return NOTIFY_OK;
2645}
2646
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647/*
2648 * /proc
2649 */
2650
2651#ifdef CONFIG_PROC_FS
2652
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653struct rt6_proc_arg
2654{
2655 char *buffer;
2656 int offset;
2657 int length;
2658 int skip;
2659 int len;
2660};
2661
2662static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2663{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002664 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002665 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002667 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668
2669#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002670 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002672 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002674 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002675 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002676 if (n) {
2677 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002679 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002681 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002682 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002683 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2684 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002685 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 return 0;
2687}
2688
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002689static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002691 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002692 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002693 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694}
2695
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002696static int ipv6_route_open(struct inode *inode, struct file *file)
2697{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002698 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002699}
2700
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002701static const struct file_operations ipv6_route_proc_fops = {
2702 .owner = THIS_MODULE,
2703 .open = ipv6_route_open,
2704 .read = seq_read,
2705 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002706 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002707};
2708
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2710{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002711 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002713 net->ipv6.rt6_stats->fib_nodes,
2714 net->ipv6.rt6_stats->fib_route_nodes,
2715 net->ipv6.rt6_stats->fib_rt_alloc,
2716 net->ipv6.rt6_stats->fib_rt_entries,
2717 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002718 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002719 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720
2721 return 0;
2722}
2723
2724static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2725{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002726 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002727}
2728
Arjan van de Ven9a321442007-02-12 00:55:35 -08002729static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 .owner = THIS_MODULE,
2731 .open = rt6_stats_seq_open,
2732 .read = seq_read,
2733 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002734 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735};
2736#endif /* CONFIG_PROC_FS */
2737
2738#ifdef CONFIG_SYSCTL
2739
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002741int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742 void __user *buffer, size_t *lenp, loff_t *ppos)
2743{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002744 struct net *net;
2745 int delay;
2746 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002748
2749 net = (struct net *)ctl->extra1;
2750 delay = net->ipv6.sysctl.flush_delay;
2751 proc_dointvec(ctl, write, buffer, lenp, ppos);
2752 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2753 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754}
2755
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002756ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002757 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002759 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002761 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002762 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763 },
2764 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002766 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 .maxlen = sizeof(int),
2768 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002769 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770 },
2771 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002773 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 .maxlen = sizeof(int),
2775 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002776 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 },
2778 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002780 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 .maxlen = sizeof(int),
2782 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002783 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 },
2785 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002787 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 .maxlen = sizeof(int),
2789 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002790 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 },
2792 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002794 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 .maxlen = sizeof(int),
2796 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002797 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 },
2799 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002801 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 .maxlen = sizeof(int),
2803 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002804 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 },
2806 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002808 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 .maxlen = sizeof(int),
2810 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002811 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 },
2813 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002815 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 .maxlen = sizeof(int),
2817 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002818 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 },
2820 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002822 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 .maxlen = sizeof(int),
2824 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002825 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002827 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828};
2829
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002830struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002831{
2832 struct ctl_table *table;
2833
2834 table = kmemdup(ipv6_route_table_template,
2835 sizeof(ipv6_route_table_template),
2836 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002837
2838 if (table) {
2839 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002840 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002841 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002842 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2843 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2844 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2845 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2846 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2847 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2848 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002849 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002850 }
2851
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002852 return table;
2853}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854#endif
2855
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002856static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002857{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002858 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002859
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002860 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2861 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002862
Eric Dumazetfc66f952010-10-08 06:37:34 +00002863 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2864 goto out_ip6_dst_ops;
2865
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002866 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2867 sizeof(*net->ipv6.ip6_null_entry),
2868 GFP_KERNEL);
2869 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002870 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002871 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002872 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002873 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002874 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2875 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002876
2877#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2878 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2879 sizeof(*net->ipv6.ip6_prohibit_entry),
2880 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002881 if (!net->ipv6.ip6_prohibit_entry)
2882 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002883 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002884 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002885 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002886 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2887 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002888
2889 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2890 sizeof(*net->ipv6.ip6_blk_hole_entry),
2891 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002892 if (!net->ipv6.ip6_blk_hole_entry)
2893 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002894 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002895 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002896 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002897 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2898 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002899#endif
2900
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002901 net->ipv6.sysctl.flush_delay = 0;
2902 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2903 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2904 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2905 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2906 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2907 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2908 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2909
Benjamin Thery6891a342008-03-04 13:49:47 -08002910 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2911
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002912 ret = 0;
2913out:
2914 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002915
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002916#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2917out_ip6_prohibit_entry:
2918 kfree(net->ipv6.ip6_prohibit_entry);
2919out_ip6_null_entry:
2920 kfree(net->ipv6.ip6_null_entry);
2921#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002922out_ip6_dst_entries:
2923 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002924out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002925 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002926}
2927
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002928static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002929{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002930 kfree(net->ipv6.ip6_null_entry);
2931#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2932 kfree(net->ipv6.ip6_prohibit_entry);
2933 kfree(net->ipv6.ip6_blk_hole_entry);
2934#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002935 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002936}
2937
Thomas Grafd1896342012-06-18 12:08:33 +00002938static int __net_init ip6_route_net_init_late(struct net *net)
2939{
2940#ifdef CONFIG_PROC_FS
2941 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2942 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2943#endif
2944 return 0;
2945}
2946
2947static void __net_exit ip6_route_net_exit_late(struct net *net)
2948{
2949#ifdef CONFIG_PROC_FS
2950 proc_net_remove(net, "ipv6_route");
2951 proc_net_remove(net, "rt6_stats");
2952#endif
2953}
2954
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002955static struct pernet_operations ip6_route_net_ops = {
2956 .init = ip6_route_net_init,
2957 .exit = ip6_route_net_exit,
2958};
2959
David S. Millerc3426b42012-06-09 16:27:05 -07002960static int __net_init ipv6_inetpeer_init(struct net *net)
2961{
2962 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2963
2964 if (!bp)
2965 return -ENOMEM;
2966 inet_peer_base_init(bp);
2967 net->ipv6.peers = bp;
2968 return 0;
2969}
2970
2971static void __net_exit ipv6_inetpeer_exit(struct net *net)
2972{
2973 struct inet_peer_base *bp = net->ipv6.peers;
2974
2975 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07002976 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07002977 kfree(bp);
2978}
2979
David S. Miller2b823f72012-06-09 19:00:16 -07002980static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07002981 .init = ipv6_inetpeer_init,
2982 .exit = ipv6_inetpeer_exit,
2983};
2984
Thomas Grafd1896342012-06-18 12:08:33 +00002985static struct pernet_operations ip6_route_net_late_ops = {
2986 .init = ip6_route_net_init_late,
2987 .exit = ip6_route_net_exit_late,
2988};
2989
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002990static struct notifier_block ip6_route_dev_notifier = {
2991 .notifier_call = ip6_route_dev_notify,
2992 .priority = 0,
2993};
2994
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002995int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002997 int ret;
2998
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002999 ret = -ENOMEM;
3000 ip6_dst_ops_template.kmem_cachep =
3001 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3002 SLAB_HWCACHE_ALIGN, NULL);
3003 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003004 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003005
Eric Dumazetfc66f952010-10-08 06:37:34 +00003006 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003007 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003008 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003009
David S. Millerc3426b42012-06-09 16:27:05 -07003010 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3011 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003012 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003013
David S. Miller7e52b332012-06-15 15:51:55 -07003014 ret = register_pernet_subsys(&ip6_route_net_ops);
3015 if (ret)
3016 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003017
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003018 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3019
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003020 /* Registering of the loopback is done before this portion of code,
3021 * the loopback reference in rt6_info will not be taken, do it
3022 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003023 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003024 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3025 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003026 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003027 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003028 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003029 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3030 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003031 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003032 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003033 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003034
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003035 ret = xfrm6_init();
3036 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003037 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003038
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003039 ret = fib6_rules_init();
3040 if (ret)
3041 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003042
Thomas Grafd1896342012-06-18 12:08:33 +00003043 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3044 if (ret)
3045 goto fib6_rules_init;
3046
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003047 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003048 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3049 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3050 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003051 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003052
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003053 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003054 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003055 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003056
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003057out:
3058 return ret;
3059
Thomas Grafd1896342012-06-18 12:08:33 +00003060out_register_late_subsys:
3061 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003062fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003063 fib6_rules_cleanup();
3064xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003065 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003066out_fib6_init:
3067 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003068out_register_subsys:
3069 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003070out_register_inetpeer:
3071 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003072out_dst_entries:
3073 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003074out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003075 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003076 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003077}
3078
3079void ip6_route_cleanup(void)
3080{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003081 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003082 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003083 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003085 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003086 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003087 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003088 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003089 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090}