blob: f52cf83bb1e01d3554a73a2480dd278401be3cc3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020047#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <net/snmp.h>
49#include <net/ipv6.h>
50#include <net/ip6_fib.h>
51#include <net/ip6_route.h>
52#include <net/ndisc.h>
53#include <net/addrconf.h>
54#include <net/tcp.h>
55#include <linux/rtnetlink.h>
56#include <net/dst.h>
57#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070058#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070059#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61#include <asm/uaccess.h>
62
63#ifdef CONFIG_SYSCTL
64#include <linux/sysctl.h>
65#endif
66
Gao feng1716a962012-04-06 00:13:10 +000067static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +000068 const struct in6_addr *dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080070static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000071static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072static struct dst_entry *ip6_negative_advice(struct dst_entry *);
73static void ip6_dst_destroy(struct dst_entry *);
74static void ip6_dst_ifdown(struct dst_entry *,
75 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080076static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78static int ip6_pkt_discard(struct sk_buff *skb);
79static int ip6_pkt_discard_out(struct sk_buff *skb);
80static void ip6_link_failure(struct sk_buff *skb);
81static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
David S. Miller6e157b62012-07-12 00:05:02 -070082static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080084#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080085static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000086 const struct in6_addr *prefix, int prefixlen,
87 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +000088 unsigned int pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080089static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000090 const struct in6_addr *prefix, int prefixlen,
91 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080092#endif
93
David S. Miller06582542011-01-27 14:58:42 -080094static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
95{
96 struct rt6_info *rt = (struct rt6_info *) dst;
97 struct inet_peer *peer;
98 u32 *p = NULL;
99
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000100 if (!(rt->dst.flags & DST_HOST))
101 return NULL;
102
David S. Millerfbfe95a2012-06-08 23:24:18 -0700103 peer = rt6_get_peer_create(rt);
David S. Miller06582542011-01-27 14:58:42 -0800104 if (peer) {
105 u32 *old_p = __DST_METRICS_PTR(old);
106 unsigned long prev, new;
107
108 p = peer->metrics;
109 if (inet_metrics_new(peer))
110 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
111
112 new = (unsigned long) p;
113 prev = cmpxchg(&dst->_metrics, old, new);
114
115 if (prev != old) {
116 p = __DST_METRICS_PTR(prev);
117 if (prev & DST_METRICS_READ_ONLY)
118 p = NULL;
119 }
120 }
121 return p;
122}
123
David S. Millerf894cbf2012-07-02 21:52:24 -0700124static inline const void *choose_neigh_daddr(struct rt6_info *rt,
125 struct sk_buff *skb,
126 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500127{
128 struct in6_addr *p = &rt->rt6i_gateway;
129
David S. Millera7563f32012-01-26 16:29:16 -0500130 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500131 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700132 else if (skb)
133 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500134 return daddr;
135}
136
David S. Millerf894cbf2012-07-02 21:52:24 -0700137static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
138 struct sk_buff *skb,
139 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700140{
David S. Miller39232972012-01-26 15:22:32 -0500141 struct rt6_info *rt = (struct rt6_info *) dst;
142 struct neighbour *n;
143
David S. Millerf894cbf2012-07-02 21:52:24 -0700144 daddr = choose_neigh_daddr(rt, skb, daddr);
David S. Miller39232972012-01-26 15:22:32 -0500145 n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500146 if (n)
147 return n;
148 return neigh_create(&nd_tbl, daddr, dst->dev);
149}
150
David S. Miller8ade06c2011-12-29 18:51:57 -0500151static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
David S. Millerf83c7792011-12-28 15:41:23 -0500152{
David S. Miller8ade06c2011-12-29 18:51:57 -0500153 struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
154 if (!n) {
155 n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
156 if (IS_ERR(n))
157 return PTR_ERR(n);
158 }
David S. Miller97cac082012-07-02 22:43:47 -0700159 rt->n = n;
David S. Millerf83c7792011-12-28 15:41:23 -0500160
161 return 0;
David S. Millerd3aaeb32011-07-18 00:40:17 -0700162}
163
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800164static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800166 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 .gc = ip6_dst_gc,
168 .gc_thresh = 1024,
169 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800170 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000171 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800172 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .destroy = ip6_dst_destroy,
174 .ifdown = ip6_dst_ifdown,
175 .negative_advice = ip6_negative_advice,
176 .link_failure = ip6_link_failure,
177 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700178 .redirect = rt6_do_redirect,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700179 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700180 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181};
182
Steffen Klassertebb762f2011-11-23 02:12:51 +0000183static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800184{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000185 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
186
187 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800188}
189
David S. Miller14e50e52007-05-24 18:17:54 -0700190static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
191{
192}
193
Held Bernhard0972ddb2011-04-24 22:07:32 +0000194static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
195 unsigned long old)
196{
197 return NULL;
198}
199
David S. Miller14e50e52007-05-24 18:17:54 -0700200static struct dst_ops ip6_dst_blackhole_ops = {
201 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800202 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700203 .destroy = ip6_dst_destroy,
204 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000205 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800206 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700207 .update_pmtu = ip6_rt_blackhole_update_pmtu,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000208 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700209 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700210};
211
David S. Miller62fa8a82011-01-26 20:51:05 -0800212static const u32 ip6_template_metrics[RTAX_MAX] = {
213 [RTAX_HOPLIMIT - 1] = 255,
214};
215
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800216static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700217 .dst = {
218 .__refcnt = ATOMIC_INIT(1),
219 .__use = 1,
220 .obsolete = -1,
221 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700222 .input = ip6_pkt_discard,
223 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 },
225 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700226 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .rt6i_metric = ~(u32) 0,
228 .rt6i_ref = ATOMIC_INIT(1),
229};
230
Thomas Graf101367c2006-08-04 03:39:02 -0700231#ifdef CONFIG_IPV6_MULTIPLE_TABLES
232
David S. Miller6723ab52006-10-18 21:20:57 -0700233static int ip6_pkt_prohibit(struct sk_buff *skb);
234static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700235
Adrian Bunk280a34c2008-04-21 02:29:32 -0700236static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700237 .dst = {
238 .__refcnt = ATOMIC_INIT(1),
239 .__use = 1,
240 .obsolete = -1,
241 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700242 .input = ip6_pkt_prohibit,
243 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700244 },
245 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700246 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700247 .rt6i_metric = ~(u32) 0,
248 .rt6i_ref = ATOMIC_INIT(1),
249};
250
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800251static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700252 .dst = {
253 .__refcnt = ATOMIC_INIT(1),
254 .__use = 1,
255 .obsolete = -1,
256 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700257 .input = dst_discard,
258 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700259 },
260 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700261 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700262 .rt6i_metric = ~(u32) 0,
263 .rt6i_ref = ATOMIC_INIT(1),
264};
265
266#endif
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700269static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700270 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700271 int flags,
272 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
David S. Miller97bab732012-06-09 22:36:36 -0700274 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
275 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700276
David S. Miller97bab732012-06-09 22:36:36 -0700277 if (rt) {
Steffen Klasserta2de86f2012-07-05 03:18:28 +0000278 memset(&rt->n, 0,
David S. Miller38308472011-12-03 18:02:47 -0500279 sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller8b96d222012-06-11 02:01:56 -0700280 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
David S. Miller97bab732012-06-09 22:36:36 -0700281 }
David S. Millercf911662011-04-28 14:31:47 -0700282 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283}
284
285static void ip6_dst_destroy(struct dst_entry *dst)
286{
287 struct rt6_info *rt = (struct rt6_info *)dst;
288 struct inet6_dev *idev = rt->rt6i_idev;
289
David S. Miller97cac082012-07-02 22:43:47 -0700290 if (rt->n)
291 neigh_release(rt->n);
292
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000293 if (!(rt->dst.flags & DST_HOST))
294 dst_destroy_metrics_generic(dst);
295
David S. Miller38308472011-12-03 18:02:47 -0500296 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 rt->rt6i_idev = NULL;
298 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900299 }
Gao feng1716a962012-04-06 00:13:10 +0000300
301 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
302 dst_release(dst->from);
303
David S. Miller97bab732012-06-09 22:36:36 -0700304 if (rt6_has_peer(rt)) {
305 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800306 inet_putpeer(peer);
307 }
308}
309
David S. Miller6431cbc2011-02-07 20:38:06 -0800310static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
311
312static u32 rt6_peer_genid(void)
313{
314 return atomic_read(&__rt6_peer_genid);
315}
316
David S. Millerb3419362010-11-30 12:27:11 -0800317void rt6_bind_peer(struct rt6_info *rt, int create)
318{
David S. Miller97bab732012-06-09 22:36:36 -0700319 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800320 struct inet_peer *peer;
321
David S. Miller97bab732012-06-09 22:36:36 -0700322 base = inetpeer_base_ptr(rt->_rt6i_peer);
323 if (!base)
324 return;
325
326 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700327 if (peer) {
328 if (!rt6_set_peer(rt, peer))
329 inet_putpeer(peer);
330 else
331 rt->rt6i_peer_genid = rt6_peer_genid();
332 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
335static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
336 int how)
337{
338 struct rt6_info *rt = (struct rt6_info *)dst;
339 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800340 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900341 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
David S. Miller97cac082012-07-02 22:43:47 -0700343 if (dev != loopback_dev) {
344 if (idev && idev->dev == dev) {
345 struct inet6_dev *loopback_idev =
346 in6_dev_get(loopback_dev);
347 if (loopback_idev) {
348 rt->rt6i_idev = loopback_idev;
349 in6_dev_put(idev);
350 }
351 }
352 if (rt->n && rt->n->dev == dev) {
353 rt->n->dev = loopback_dev;
354 dev_hold(loopback_dev);
355 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 }
357 }
358}
359
Eric Dumazeta50feda2012-05-18 18:57:34 +0000360static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
Gao feng1716a962012-04-06 00:13:10 +0000362 struct rt6_info *ort = NULL;
363
364 if (rt->rt6i_flags & RTF_EXPIRES) {
365 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000366 return true;
Gao feng1716a962012-04-06 00:13:10 +0000367 } else if (rt->dst.from) {
368 ort = (struct rt6_info *) rt->dst.from;
369 return (ort->rt6i_flags & RTF_EXPIRES) &&
370 time_after(jiffies, ort->dst.expires);
371 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000372 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373}
374
Eric Dumazeta50feda2012-05-18 18:57:34 +0000375static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700376{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000377 return ipv6_addr_type(daddr) &
378 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700379}
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700382 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 */
384
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800385static inline struct rt6_info *rt6_device_match(struct net *net,
386 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000387 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700389 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
391 struct rt6_info *local = NULL;
392 struct rt6_info *sprt;
393
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900394 if (!oif && ipv6_addr_any(saddr))
395 goto out;
396
Changli Gaod8d1f302010-06-10 23:31:35 -0700397 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500398 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900399
400 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 if (dev->ifindex == oif)
402 return sprt;
403 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500404 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700406 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900408 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 local->rt6i_idev->dev->ifindex == oif))
410 continue;
411 }
412 local = sprt;
413 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900414 } else {
415 if (ipv6_chk_addr(net, saddr, dev,
416 flags & RT6_LOOKUP_F_IFACE))
417 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900419 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900421 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 if (local)
423 return local;
424
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700425 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800426 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900428out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 return rt;
430}
431
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800432#ifdef CONFIG_IPV6_ROUTER_PREF
433static void rt6_probe(struct rt6_info *rt)
434{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000435 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800436 /*
437 * Okay, this does not seem to be appropriate
438 * for now, however, we need to check if it
439 * is really so; aka Router Reachability Probing.
440 *
441 * Router Reachability Probe MUST be rate-limited
442 * to no more than one per minute.
443 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000444 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700445 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800446 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000447 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800448 read_lock_bh(&neigh->lock);
449 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800450 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800451 struct in6_addr mcaddr;
452 struct in6_addr *target;
453
454 neigh->updated = jiffies;
455 read_unlock_bh(&neigh->lock);
456
457 target = (struct in6_addr *)&neigh->primary_key;
458 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500459 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000460 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800461 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000462 }
463out:
464 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800465}
466#else
467static inline void rt6_probe(struct rt6_info *rt)
468{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800469}
470#endif
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800473 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700475static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
David S. Millerd1918542011-12-28 20:19:20 -0500477 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700478 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800479 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700480 if ((dev->flags & IFF_LOOPBACK) &&
481 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
482 return 1;
483 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
Dave Jonesb6f99a22007-03-22 12:27:49 -0700486static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000488 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800489 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000490
491 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700492 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700493 if (rt->rt6i_flags & RTF_NONEXTHOP ||
494 !(rt->rt6i_flags & RTF_GATEWAY))
495 m = 1;
496 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800497 read_lock_bh(&neigh->lock);
498 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700499 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800500#ifdef CONFIG_IPV6_ROUTER_PREF
501 else if (neigh->nud_state & NUD_FAILED)
502 m = 0;
503#endif
504 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800505 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800506 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800507 } else
508 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000509 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800510 return m;
511}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800513static int rt6_score_route(struct rt6_info *rt, int oif,
514 int strict)
515{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700516 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900517
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700518 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700519 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800520 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800521#ifdef CONFIG_IPV6_ROUTER_PREF
522 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
523#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700524 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800525 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800526 return -1;
527 return m;
528}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
David S. Millerf11e6652007-03-24 20:36:25 -0700530static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
531 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800532{
David S. Millerf11e6652007-03-24 20:36:25 -0700533 int m;
534
535 if (rt6_check_expired(rt))
536 goto out;
537
538 m = rt6_score_route(rt, oif, strict);
539 if (m < 0)
540 goto out;
541
542 if (m > *mpri) {
543 if (strict & RT6_LOOKUP_F_REACHABLE)
544 rt6_probe(match);
545 *mpri = m;
546 match = rt;
547 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
548 rt6_probe(rt);
549 }
550
551out:
552 return match;
553}
554
555static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
556 struct rt6_info *rr_head,
557 u32 metric, int oif, int strict)
558{
559 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800560 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
David S. Millerf11e6652007-03-24 20:36:25 -0700562 match = NULL;
563 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700564 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700565 match = find_match(rt, oif, strict, &mpri, match);
566 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700567 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700568 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800569
David S. Millerf11e6652007-03-24 20:36:25 -0700570 return match;
571}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800572
David S. Millerf11e6652007-03-24 20:36:25 -0700573static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
574{
575 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800576 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
David S. Millerf11e6652007-03-24 20:36:25 -0700578 rt0 = fn->rr_ptr;
579 if (!rt0)
580 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
David S. Millerf11e6652007-03-24 20:36:25 -0700582 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800584 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700585 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700586 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700587
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800588 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700589 if (!next || next->rt6i_metric != rt0->rt6i_metric)
590 next = fn->leaf;
591
592 if (next != rt0)
593 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 }
595
David S. Millerd1918542011-12-28 20:19:20 -0500596 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000597 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598}
599
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800600#ifdef CONFIG_IPV6_ROUTE_INFO
601int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000602 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800603{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900604 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800605 struct route_info *rinfo = (struct route_info *) opt;
606 struct in6_addr prefix_buf, *prefix;
607 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900608 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800609 struct rt6_info *rt;
610
611 if (len < sizeof(struct route_info)) {
612 return -EINVAL;
613 }
614
615 /* Sanity check for prefix_len and length */
616 if (rinfo->length > 3) {
617 return -EINVAL;
618 } else if (rinfo->prefix_len > 128) {
619 return -EINVAL;
620 } else if (rinfo->prefix_len > 64) {
621 if (rinfo->length < 2) {
622 return -EINVAL;
623 }
624 } else if (rinfo->prefix_len > 0) {
625 if (rinfo->length < 1) {
626 return -EINVAL;
627 }
628 }
629
630 pref = rinfo->route_pref;
631 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000632 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800633
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900634 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800635
636 if (rinfo->length == 3)
637 prefix = (struct in6_addr *)rinfo->prefix;
638 else {
639 /* this function is safe */
640 ipv6_addr_prefix(&prefix_buf,
641 (struct in6_addr *)rinfo->prefix,
642 rinfo->prefix_len);
643 prefix = &prefix_buf;
644 }
645
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800646 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
647 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800648
649 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700650 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800651 rt = NULL;
652 }
653
654 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800655 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800656 pref);
657 else if (rt)
658 rt->rt6i_flags = RTF_ROUTEINFO |
659 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
660
661 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000662 if (!addrconf_finite_timeout(lifetime))
663 rt6_clean_expires(rt);
664 else
665 rt6_set_expires(rt, jiffies + HZ * lifetime);
666
Changli Gaod8d1f302010-06-10 23:31:35 -0700667 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800668 }
669 return 0;
670}
671#endif
672
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800673#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700674do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800675 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700676 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700677 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700678 if (fn->fn_flags & RTN_TL_ROOT) \
679 goto out; \
680 pn = fn->parent; \
681 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800682 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700683 else \
684 fn = pn; \
685 if (fn->fn_flags & RTN_RTINFO) \
686 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700687 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700688 } \
David S. Miller38308472011-12-03 18:02:47 -0500689} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700690
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800691static struct rt6_info *ip6_pol_route_lookup(struct net *net,
692 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500693 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694{
695 struct fib6_node *fn;
696 struct rt6_info *rt;
697
Thomas Grafc71099a2006-08-04 23:20:06 -0700698 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500699 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700700restart:
701 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500702 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
703 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700704out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700705 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700706 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700707 return rt;
708
709}
710
Florian Westphalea6e5742011-09-05 16:05:44 +0200711struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
712 int flags)
713{
714 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
715}
716EXPORT_SYMBOL_GPL(ip6_route_lookup);
717
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900718struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
719 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700720{
David S. Miller4c9483b2011-03-12 16:22:43 -0500721 struct flowi6 fl6 = {
722 .flowi6_oif = oif,
723 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700724 };
725 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700726 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700727
Thomas Grafadaa70b2006-10-13 15:01:03 -0700728 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500729 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700730 flags |= RT6_LOOKUP_F_HAS_SADDR;
731 }
732
David S. Miller4c9483b2011-03-12 16:22:43 -0500733 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700734 if (dst->error == 0)
735 return (struct rt6_info *) dst;
736
737 dst_release(dst);
738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 return NULL;
740}
741
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900742EXPORT_SYMBOL(rt6_lookup);
743
Thomas Grafc71099a2006-08-04 23:20:06 -0700744/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 It takes new route entry, the addition fails by any reason the
746 route is freed. In any case, if caller does not hold it, it may
747 be destroyed.
748 */
749
Thomas Graf86872cb2006-08-22 00:01:08 -0700750static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
752 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700753 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Thomas Grafc71099a2006-08-04 23:20:06 -0700755 table = rt->rt6i_table;
756 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700757 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700758 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
760 return err;
761}
762
Thomas Graf40e22e82006-08-22 00:00:45 -0700763int ip6_ins_rt(struct rt6_info *rt)
764{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800765 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500766 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800767 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800768 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700769}
770
Gao feng1716a962012-04-06 00:13:10 +0000771static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000772 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000773 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 struct rt6_info *rt;
776
777 /*
778 * Clone the route.
779 */
780
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000781 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800784 int attempts = !in_softirq();
785
David S. Miller38308472011-12-03 18:02:47 -0500786 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500787 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000788 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900789 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000790 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900791 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795#ifdef CONFIG_IPV6_SUBTREES
796 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000797 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 rt->rt6i_src.plen = 128;
799 }
800#endif
801
David S. Miller14deae42009-01-04 16:04:39 -0800802 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500803 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500804 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800805 int saved_rt_min_interval =
806 net->ipv6.sysctl.ip6_rt_gc_min_interval;
807 int saved_rt_elasticity =
808 net->ipv6.sysctl.ip6_rt_gc_elasticity;
809
810 if (attempts-- > 0) {
811 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
812 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
813
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000814 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800815
816 net->ipv6.sysctl.ip6_rt_gc_elasticity =
817 saved_rt_elasticity;
818 net->ipv6.sysctl.ip6_rt_gc_min_interval =
819 saved_rt_min_interval;
820 goto retry;
821 }
822
Joe Perchesf3213832012-05-15 14:11:53 +0000823 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700824 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800825 return NULL;
826 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800827 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800829 return rt;
830}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000832static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
833 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800834{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000835 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
836
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800837 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800838 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700839 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800840 }
841 return rt;
842}
843
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800844static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500845 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846{
847 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800848 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700849 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800851 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700852 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700854 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
856relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700857 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800859restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500860 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
862restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700863 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800864
David S. Miller4c9483b2011-03-12 16:22:43 -0500865 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800866 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800867 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800868 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
Changli Gaod8d1f302010-06-10 23:31:35 -0700870 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700871 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800872
David S. Miller97cac082012-07-02 22:43:47 -0700873 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500874 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800875 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500876 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800877 else
878 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800879
Changli Gaod8d1f302010-06-10 23:31:35 -0700880 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800881 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800882
Changli Gaod8d1f302010-06-10 23:31:35 -0700883 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800884 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700885 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800886 if (!err)
887 goto out2;
888 }
889
890 if (--attempts <= 0)
891 goto out2;
892
893 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700894 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800895 * released someone could insert this route. Relookup.
896 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700897 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800898 goto relookup;
899
900out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800901 if (reachable) {
902 reachable = 0;
903 goto restart_2;
904 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700905 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700906 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700908 rt->dst.lastuse = jiffies;
909 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700910
911 return rt;
912}
913
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800914static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500915 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700916{
David S. Miller4c9483b2011-03-12 16:22:43 -0500917 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700918}
919
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000920static struct dst_entry *ip6_route_input_lookup(struct net *net,
921 struct net_device *dev,
922 struct flowi6 *fl6, int flags)
923{
924 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
925 flags |= RT6_LOOKUP_F_IFACE;
926
927 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
928}
929
Thomas Grafc71099a2006-08-04 23:20:06 -0700930void ip6_route_input(struct sk_buff *skb)
931{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000932 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900933 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700934 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500935 struct flowi6 fl6 = {
936 .flowi6_iif = skb->dev->ifindex,
937 .daddr = iph->daddr,
938 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500939 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500940 .flowi6_mark = skb->mark,
941 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700942 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700943
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000944 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700945}
946
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800947static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500948 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700949{
David S. Miller4c9483b2011-03-12 16:22:43 -0500950 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700951}
952
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700953struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500954 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700955{
956 int flags = 0;
957
David McCullough4dc27d1c2012-06-25 15:42:26 +0000958 fl6->flowi6_iif = net->loopback_dev->ifindex;
959
David S. Miller4c9483b2011-03-12 16:22:43 -0500960 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700961 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700962
David S. Miller4c9483b2011-03-12 16:22:43 -0500963 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700964 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000965 else if (sk)
966 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700967
David S. Miller4c9483b2011-03-12 16:22:43 -0500968 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969}
970
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900971EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
David S. Miller2774c132011-03-01 14:59:04 -0800973struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700974{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700975 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700976 struct dst_entry *new = NULL;
977
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700978 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700979 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700980 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller97bab732012-06-09 22:36:36 -0700981 rt6_init_peer(rt, net->ipv6.peers);
David S. Millercf911662011-04-28 14:31:47 -0700982
Changli Gaod8d1f302010-06-10 23:31:35 -0700983 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700984
David S. Miller14e50e52007-05-24 18:17:54 -0700985 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800986 new->input = dst_discard;
987 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700988
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000989 if (dst_metrics_read_only(&ort->dst))
990 new->_metrics = ort->dst._metrics;
991 else
992 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700993 rt->rt6i_idev = ort->rt6i_idev;
994 if (rt->rt6i_idev)
995 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -0700996
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000997 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +0000998 rt->rt6i_flags = ort->rt6i_flags;
999 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001000 rt->rt6i_metric = 0;
1001
1002 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1003#ifdef CONFIG_IPV6_SUBTREES
1004 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1005#endif
1006
1007 dst_free(new);
1008 }
1009
David S. Miller69ead7a2011-03-01 14:45:33 -08001010 dst_release(dst_orig);
1011 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001012}
David S. Miller14e50e52007-05-24 18:17:54 -07001013
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014/*
1015 * Destination cache support functions
1016 */
1017
1018static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1019{
1020 struct rt6_info *rt;
1021
1022 rt = (struct rt6_info *) dst;
1023
David S. Miller6431cbc2011-02-07 20:38:06 -08001024 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1025 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001026 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001027 rt6_bind_peer(rt, 0);
1028 rt->rt6i_peer_genid = rt6_peer_genid();
1029 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001031 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 return NULL;
1033}
1034
1035static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1036{
1037 struct rt6_info *rt = (struct rt6_info *) dst;
1038
1039 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001040 if (rt->rt6i_flags & RTF_CACHE) {
1041 if (rt6_check_expired(rt)) {
1042 ip6_del_rt(rt);
1043 dst = NULL;
1044 }
1045 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001047 dst = NULL;
1048 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001050 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051}
1052
1053static void ip6_link_failure(struct sk_buff *skb)
1054{
1055 struct rt6_info *rt;
1056
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001057 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Eric Dumazetadf30902009-06-02 05:19:30 +00001059 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001061 if (rt->rt6i_flags & RTF_CACHE)
1062 rt6_update_expires(rt, 0);
1063 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 rt->rt6i_node->fn_sernum = -1;
1065 }
1066}
1067
1068static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1069{
1070 struct rt6_info *rt6 = (struct rt6_info*)dst;
1071
David S. Miller81aded22012-06-15 14:54:11 -07001072 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001074 struct net *net = dev_net(dst->dev);
1075
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 rt6->rt6i_flags |= RTF_MODIFIED;
1077 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001078 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001080 features |= RTAX_FEATURE_ALLFRAG;
1081 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 }
David S. Millerdefb3512010-12-08 21:16:57 -08001083 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001084 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 }
1086}
1087
David S. Miller42ae66c2012-06-15 20:01:57 -07001088void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1089 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001090{
1091 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1092 struct dst_entry *dst;
1093 struct flowi6 fl6;
1094
1095 memset(&fl6, 0, sizeof(fl6));
1096 fl6.flowi6_oif = oif;
1097 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001098 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001099 fl6.daddr = iph->daddr;
1100 fl6.saddr = iph->saddr;
1101 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1102
1103 dst = ip6_route_output(net, NULL, &fl6);
1104 if (!dst->error)
1105 ip6_rt_update_pmtu(dst, ntohl(mtu));
1106 dst_release(dst);
1107}
1108EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1109
1110void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1111{
1112 ip6_update_pmtu(skb, sock_net(sk), mtu,
1113 sk->sk_bound_dev_if, sk->sk_mark);
1114}
1115EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1116
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001117void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1118{
1119 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1120 struct dst_entry *dst;
1121 struct flowi6 fl6;
1122
1123 memset(&fl6, 0, sizeof(fl6));
1124 fl6.flowi6_oif = oif;
1125 fl6.flowi6_mark = mark;
1126 fl6.flowi6_flags = 0;
1127 fl6.daddr = iph->daddr;
1128 fl6.saddr = iph->saddr;
1129 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1130
1131 dst = ip6_route_output(net, NULL, &fl6);
1132 if (!dst->error)
1133 rt6_do_redirect(dst, skb);
1134 dst_release(dst);
1135}
1136EXPORT_SYMBOL_GPL(ip6_redirect);
1137
1138void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1139{
1140 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1141}
1142EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1143
David S. Miller0dbaee32010-12-13 12:52:14 -08001144static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145{
David S. Miller0dbaee32010-12-13 12:52:14 -08001146 struct net_device *dev = dst->dev;
1147 unsigned int mtu = dst_mtu(dst);
1148 struct net *net = dev_net(dev);
1149
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1151
Daniel Lezcano55786892008-03-04 13:47:47 -08001152 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1153 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154
1155 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001156 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1157 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1158 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 * rely only on pmtu discovery"
1160 */
1161 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1162 mtu = IPV6_MAXPLEN;
1163 return mtu;
1164}
1165
Steffen Klassertebb762f2011-11-23 02:12:51 +00001166static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001167{
David S. Millerd33e4552010-12-14 13:01:14 -08001168 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001169 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1170
1171 if (mtu)
1172 return mtu;
1173
1174 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001175
1176 rcu_read_lock();
1177 idev = __in6_dev_get(dst->dev);
1178 if (idev)
1179 mtu = idev->cnf.mtu6;
1180 rcu_read_unlock();
1181
1182 return mtu;
1183}
1184
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001185static struct dst_entry *icmp6_dst_gc_list;
1186static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001187
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001188struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001190 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191{
David S. Miller87a11572011-12-06 17:04:13 -05001192 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 struct rt6_info *rt;
1194 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001195 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
David S. Miller38308472011-12-03 18:02:47 -05001197 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001198 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
David S. Miller8b96d222012-06-11 02:01:56 -07001200 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001201 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001203 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 goto out;
1205 }
1206
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 if (neigh)
1208 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001209 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001210 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001211 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001212 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001213 dst_free(&rt->dst);
1214 return ERR_CAST(neigh);
1215 }
David S. Miller14deae42009-01-04 16:04:39 -08001216 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001218 rt->dst.flags |= DST_HOST;
1219 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001220 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001221 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001222 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001223 rt->rt6i_dst.plen = 128;
1224 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001225 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001227 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001228 rt->dst.next = icmp6_dst_gc_list;
1229 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001230 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231
Daniel Lezcano55786892008-03-04 13:47:47 -08001232 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233
David S. Miller87a11572011-12-06 17:04:13 -05001234 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1235
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236out:
David S. Miller87a11572011-12-06 17:04:13 -05001237 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238}
1239
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001240int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001242 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001243 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001245 spin_lock_bh(&icmp6_dst_lock);
1246 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001247
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 while ((dst = *pprev) != NULL) {
1249 if (!atomic_read(&dst->__refcnt)) {
1250 *pprev = dst->next;
1251 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 } else {
1253 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001254 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 }
1256 }
1257
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001258 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001259
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001260 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261}
1262
David S. Miller1e493d12008-09-10 17:27:15 -07001263static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1264 void *arg)
1265{
1266 struct dst_entry *dst, **pprev;
1267
1268 spin_lock_bh(&icmp6_dst_lock);
1269 pprev = &icmp6_dst_gc_list;
1270 while ((dst = *pprev) != NULL) {
1271 struct rt6_info *rt = (struct rt6_info *) dst;
1272 if (func(rt, arg)) {
1273 *pprev = dst->next;
1274 dst_free(dst);
1275 } else {
1276 pprev = &dst->next;
1277 }
1278 }
1279 spin_unlock_bh(&icmp6_dst_lock);
1280}
1281
Daniel Lezcano569d3642008-01-18 03:56:57 -08001282static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001285 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001286 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1287 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1288 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1289 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1290 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001291 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
Eric Dumazetfc66f952010-10-08 06:37:34 +00001293 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001294 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001295 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 goto out;
1297
Benjamin Thery6891a342008-03-04 13:49:47 -08001298 net->ipv6.ip6_rt_gc_expire++;
1299 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1300 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001301 entries = dst_entries_get_slow(ops);
1302 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001303 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001305 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001306 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307}
1308
1309/* Clean host part of a prefix. Not necessary in radix tree,
1310 but results in cleaner routing tables.
1311
1312 Remove it only when all the things will work!
1313 */
1314
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001315int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316{
David S. Miller5170ae82010-12-12 21:35:57 -08001317 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001318 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001319 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001320 struct inet6_dev *idev;
1321
1322 rcu_read_lock();
1323 idev = __in6_dev_get(dev);
1324 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001325 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001326 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001327 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001328 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 }
1330 return hoplimit;
1331}
David S. Millerabbf46a2010-12-12 21:14:46 -08001332EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333
1334/*
1335 *
1336 */
1337
Thomas Graf86872cb2006-08-22 00:01:08 -07001338int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339{
1340 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001341 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 struct rt6_info *rt = NULL;
1343 struct net_device *dev = NULL;
1344 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001345 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 int addr_type;
1347
Thomas Graf86872cb2006-08-22 00:01:08 -07001348 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 return -EINVAL;
1350#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001351 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 return -EINVAL;
1353#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001354 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001356 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 if (!dev)
1358 goto out;
1359 idev = in6_dev_get(dev);
1360 if (!idev)
1361 goto out;
1362 }
1363
Thomas Graf86872cb2006-08-22 00:01:08 -07001364 if (cfg->fc_metric == 0)
1365 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Matti Vaittinend71314b2011-11-14 00:14:49 +00001367 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001368 if (cfg->fc_nlinfo.nlh &&
1369 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001370 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001371 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001372 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001373 table = fib6_new_table(net, cfg->fc_table);
1374 }
1375 } else {
1376 table = fib6_new_table(net, cfg->fc_table);
1377 }
David S. Miller38308472011-12-03 18:02:47 -05001378
1379 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001380 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001381
David S. Miller8b96d222012-06-11 02:01:56 -07001382 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
David S. Miller38308472011-12-03 18:02:47 -05001384 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 err = -ENOMEM;
1386 goto out;
1387 }
1388
Changli Gaod8d1f302010-06-10 23:31:35 -07001389 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001390
1391 if (cfg->fc_flags & RTF_EXPIRES)
1392 rt6_set_expires(rt, jiffies +
1393 clock_t_to_jiffies(cfg->fc_expires));
1394 else
1395 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
Thomas Graf86872cb2006-08-22 00:01:08 -07001397 if (cfg->fc_protocol == RTPROT_UNSPEC)
1398 cfg->fc_protocol = RTPROT_BOOT;
1399 rt->rt6i_protocol = cfg->fc_protocol;
1400
1401 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001404 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001405 else if (cfg->fc_flags & RTF_LOCAL)
1406 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001408 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
Changli Gaod8d1f302010-06-10 23:31:35 -07001410 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411
Thomas Graf86872cb2006-08-22 00:01:08 -07001412 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1413 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001415 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001417 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1418 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1419 if (!metrics) {
1420 err = -ENOMEM;
1421 goto out;
1422 }
1423 dst_init_metrics(&rt->dst, metrics, 0);
1424 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001426 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1427 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428#endif
1429
Thomas Graf86872cb2006-08-22 00:01:08 -07001430 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431
1432 /* We cannot add true routes via loopback here,
1433 they would result in kernel looping; promote them to reject routes
1434 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001435 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001436 (dev && (dev->flags & IFF_LOOPBACK) &&
1437 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1438 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001440 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 if (dev) {
1442 dev_put(dev);
1443 in6_dev_put(idev);
1444 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001445 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 dev_hold(dev);
1447 idev = in6_dev_get(dev);
1448 if (!idev) {
1449 err = -ENODEV;
1450 goto out;
1451 }
1452 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001453 rt->dst.output = ip6_pkt_discard_out;
1454 rt->dst.input = ip6_pkt_discard;
1455 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1457 goto install_route;
1458 }
1459
Thomas Graf86872cb2006-08-22 00:01:08 -07001460 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001461 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 int gwa_type;
1463
Thomas Graf86872cb2006-08-22 00:01:08 -07001464 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001465 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 gwa_type = ipv6_addr_type(gw_addr);
1467
1468 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1469 struct rt6_info *grt;
1470
1471 /* IPv6 strictly inhibits using not link-local
1472 addresses as nexthop address.
1473 Otherwise, router will not able to send redirects.
1474 It is very good, but in some (rare!) circumstances
1475 (SIT, PtP, NBMA NOARP links) it is handy to allow
1476 some exceptions. --ANK
1477 */
1478 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001479 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 goto out;
1481
Daniel Lezcano55786892008-03-04 13:47:47 -08001482 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
1484 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001485 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 goto out;
1487 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001488 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001489 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 goto out;
1491 }
1492 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001493 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 idev = grt->rt6i_idev;
1495 dev_hold(dev);
1496 in6_dev_hold(grt->rt6i_idev);
1497 }
David S. Miller38308472011-12-03 18:02:47 -05001498 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001500 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
1502 if (err)
1503 goto out;
1504 }
1505 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001506 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 goto out;
1508 }
1509
1510 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001511 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 goto out;
1513
Daniel Walterc3968a82011-04-13 21:10:57 +00001514 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1515 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1516 err = -EINVAL;
1517 goto out;
1518 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001519 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001520 rt->rt6i_prefsrc.plen = 128;
1521 } else
1522 rt->rt6i_prefsrc.plen = 0;
1523
Thomas Graf86872cb2006-08-22 00:01:08 -07001524 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001525 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001526 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 }
1529
Thomas Graf86872cb2006-08-22 00:01:08 -07001530 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
1532install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001533 if (cfg->fc_mx) {
1534 struct nlattr *nla;
1535 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
Thomas Graf86872cb2006-08-22 00:01:08 -07001537 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001538 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001539
1540 if (type) {
1541 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 err = -EINVAL;
1543 goto out;
1544 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001545
David S. Millerdefb3512010-12-08 21:16:57 -08001546 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 }
1549 }
1550
Changli Gaod8d1f302010-06-10 23:31:35 -07001551 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001553 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001554
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001555 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001556
Thomas Graf86872cb2006-08-22 00:01:08 -07001557 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
1559out:
1560 if (dev)
1561 dev_put(dev);
1562 if (idev)
1563 in6_dev_put(idev);
1564 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001565 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 return err;
1567}
1568
Thomas Graf86872cb2006-08-22 00:01:08 -07001569static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570{
1571 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001572 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001573 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001575 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001576 return -ENOENT;
1577
Thomas Grafc71099a2006-08-04 23:20:06 -07001578 table = rt->rt6i_table;
1579 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
Thomas Graf86872cb2006-08-22 00:01:08 -07001581 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001582 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
Thomas Grafc71099a2006-08-04 23:20:06 -07001584 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585
1586 return err;
1587}
1588
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001589int ip6_del_rt(struct rt6_info *rt)
1590{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001591 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001592 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001593 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001594 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001595}
1596
Thomas Graf86872cb2006-08-22 00:01:08 -07001597static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598{
Thomas Grafc71099a2006-08-04 23:20:06 -07001599 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 struct fib6_node *fn;
1601 struct rt6_info *rt;
1602 int err = -ESRCH;
1603
Daniel Lezcano55786892008-03-04 13:47:47 -08001604 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001605 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001606 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
Thomas Grafc71099a2006-08-04 23:20:06 -07001608 read_lock_bh(&table->tb6_lock);
1609
1610 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001611 &cfg->fc_dst, cfg->fc_dst_len,
1612 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001613
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001615 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001616 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001617 (!rt->dst.dev ||
1618 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001620 if (cfg->fc_flags & RTF_GATEWAY &&
1621 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001623 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001625 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001626 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
Thomas Graf86872cb2006-08-22 00:01:08 -07001628 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 }
1630 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001631 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
1633 return err;
1634}
1635
1636/*
1637 * Handle redirects
1638 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001639struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001640 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001641 struct in6_addr gateway;
1642};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001644static struct rt6_info *__ip6_route_redirect(struct net *net,
1645 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001646 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001647 int flags)
1648{
David S. Miller4c9483b2011-03-12 16:22:43 -05001649 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001650 struct rt6_info *rt;
1651 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001652
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001654 * Get the "current" route for this destination and
1655 * check if the redirect has come from approriate router.
1656 *
1657 * RFC 2461 specifies that redirects should only be
1658 * accepted if they come from the nexthop to the target.
1659 * Due to the way the routes are chosen, this notion
1660 * is a bit fuzzy and one might need to check all possible
1661 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
Thomas Grafc71099a2006-08-04 23:20:06 -07001664 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001665 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001666restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001667 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001668 /*
1669 * Current route is on-link; redirect is always invalid.
1670 *
1671 * Seems, previous statement is not true. It could
1672 * be node, which looks for us as on-link (f.e. proxy ndisc)
1673 * But then router serving it might decide, that we should
1674 * know truth 8)8) --ANK (980726).
1675 */
1676 if (rt6_check_expired(rt))
1677 continue;
1678 if (!(rt->rt6i_flags & RTF_GATEWAY))
1679 continue;
David S. Millerd1918542011-12-28 20:19:20 -05001680 if (fl6->flowi6_oif != rt->dst.dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001681 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001682 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001683 continue;
1684 break;
1685 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001686
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001687 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001688 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001689 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001690out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001691 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001692
1693 read_unlock_bh(&table->tb6_lock);
1694
1695 return rt;
1696};
1697
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001698static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1699 const struct in6_addr *src,
1700 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001701 struct net_device *dev)
1702{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001703 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001704 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001705 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001706 .fl6 = {
1707 .flowi6_oif = dev->ifindex,
1708 .daddr = *dest,
1709 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001710 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001711 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001712
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001713 rdfl.gateway = *gateway;
Brian Haley86c36ce2009-10-07 13:58:01 -07001714
Thomas Grafadaa70b2006-10-13 15:01:03 -07001715 if (rt6_need_strict(dest))
1716 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001717
David S. Miller4c9483b2011-03-12 16:22:43 -05001718 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001719 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001720}
1721
David S. Miller6e157b62012-07-12 00:05:02 -07001722static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001723{
David S. Millere8599ff2012-07-11 23:43:53 -07001724 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001725 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001726 struct rt6_info *rt, *nrt = NULL;
1727 const struct in6_addr *target;
David S. Millere8599ff2012-07-11 23:43:53 -07001728 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001729 const struct in6_addr *dest;
1730 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001731 struct inet6_dev *in6_dev;
1732 struct neighbour *neigh;
1733 struct icmp6hdr *icmph;
David S. Miller6e157b62012-07-12 00:05:02 -07001734 int optlen, on_link;
1735 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001736
1737 optlen = skb->tail - skb->transport_header;
1738 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1739
1740 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001741 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001742 return;
1743 }
1744
1745 icmph = icmp6_hdr(skb);
1746 target = (const struct in6_addr *) (icmph + 1);
1747 dest = target + 1;
1748
1749 if (ipv6_addr_is_multicast(dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001750 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001751 return;
1752 }
1753
David S. Miller6e157b62012-07-12 00:05:02 -07001754 on_link = 0;
David S. Millere8599ff2012-07-11 23:43:53 -07001755 if (ipv6_addr_equal(dest, target)) {
1756 on_link = 1;
1757 } else if (ipv6_addr_type(target) !=
1758 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001759 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001760 return;
1761 }
1762
1763 in6_dev = __in6_dev_get(skb->dev);
1764 if (!in6_dev)
1765 return;
1766 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1767 return;
1768
1769 /* RFC2461 8.1:
1770 * The IP source address of the Redirect MUST be the same as the current
1771 * first-hop router for the specified ICMP Destination Address.
1772 */
1773
1774 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1775 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1776 return;
1777 }
David S. Miller6e157b62012-07-12 00:05:02 -07001778
1779 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001780 if (ndopts.nd_opts_tgt_lladdr) {
1781 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1782 skb->dev);
1783 if (!lladdr) {
1784 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1785 return;
1786 }
1787 }
1788
David S. Miller6e157b62012-07-12 00:05:02 -07001789 rt = (struct rt6_info *) dst;
1790 if (rt == net->ipv6.ip6_null_entry) {
1791 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1792 return;
1793 }
1794
1795 /* Redirect received -> path was valid.
1796 * Look, redirects are sent only in response to data packets,
1797 * so that this nexthop apparently is reachable. --ANK
1798 */
1799 dst_confirm(&rt->dst);
1800
David S. Millere8599ff2012-07-11 23:43:53 -07001801 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1802 if (!neigh)
1803 return;
1804
David S. Miller6e157b62012-07-12 00:05:02 -07001805 /* Duplicate redirect: silently ignore. */
1806 old_neigh = rt->n;
1807 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001808 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 /*
1811 * We have finally decided to accept it.
1812 */
1813
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001814 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1816 NEIGH_UPDATE_F_OVERRIDE|
1817 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1818 NEIGH_UPDATE_F_ISROUTER))
1819 );
1820
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001821 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001822 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 goto out;
1824
1825 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1826 if (on_link)
1827 nrt->rt6i_flags &= ~RTF_GATEWAY;
1828
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001829 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001830 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831
Thomas Graf40e22e82006-08-22 00:00:45 -07001832 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 goto out;
1834
Changli Gaod8d1f302010-06-10 23:31:35 -07001835 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001836 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001837 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001838 netevent.new_neigh = neigh;
1839 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001840 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1841
David S. Miller38308472011-12-03 18:02:47 -05001842 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001843 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001844 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 }
1846
1847out:
David S. Millere8599ff2012-07-11 23:43:53 -07001848 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001849}
1850
1851void rt6_redirect(struct sk_buff *skb)
1852{
1853 const struct in6_addr *target;
1854 const struct in6_addr *dest;
1855 const struct in6_addr *src;
1856 const struct in6_addr *saddr;
1857 struct icmp6hdr *icmph;
1858 struct rt6_info *rt;
1859
1860 icmph = icmp6_hdr(skb);
1861 target = (const struct in6_addr *) (icmph + 1);
1862 dest = target + 1;
1863
1864 src = &ipv6_hdr(skb)->daddr;
1865 saddr = &ipv6_hdr(skb)->saddr;
1866
1867 rt = ip6_route_redirect(dest, src, saddr, skb->dev);
1868 rt6_do_redirect(&rt->dst, skb);
Changli Gaod8d1f302010-06-10 23:31:35 -07001869 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870}
1871
1872/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 * Misc support functions
1874 */
1875
Gao feng1716a962012-04-06 00:13:10 +00001876static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001877 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878{
David S. Millerd1918542011-12-28 20:19:20 -05001879 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001880 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1881 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882
1883 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001884 rt->dst.input = ort->dst.input;
1885 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001886 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001888 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001889 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001890 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001891 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 rt->rt6i_idev = ort->rt6i_idev;
1893 if (rt->rt6i_idev)
1894 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001895 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001897 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001898 rt->rt6i_flags = ort->rt6i_flags;
1899 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1900 (RTF_DEFAULT | RTF_ADDRCONF))
1901 rt6_set_from(rt, ort);
1902 else
1903 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 rt->rt6i_metric = 0;
1905
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906#ifdef CONFIG_IPV6_SUBTREES
1907 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1908#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001909 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001910 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 }
1912 return rt;
1913}
1914
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001915#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001916static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001917 const struct in6_addr *prefix, int prefixlen,
1918 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001919{
1920 struct fib6_node *fn;
1921 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001922 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001923
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001924 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001925 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001926 return NULL;
1927
1928 write_lock_bh(&table->tb6_lock);
1929 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001930 if (!fn)
1931 goto out;
1932
Changli Gaod8d1f302010-06-10 23:31:35 -07001933 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001934 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001935 continue;
1936 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1937 continue;
1938 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1939 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001940 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001941 break;
1942 }
1943out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001944 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001945 return rt;
1946}
1947
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001948static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001949 const struct in6_addr *prefix, int prefixlen,
1950 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001951 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001952{
Thomas Graf86872cb2006-08-22 00:01:08 -07001953 struct fib6_config cfg = {
1954 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001955 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001956 .fc_ifindex = ifindex,
1957 .fc_dst_len = prefixlen,
1958 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1959 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001960 .fc_nlinfo.pid = 0,
1961 .fc_nlinfo.nlh = NULL,
1962 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001963 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001964
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001965 cfg.fc_dst = *prefix;
1966 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001967
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001968 /* We should treat it as a default route if prefix length is 0. */
1969 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001970 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001971
Thomas Graf86872cb2006-08-22 00:01:08 -07001972 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001973
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001974 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001975}
1976#endif
1977
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001978struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001979{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001981 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001983 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001984 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001985 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986
Thomas Grafc71099a2006-08-04 23:20:06 -07001987 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001988 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001989 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001990 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1992 break;
1993 }
1994 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001995 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001996 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 return rt;
1998}
1999
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002000struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08002001 struct net_device *dev,
2002 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003{
Thomas Graf86872cb2006-08-22 00:01:08 -07002004 struct fib6_config cfg = {
2005 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08002006 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07002007 .fc_ifindex = dev->ifindex,
2008 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
2009 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08002010 .fc_nlinfo.pid = 0,
2011 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002012 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07002013 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002015 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
Thomas Graf86872cb2006-08-22 00:01:08 -07002017 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 return rt6_get_dflt_router(gwaddr, dev);
2020}
2021
Daniel Lezcano7b4da532008-03-04 13:47:14 -08002022void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023{
2024 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07002025 struct fib6_table *table;
2026
2027 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08002028 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05002029 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07002030 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031
2032restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07002033 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07002034 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002036 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07002037 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07002038 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 goto restart;
2040 }
2041 }
Thomas Grafc71099a2006-08-04 23:20:06 -07002042 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043}
2044
Daniel Lezcano55786892008-03-04 13:47:47 -08002045static void rtmsg_to_fib6_config(struct net *net,
2046 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07002047 struct fib6_config *cfg)
2048{
2049 memset(cfg, 0, sizeof(*cfg));
2050
2051 cfg->fc_table = RT6_TABLE_MAIN;
2052 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
2053 cfg->fc_metric = rtmsg->rtmsg_metric;
2054 cfg->fc_expires = rtmsg->rtmsg_info;
2055 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
2056 cfg->fc_src_len = rtmsg->rtmsg_src_len;
2057 cfg->fc_flags = rtmsg->rtmsg_flags;
2058
Daniel Lezcano55786892008-03-04 13:47:47 -08002059 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08002060
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002061 cfg->fc_dst = rtmsg->rtmsg_dst;
2062 cfg->fc_src = rtmsg->rtmsg_src;
2063 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07002064}
2065
Daniel Lezcano55786892008-03-04 13:47:47 -08002066int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067{
Thomas Graf86872cb2006-08-22 00:01:08 -07002068 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 struct in6_rtmsg rtmsg;
2070 int err;
2071
2072 switch(cmd) {
2073 case SIOCADDRT: /* Add a route */
2074 case SIOCDELRT: /* Delete a route */
2075 if (!capable(CAP_NET_ADMIN))
2076 return -EPERM;
2077 err = copy_from_user(&rtmsg, arg,
2078 sizeof(struct in6_rtmsg));
2079 if (err)
2080 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07002081
Daniel Lezcano55786892008-03-04 13:47:47 -08002082 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07002083
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 rtnl_lock();
2085 switch (cmd) {
2086 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002087 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 break;
2089 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002090 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 break;
2092 default:
2093 err = -EINVAL;
2094 }
2095 rtnl_unlock();
2096
2097 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002098 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099
2100 return -EINVAL;
2101}
2102
2103/*
2104 * Drop the packet on the floor
2105 */
2106
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002107static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002109 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002110 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002111 switch (ipstats_mib_noroutes) {
2112 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002113 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002114 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002115 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2116 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002117 break;
2118 }
2119 /* FALLTHROUGH */
2120 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002121 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2122 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002123 break;
2124 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002125 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 kfree_skb(skb);
2127 return 0;
2128}
2129
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002130static int ip6_pkt_discard(struct sk_buff *skb)
2131{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002132 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002133}
2134
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002135static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136{
Eric Dumazetadf30902009-06-02 05:19:30 +00002137 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002138 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139}
2140
David S. Miller6723ab52006-10-18 21:20:57 -07002141#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2142
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002143static int ip6_pkt_prohibit(struct sk_buff *skb)
2144{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002145 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002146}
2147
2148static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2149{
Eric Dumazetadf30902009-06-02 05:19:30 +00002150 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002151 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002152}
2153
David S. Miller6723ab52006-10-18 21:20:57 -07002154#endif
2155
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156/*
2157 * Allocate a dst for local (unicast / anycast) address.
2158 */
2159
2160struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2161 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002162 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002164 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002165 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002166 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167
David S. Miller38308472011-12-03 18:02:47 -05002168 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002169 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002171 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 in6_dev_hold(idev);
2174
David S. Miller11d53b42011-06-24 15:23:34 -07002175 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002176 rt->dst.input = ip6_input;
2177 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002179 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
2181 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002182 if (anycast)
2183 rt->rt6i_flags |= RTF_ANYCAST;
2184 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002186 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002187 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002188 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002189 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 }
2191
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002192 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002194 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195
Changli Gaod8d1f302010-06-10 23:31:35 -07002196 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197
2198 return rt;
2199}
2200
Daniel Walterc3968a82011-04-13 21:10:57 +00002201int ip6_route_get_saddr(struct net *net,
2202 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002203 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002204 unsigned int prefs,
2205 struct in6_addr *saddr)
2206{
2207 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2208 int err = 0;
2209 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002210 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002211 else
2212 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2213 daddr, prefs, saddr);
2214 return err;
2215}
2216
2217/* remove deleted ip from prefsrc entries */
2218struct arg_dev_net_ip {
2219 struct net_device *dev;
2220 struct net *net;
2221 struct in6_addr *addr;
2222};
2223
2224static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2225{
2226 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2227 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2228 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2229
David S. Millerd1918542011-12-28 20:19:20 -05002230 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002231 rt != net->ipv6.ip6_null_entry &&
2232 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2233 /* remove prefsrc entry */
2234 rt->rt6i_prefsrc.plen = 0;
2235 }
2236 return 0;
2237}
2238
2239void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2240{
2241 struct net *net = dev_net(ifp->idev->dev);
2242 struct arg_dev_net_ip adni = {
2243 .dev = ifp->idev->dev,
2244 .net = net,
2245 .addr = &ifp->addr,
2246 };
2247 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2248}
2249
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002250struct arg_dev_net {
2251 struct net_device *dev;
2252 struct net *net;
2253};
2254
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255static int fib6_ifdown(struct rt6_info *rt, void *arg)
2256{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002257 const struct arg_dev_net *adn = arg;
2258 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002259
David S. Millerd1918542011-12-28 20:19:20 -05002260 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002261 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002263
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 return 0;
2265}
2266
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002267void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002269 struct arg_dev_net adn = {
2270 .dev = dev,
2271 .net = net,
2272 };
2273
2274 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002275 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276}
2277
Eric Dumazet95c96172012-04-15 05:58:06 +00002278struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002280 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281};
2282
2283static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2284{
2285 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2286 struct inet6_dev *idev;
2287
2288 /* In IPv6 pmtu discovery is not optional,
2289 so that RTAX_MTU lock cannot disable it.
2290 We still use this lock to block changes
2291 caused by addrconf/ndisc.
2292 */
2293
2294 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002295 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 return 0;
2297
2298 /* For administrative MTU increase, there is no way to discover
2299 IPv6 PMTU increase, so PMTU increase should be updated here.
2300 Since RFC 1981 doesn't include administrative MTU increase
2301 update PMTU increase is a MUST. (i.e. jumbo frame)
2302 */
2303 /*
2304 If new MTU is less than route PMTU, this new MTU will be the
2305 lowest MTU in the path, update the route PMTU to reflect PMTU
2306 decreases; if new MTU is greater than route PMTU, and the
2307 old MTU is the lowest MTU in the path, update the route PMTU
2308 to reflect the increase. In this case if the other nodes' MTU
2309 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2310 PMTU discouvery.
2311 */
David S. Millerd1918542011-12-28 20:19:20 -05002312 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002313 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2314 (dst_mtu(&rt->dst) >= arg->mtu ||
2315 (dst_mtu(&rt->dst) < arg->mtu &&
2316 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002317 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002318 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319 return 0;
2320}
2321
Eric Dumazet95c96172012-04-15 05:58:06 +00002322void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323{
Thomas Grafc71099a2006-08-04 23:20:06 -07002324 struct rt6_mtu_change_arg arg = {
2325 .dev = dev,
2326 .mtu = mtu,
2327 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002329 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330}
2331
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002332static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002333 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002334 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002335 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002336 [RTA_PRIORITY] = { .type = NLA_U32 },
2337 [RTA_METRICS] = { .type = NLA_NESTED },
2338};
2339
2340static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2341 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342{
Thomas Graf86872cb2006-08-22 00:01:08 -07002343 struct rtmsg *rtm;
2344 struct nlattr *tb[RTA_MAX+1];
2345 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
Thomas Graf86872cb2006-08-22 00:01:08 -07002347 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2348 if (err < 0)
2349 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
Thomas Graf86872cb2006-08-22 00:01:08 -07002351 err = -EINVAL;
2352 rtm = nlmsg_data(nlh);
2353 memset(cfg, 0, sizeof(*cfg));
2354
2355 cfg->fc_table = rtm->rtm_table;
2356 cfg->fc_dst_len = rtm->rtm_dst_len;
2357 cfg->fc_src_len = rtm->rtm_src_len;
2358 cfg->fc_flags = RTF_UP;
2359 cfg->fc_protocol = rtm->rtm_protocol;
2360
2361 if (rtm->rtm_type == RTN_UNREACHABLE)
2362 cfg->fc_flags |= RTF_REJECT;
2363
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002364 if (rtm->rtm_type == RTN_LOCAL)
2365 cfg->fc_flags |= RTF_LOCAL;
2366
Thomas Graf86872cb2006-08-22 00:01:08 -07002367 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2368 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002369 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002370
2371 if (tb[RTA_GATEWAY]) {
2372 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2373 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002375
2376 if (tb[RTA_DST]) {
2377 int plen = (rtm->rtm_dst_len + 7) >> 3;
2378
2379 if (nla_len(tb[RTA_DST]) < plen)
2380 goto errout;
2381
2382 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002384
2385 if (tb[RTA_SRC]) {
2386 int plen = (rtm->rtm_src_len + 7) >> 3;
2387
2388 if (nla_len(tb[RTA_SRC]) < plen)
2389 goto errout;
2390
2391 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002393
Daniel Walterc3968a82011-04-13 21:10:57 +00002394 if (tb[RTA_PREFSRC])
2395 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2396
Thomas Graf86872cb2006-08-22 00:01:08 -07002397 if (tb[RTA_OIF])
2398 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2399
2400 if (tb[RTA_PRIORITY])
2401 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2402
2403 if (tb[RTA_METRICS]) {
2404 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2405 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002407
2408 if (tb[RTA_TABLE])
2409 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2410
2411 err = 0;
2412errout:
2413 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414}
2415
Thomas Grafc127ea22007-03-22 11:58:32 -07002416static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417{
Thomas Graf86872cb2006-08-22 00:01:08 -07002418 struct fib6_config cfg;
2419 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420
Thomas Graf86872cb2006-08-22 00:01:08 -07002421 err = rtm_to_fib6_config(skb, nlh, &cfg);
2422 if (err < 0)
2423 return err;
2424
2425 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426}
2427
Thomas Grafc127ea22007-03-22 11:58:32 -07002428static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429{
Thomas Graf86872cb2006-08-22 00:01:08 -07002430 struct fib6_config cfg;
2431 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432
Thomas Graf86872cb2006-08-22 00:01:08 -07002433 err = rtm_to_fib6_config(skb, nlh, &cfg);
2434 if (err < 0)
2435 return err;
2436
2437 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438}
2439
Thomas Graf339bf982006-11-10 14:10:15 -08002440static inline size_t rt6_nlmsg_size(void)
2441{
2442 return NLMSG_ALIGN(sizeof(struct rtmsg))
2443 + nla_total_size(16) /* RTA_SRC */
2444 + nla_total_size(16) /* RTA_DST */
2445 + nla_total_size(16) /* RTA_GATEWAY */
2446 + nla_total_size(16) /* RTA_PREFSRC */
2447 + nla_total_size(4) /* RTA_TABLE */
2448 + nla_total_size(4) /* RTA_IIF */
2449 + nla_total_size(4) /* RTA_OIF */
2450 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002451 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002452 + nla_total_size(sizeof(struct rta_cacheinfo));
2453}
2454
Brian Haley191cd582008-08-14 15:33:21 -07002455static int rt6_fill_node(struct net *net,
2456 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002457 struct in6_addr *dst, struct in6_addr *src,
2458 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002459 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460{
2461 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002462 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002463 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002464 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002465 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466
2467 if (prefix) { /* user wants prefix routes only */
2468 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2469 /* success since this is not a prefix route */
2470 return 1;
2471 }
2472 }
2473
Thomas Graf2d7202b2006-08-22 00:01:27 -07002474 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002475 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002476 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002477
2478 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479 rtm->rtm_family = AF_INET6;
2480 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2481 rtm->rtm_src_len = rt->rt6i_src.plen;
2482 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002483 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002484 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002485 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002486 table = RT6_TABLE_UNSPEC;
2487 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002488 if (nla_put_u32(skb, RTA_TABLE, table))
2489 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002490 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002492 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002493 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002494 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 rtm->rtm_type = RTN_LOCAL;
2496 else
2497 rtm->rtm_type = RTN_UNICAST;
2498 rtm->rtm_flags = 0;
2499 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2500 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002501 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502 rtm->rtm_protocol = RTPROT_REDIRECT;
2503 else if (rt->rt6i_flags & RTF_ADDRCONF)
2504 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002505 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506 rtm->rtm_protocol = RTPROT_RA;
2507
David S. Miller38308472011-12-03 18:02:47 -05002508 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509 rtm->rtm_flags |= RTM_F_CLONED;
2510
2511 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002512 if (nla_put(skb, RTA_DST, 16, dst))
2513 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002514 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002516 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2517 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518#ifdef CONFIG_IPV6_SUBTREES
2519 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002520 if (nla_put(skb, RTA_SRC, 16, src))
2521 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002522 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002523 } else if (rtm->rtm_src_len &&
2524 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2525 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002527 if (iif) {
2528#ifdef CONFIG_IPV6_MROUTE
2529 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002530 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002531 if (err <= 0) {
2532 if (!nowait) {
2533 if (err == 0)
2534 return 0;
2535 goto nla_put_failure;
2536 } else {
2537 if (err == -EMSGSIZE)
2538 goto nla_put_failure;
2539 }
2540 }
2541 } else
2542#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002543 if (nla_put_u32(skb, RTA_IIF, iif))
2544 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002545 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002547 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2548 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2549 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002551
Daniel Walterc3968a82011-04-13 21:10:57 +00002552 if (rt->rt6i_prefsrc.plen) {
2553 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002554 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002555 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2556 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002557 }
2558
David S. Millerdefb3512010-12-08 21:16:57 -08002559 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002560 goto nla_put_failure;
2561
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002562 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002563 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002564 if (n) {
2565 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2566 rcu_read_unlock();
2567 goto nla_put_failure;
2568 }
2569 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002570 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002571
David S. Millerc78679e2012-04-01 20:27:33 -04002572 if (rt->dst.dev &&
2573 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2574 goto nla_put_failure;
2575 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2576 goto nla_put_failure;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002577 if (!(rt->rt6i_flags & RTF_EXPIRES))
2578 expires = 0;
David S. Millerd1918542011-12-28 20:19:20 -05002579 else if (rt->dst.expires - jiffies < INT_MAX)
2580 expires = rt->dst.expires - jiffies;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002581 else
2582 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002583
David S. Miller87a50692012-07-10 05:06:14 -07002584 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002585 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586
Thomas Graf2d7202b2006-08-22 00:01:27 -07002587 return nlmsg_end(skb, nlh);
2588
2589nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002590 nlmsg_cancel(skb, nlh);
2591 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592}
2593
Patrick McHardy1b43af52006-08-10 23:11:17 -07002594int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595{
2596 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2597 int prefix;
2598
Thomas Graf2d7202b2006-08-22 00:01:27 -07002599 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2600 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2602 } else
2603 prefix = 0;
2604
Brian Haley191cd582008-08-14 15:33:21 -07002605 return rt6_fill_node(arg->net,
2606 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002608 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609}
2610
Thomas Grafc127ea22007-03-22 11:58:32 -07002611static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002613 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002614 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002616 struct sk_buff *skb;
2617 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002618 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002619 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002620
2621 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2622 if (err < 0)
2623 goto errout;
2624
2625 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002626 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002627
2628 if (tb[RTA_SRC]) {
2629 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2630 goto errout;
2631
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002632 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002633 }
2634
2635 if (tb[RTA_DST]) {
2636 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2637 goto errout;
2638
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002639 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002640 }
2641
2642 if (tb[RTA_IIF])
2643 iif = nla_get_u32(tb[RTA_IIF]);
2644
2645 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002646 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002647
2648 if (iif) {
2649 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002650 int flags = 0;
2651
Daniel Lezcano55786892008-03-04 13:47:47 -08002652 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002653 if (!dev) {
2654 err = -ENODEV;
2655 goto errout;
2656 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002657
2658 fl6.flowi6_iif = iif;
2659
2660 if (!ipv6_addr_any(&fl6.saddr))
2661 flags |= RT6_LOOKUP_F_HAS_SADDR;
2662
2663 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2664 flags);
2665 } else {
2666 fl6.flowi6_oif = oif;
2667
2668 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002669 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670
2671 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002672 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002673 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002674 err = -ENOBUFS;
2675 goto errout;
2676 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677
2678 /* Reserve room for dummy headers, this skb can pass
2679 through good chunk of routing engine.
2680 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002681 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2683
Changli Gaod8d1f302010-06-10 23:31:35 -07002684 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685
David S. Miller4c9483b2011-03-12 16:22:43 -05002686 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002688 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002690 kfree_skb(skb);
2691 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 }
2693
Daniel Lezcano55786892008-03-04 13:47:47 -08002694 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002695errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697}
2698
Thomas Graf86872cb2006-08-22 00:01:08 -07002699void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700{
2701 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002702 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002703 u32 seq;
2704 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002706 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002707 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002708
Thomas Graf339bf982006-11-10 14:10:15 -08002709 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002710 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002711 goto errout;
2712
Brian Haley191cd582008-08-14 15:33:21 -07002713 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002714 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002715 if (err < 0) {
2716 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2717 WARN_ON(err == -EMSGSIZE);
2718 kfree_skb(skb);
2719 goto errout;
2720 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002721 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2722 info->nlh, gfp_any());
2723 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002724errout:
2725 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002726 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727}
2728
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002729static int ip6_route_dev_notify(struct notifier_block *this,
2730 unsigned long event, void *data)
2731{
2732 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002733 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002734
2735 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002736 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002737 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2738#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002739 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002740 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002741 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002742 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2743#endif
2744 }
2745
2746 return NOTIFY_OK;
2747}
2748
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749/*
2750 * /proc
2751 */
2752
2753#ifdef CONFIG_PROC_FS
2754
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755struct rt6_proc_arg
2756{
2757 char *buffer;
2758 int offset;
2759 int length;
2760 int skip;
2761 int len;
2762};
2763
2764static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2765{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002766 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002767 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002769 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770
2771#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002772 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002774 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002776 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002777 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002778 if (n) {
2779 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002781 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002783 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002784 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002785 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2786 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002787 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 return 0;
2789}
2790
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002791static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002793 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002794 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002795 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796}
2797
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002798static int ipv6_route_open(struct inode *inode, struct file *file)
2799{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002800 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002801}
2802
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002803static const struct file_operations ipv6_route_proc_fops = {
2804 .owner = THIS_MODULE,
2805 .open = ipv6_route_open,
2806 .read = seq_read,
2807 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002808 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002809};
2810
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2812{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002813 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002815 net->ipv6.rt6_stats->fib_nodes,
2816 net->ipv6.rt6_stats->fib_route_nodes,
2817 net->ipv6.rt6_stats->fib_rt_alloc,
2818 net->ipv6.rt6_stats->fib_rt_entries,
2819 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002820 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002821 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822
2823 return 0;
2824}
2825
2826static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2827{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002828 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002829}
2830
Arjan van de Ven9a321442007-02-12 00:55:35 -08002831static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 .owner = THIS_MODULE,
2833 .open = rt6_stats_seq_open,
2834 .read = seq_read,
2835 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002836 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837};
2838#endif /* CONFIG_PROC_FS */
2839
2840#ifdef CONFIG_SYSCTL
2841
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002843int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844 void __user *buffer, size_t *lenp, loff_t *ppos)
2845{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002846 struct net *net;
2847 int delay;
2848 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002850
2851 net = (struct net *)ctl->extra1;
2852 delay = net->ipv6.sysctl.flush_delay;
2853 proc_dointvec(ctl, write, buffer, lenp, ppos);
2854 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2855 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856}
2857
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002858ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002859 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002861 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002863 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002864 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 },
2866 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002868 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869 .maxlen = sizeof(int),
2870 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002871 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 },
2873 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002875 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 .maxlen = sizeof(int),
2877 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002878 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002879 },
2880 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002882 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883 .maxlen = sizeof(int),
2884 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002885 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886 },
2887 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002889 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 .maxlen = sizeof(int),
2891 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002892 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 },
2894 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002896 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 .maxlen = sizeof(int),
2898 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002899 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 },
2901 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002903 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002904 .maxlen = sizeof(int),
2905 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002906 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 },
2908 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002909 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002910 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911 .maxlen = sizeof(int),
2912 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002913 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 },
2915 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002917 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918 .maxlen = sizeof(int),
2919 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002920 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 },
2922 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002924 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002925 .maxlen = sizeof(int),
2926 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002927 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002929 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930};
2931
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002932struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002933{
2934 struct ctl_table *table;
2935
2936 table = kmemdup(ipv6_route_table_template,
2937 sizeof(ipv6_route_table_template),
2938 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002939
2940 if (table) {
2941 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002942 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002943 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002944 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2945 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2946 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2947 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2948 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2949 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2950 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002951 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002952 }
2953
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002954 return table;
2955}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956#endif
2957
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002958static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002959{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002960 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002961
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002962 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2963 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002964
Eric Dumazetfc66f952010-10-08 06:37:34 +00002965 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2966 goto out_ip6_dst_ops;
2967
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002968 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2969 sizeof(*net->ipv6.ip6_null_entry),
2970 GFP_KERNEL);
2971 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002972 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002973 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002974 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002975 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002976 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2977 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002978
2979#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2980 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2981 sizeof(*net->ipv6.ip6_prohibit_entry),
2982 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002983 if (!net->ipv6.ip6_prohibit_entry)
2984 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002985 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002986 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002987 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002988 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2989 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002990
2991 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2992 sizeof(*net->ipv6.ip6_blk_hole_entry),
2993 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002994 if (!net->ipv6.ip6_blk_hole_entry)
2995 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002996 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002997 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002998 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002999 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
3000 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003001#endif
3002
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07003003 net->ipv6.sysctl.flush_delay = 0;
3004 net->ipv6.sysctl.ip6_rt_max_size = 4096;
3005 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3006 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3007 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3008 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3009 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3010 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3011
Benjamin Thery6891a342008-03-04 13:49:47 -08003012 net->ipv6.ip6_rt_gc_expire = 30*HZ;
3013
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003014 ret = 0;
3015out:
3016 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003017
Peter Zijlstra68fffc62008-10-07 14:12:10 -07003018#ifdef CONFIG_IPV6_MULTIPLE_TABLES
3019out_ip6_prohibit_entry:
3020 kfree(net->ipv6.ip6_prohibit_entry);
3021out_ip6_null_entry:
3022 kfree(net->ipv6.ip6_null_entry);
3023#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00003024out_ip6_dst_entries:
3025 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003026out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003027 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003028}
3029
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00003030static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003031{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003032 kfree(net->ipv6.ip6_null_entry);
3033#ifdef CONFIG_IPV6_MULTIPLE_TABLES
3034 kfree(net->ipv6.ip6_prohibit_entry);
3035 kfree(net->ipv6.ip6_blk_hole_entry);
3036#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003037 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003038}
3039
Thomas Grafd1896342012-06-18 12:08:33 +00003040static int __net_init ip6_route_net_init_late(struct net *net)
3041{
3042#ifdef CONFIG_PROC_FS
3043 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
3044 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
3045#endif
3046 return 0;
3047}
3048
3049static void __net_exit ip6_route_net_exit_late(struct net *net)
3050{
3051#ifdef CONFIG_PROC_FS
3052 proc_net_remove(net, "ipv6_route");
3053 proc_net_remove(net, "rt6_stats");
3054#endif
3055}
3056
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003057static struct pernet_operations ip6_route_net_ops = {
3058 .init = ip6_route_net_init,
3059 .exit = ip6_route_net_exit,
3060};
3061
David S. Millerc3426b42012-06-09 16:27:05 -07003062static int __net_init ipv6_inetpeer_init(struct net *net)
3063{
3064 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3065
3066 if (!bp)
3067 return -ENOMEM;
3068 inet_peer_base_init(bp);
3069 net->ipv6.peers = bp;
3070 return 0;
3071}
3072
3073static void __net_exit ipv6_inetpeer_exit(struct net *net)
3074{
3075 struct inet_peer_base *bp = net->ipv6.peers;
3076
3077 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07003078 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07003079 kfree(bp);
3080}
3081
David S. Miller2b823f72012-06-09 19:00:16 -07003082static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003083 .init = ipv6_inetpeer_init,
3084 .exit = ipv6_inetpeer_exit,
3085};
3086
Thomas Grafd1896342012-06-18 12:08:33 +00003087static struct pernet_operations ip6_route_net_late_ops = {
3088 .init = ip6_route_net_init_late,
3089 .exit = ip6_route_net_exit_late,
3090};
3091
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003092static struct notifier_block ip6_route_dev_notifier = {
3093 .notifier_call = ip6_route_dev_notify,
3094 .priority = 0,
3095};
3096
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003097int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003099 int ret;
3100
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003101 ret = -ENOMEM;
3102 ip6_dst_ops_template.kmem_cachep =
3103 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3104 SLAB_HWCACHE_ALIGN, NULL);
3105 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003106 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003107
Eric Dumazetfc66f952010-10-08 06:37:34 +00003108 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003109 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003110 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003111
David S. Millerc3426b42012-06-09 16:27:05 -07003112 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3113 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003114 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003115
David S. Miller7e52b332012-06-15 15:51:55 -07003116 ret = register_pernet_subsys(&ip6_route_net_ops);
3117 if (ret)
3118 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003119
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003120 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3121
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003122 /* Registering of the loopback is done before this portion of code,
3123 * the loopback reference in rt6_info will not be taken, do it
3124 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003125 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003126 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3127 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003128 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003129 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003130 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003131 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3132 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003133 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003134 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003135 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003136
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003137 ret = xfrm6_init();
3138 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003139 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003140
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003141 ret = fib6_rules_init();
3142 if (ret)
3143 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003144
Thomas Grafd1896342012-06-18 12:08:33 +00003145 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3146 if (ret)
3147 goto fib6_rules_init;
3148
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003149 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003150 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3151 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3152 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003153 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003154
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003155 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003156 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003157 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003158
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003159out:
3160 return ret;
3161
Thomas Grafd1896342012-06-18 12:08:33 +00003162out_register_late_subsys:
3163 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003164fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003165 fib6_rules_cleanup();
3166xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003167 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003168out_fib6_init:
3169 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003170out_register_subsys:
3171 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003172out_register_inetpeer:
3173 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003174out_dst_entries:
3175 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003176out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003177 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003178 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179}
3180
3181void ip6_route_cleanup(void)
3182{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003183 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003184 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003185 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003188 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003189 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003190 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003191 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003192}