blob: d82eb1f24b25cbf5013622a13e13ea33c59a327d [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>
Nicolas Dichtel51ebd312012-10-22 03:42:09 +000060#include <net/nexthop.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
62#include <asm/uaccess.h>
63
64#ifdef CONFIG_SYSCTL
65#include <linux/sysctl.h>
66#endif
67
Gao feng1716a962012-04-06 00:13:10 +000068static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +000069 const struct in6_addr *dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080071static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000072static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073static struct dst_entry *ip6_negative_advice(struct dst_entry *);
74static void ip6_dst_destroy(struct dst_entry *);
75static void ip6_dst_ifdown(struct dst_entry *,
76 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080077static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79static int ip6_pkt_discard(struct sk_buff *skb);
80static int ip6_pkt_discard_out(struct sk_buff *skb);
81static void ip6_link_failure(struct sk_buff *skb);
David S. Miller6700c272012-07-17 03:29:28 -070082static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
83 struct sk_buff *skb, u32 mtu);
84static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
85 struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080087#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080088static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000089 const struct in6_addr *prefix, int prefixlen,
90 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +000091 unsigned int pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080092static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000093 const struct in6_addr *prefix, int prefixlen,
94 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080095#endif
96
David S. Miller06582542011-01-27 14:58:42 -080097static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
98{
99 struct rt6_info *rt = (struct rt6_info *) dst;
100 struct inet_peer *peer;
101 u32 *p = NULL;
102
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000103 if (!(rt->dst.flags & DST_HOST))
104 return NULL;
105
David S. Millerfbfe95a2012-06-08 23:24:18 -0700106 peer = rt6_get_peer_create(rt);
David S. Miller06582542011-01-27 14:58:42 -0800107 if (peer) {
108 u32 *old_p = __DST_METRICS_PTR(old);
109 unsigned long prev, new;
110
111 p = peer->metrics;
112 if (inet_metrics_new(peer))
113 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
114
115 new = (unsigned long) p;
116 prev = cmpxchg(&dst->_metrics, old, new);
117
118 if (prev != old) {
119 p = __DST_METRICS_PTR(prev);
120 if (prev & DST_METRICS_READ_ONLY)
121 p = NULL;
122 }
123 }
124 return p;
125}
126
David S. Millerf894cbf2012-07-02 21:52:24 -0700127static inline const void *choose_neigh_daddr(struct rt6_info *rt,
128 struct sk_buff *skb,
129 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500130{
131 struct in6_addr *p = &rt->rt6i_gateway;
132
David S. Millera7563f32012-01-26 16:29:16 -0500133 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500134 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700135 else if (skb)
136 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500137 return daddr;
138}
139
David S. Millerf894cbf2012-07-02 21:52:24 -0700140static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
141 struct sk_buff *skb,
142 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700143{
David S. Miller39232972012-01-26 15:22:32 -0500144 struct rt6_info *rt = (struct rt6_info *) dst;
145 struct neighbour *n;
146
David S. Millerf894cbf2012-07-02 21:52:24 -0700147 daddr = choose_neigh_daddr(rt, skb, daddr);
YOSHIFUJI Hideaki / 吉藤英明8e022ee2013-01-17 12:53:09 +0000148 n = __ipv6_neigh_lookup(dst->dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500149 if (n)
150 return n;
151 return neigh_create(&nd_tbl, daddr, dst->dev);
152}
153
David S. Miller8ade06c2011-12-29 18:51:57 -0500154static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
David S. Millerf83c7792011-12-28 15:41:23 -0500155{
YOSHIFUJI Hideaki / 吉藤英明8e022ee2013-01-17 12:53:09 +0000156 struct neighbour *n = __ipv6_neigh_lookup(dev, &rt->rt6i_gateway);
David S. Miller8ade06c2011-12-29 18:51:57 -0500157 if (!n) {
158 n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
159 if (IS_ERR(n))
160 return PTR_ERR(n);
161 }
David S. Miller97cac082012-07-02 22:43:47 -0700162 rt->n = n;
David S. Millerf83c7792011-12-28 15:41:23 -0500163
164 return 0;
David S. Millerd3aaeb32011-07-18 00:40:17 -0700165}
166
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800167static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800169 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 .gc = ip6_dst_gc,
171 .gc_thresh = 1024,
172 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800173 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000174 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800175 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 .destroy = ip6_dst_destroy,
177 .ifdown = ip6_dst_ifdown,
178 .negative_advice = ip6_negative_advice,
179 .link_failure = ip6_link_failure,
180 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700181 .redirect = rt6_do_redirect,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700182 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700183 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184};
185
Steffen Klassertebb762f2011-11-23 02:12:51 +0000186static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800187{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000188 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
189
190 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800191}
192
David S. Miller6700c272012-07-17 03:29:28 -0700193static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
194 struct sk_buff *skb, u32 mtu)
David S. Miller14e50e52007-05-24 18:17:54 -0700195{
196}
197
David S. Miller6700c272012-07-17 03:29:28 -0700198static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
199 struct sk_buff *skb)
David S. Millerb587ee32012-07-12 00:39:24 -0700200{
201}
202
Held Bernhard0972ddb2011-04-24 22:07:32 +0000203static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
204 unsigned long old)
205{
206 return NULL;
207}
208
David S. Miller14e50e52007-05-24 18:17:54 -0700209static struct dst_ops ip6_dst_blackhole_ops = {
210 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800211 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700212 .destroy = ip6_dst_destroy,
213 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000214 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800215 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700216 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700217 .redirect = ip6_rt_blackhole_redirect,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000218 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700219 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700220};
221
David S. Miller62fa8a82011-01-26 20:51:05 -0800222static const u32 ip6_template_metrics[RTAX_MAX] = {
Li RongQing14edd872012-10-24 14:01:18 +0800223 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800224};
225
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000226static const struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700227 .dst = {
228 .__refcnt = ATOMIC_INIT(1),
229 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000230 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700231 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700232 .input = ip6_pkt_discard,
233 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 },
235 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700236 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 .rt6i_metric = ~(u32) 0,
238 .rt6i_ref = ATOMIC_INIT(1),
239};
240
Thomas Graf101367c2006-08-04 03:39:02 -0700241#ifdef CONFIG_IPV6_MULTIPLE_TABLES
242
David S. Miller6723ab52006-10-18 21:20:57 -0700243static int ip6_pkt_prohibit(struct sk_buff *skb);
244static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700245
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000246static const struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700247 .dst = {
248 .__refcnt = ATOMIC_INIT(1),
249 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000250 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700251 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700252 .input = ip6_pkt_prohibit,
253 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700254 },
255 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700256 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700257 .rt6i_metric = ~(u32) 0,
258 .rt6i_ref = ATOMIC_INIT(1),
259};
260
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000261static const struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700262 .dst = {
263 .__refcnt = ATOMIC_INIT(1),
264 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000265 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700266 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700267 .input = dst_discard,
268 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700269 },
270 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700271 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700272 .rt6i_metric = ~(u32) 0,
273 .rt6i_ref = ATOMIC_INIT(1),
274};
275
276#endif
277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700279static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700280 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700281 int flags,
282 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
David S. Miller97bab732012-06-09 22:36:36 -0700284 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +0000285 0, DST_OBSOLETE_FORCE_CHK, flags);
David S. Millercf911662011-04-28 14:31:47 -0700286
David S. Miller97bab732012-06-09 22:36:36 -0700287 if (rt) {
Steffen Klassert81048912012-07-05 23:37:09 +0000288 struct dst_entry *dst = &rt->dst;
289
290 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
David S. Miller8b96d222012-06-11 02:01:56 -0700291 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +0000292 rt->rt6i_genid = rt_genid(net);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000293 INIT_LIST_HEAD(&rt->rt6i_siblings);
294 rt->rt6i_nsiblings = 0;
David S. Miller97bab732012-06-09 22:36:36 -0700295 }
David S. Millercf911662011-04-28 14:31:47 -0700296 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297}
298
299static void ip6_dst_destroy(struct dst_entry *dst)
300{
301 struct rt6_info *rt = (struct rt6_info *)dst;
302 struct inet6_dev *idev = rt->rt6i_idev;
303
David S. Miller97cac082012-07-02 22:43:47 -0700304 if (rt->n)
305 neigh_release(rt->n);
306
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000307 if (!(rt->dst.flags & DST_HOST))
308 dst_destroy_metrics_generic(dst);
309
David S. Miller38308472011-12-03 18:02:47 -0500310 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 rt->rt6i_idev = NULL;
312 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900313 }
Gao feng1716a962012-04-06 00:13:10 +0000314
315 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
316 dst_release(dst->from);
317
David S. Miller97bab732012-06-09 22:36:36 -0700318 if (rt6_has_peer(rt)) {
319 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800320 inet_putpeer(peer);
321 }
322}
323
324void rt6_bind_peer(struct rt6_info *rt, int create)
325{
David S. Miller97bab732012-06-09 22:36:36 -0700326 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800327 struct inet_peer *peer;
328
David S. Miller97bab732012-06-09 22:36:36 -0700329 base = inetpeer_base_ptr(rt->_rt6i_peer);
330 if (!base)
331 return;
332
333 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700334 if (peer) {
335 if (!rt6_set_peer(rt, peer))
336 inet_putpeer(peer);
David S. Miller7b34ca22012-06-11 04:13:57 -0700337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
340static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
341 int how)
342{
343 struct rt6_info *rt = (struct rt6_info *)dst;
344 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800345 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900346 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
David S. Miller97cac082012-07-02 22:43:47 -0700348 if (dev != loopback_dev) {
349 if (idev && idev->dev == dev) {
350 struct inet6_dev *loopback_idev =
351 in6_dev_get(loopback_dev);
352 if (loopback_idev) {
353 rt->rt6i_idev = loopback_idev;
354 in6_dev_put(idev);
355 }
356 }
357 if (rt->n && rt->n->dev == dev) {
358 rt->n->dev = loopback_dev;
359 dev_hold(loopback_dev);
360 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 }
362 }
363}
364
Eric Dumazeta50feda2012-05-18 18:57:34 +0000365static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366{
Gao feng1716a962012-04-06 00:13:10 +0000367 if (rt->rt6i_flags & RTF_EXPIRES) {
368 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000369 return true;
Gao feng1716a962012-04-06 00:13:10 +0000370 } else if (rt->dst.from) {
Li RongQing3fd91fb2012-09-13 19:54:57 +0000371 return rt6_check_expired((struct rt6_info *) rt->dst.from);
Gao feng1716a962012-04-06 00:13:10 +0000372 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000373 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374}
375
Eric Dumazeta50feda2012-05-18 18:57:34 +0000376static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700377{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000378 return ipv6_addr_type(daddr) &
379 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700380}
381
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000382/* Multipath route selection:
383 * Hash based function using packet header and flowlabel.
384 * Adapted from fib_info_hashfn()
385 */
386static int rt6_info_hash_nhsfn(unsigned int candidate_count,
387 const struct flowi6 *fl6)
388{
389 unsigned int val = fl6->flowi6_proto;
390
YOSHIFUJI Hideaki / 吉藤英明c08977b2013-01-13 05:02:29 +0000391 val ^= ipv6_addr_hash(&fl6->daddr);
392 val ^= ipv6_addr_hash(&fl6->saddr);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000393
394 /* Work only if this not encapsulated */
395 switch (fl6->flowi6_proto) {
396 case IPPROTO_UDP:
397 case IPPROTO_TCP:
398 case IPPROTO_SCTP:
Nicolas Dichtelb3ce5ae2012-10-22 23:35:06 +0000399 val ^= (__force u16)fl6->fl6_sport;
400 val ^= (__force u16)fl6->fl6_dport;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000401 break;
402
403 case IPPROTO_ICMPV6:
Nicolas Dichtelb3ce5ae2012-10-22 23:35:06 +0000404 val ^= (__force u16)fl6->fl6_icmp_type;
405 val ^= (__force u16)fl6->fl6_icmp_code;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000406 break;
407 }
408 /* RFC6438 recommands to use flowlabel */
Nicolas Dichtelb3ce5ae2012-10-22 23:35:06 +0000409 val ^= (__force u32)fl6->flowlabel;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000410
411 /* Perhaps, we need to tune, this function? */
412 val = val ^ (val >> 7) ^ (val >> 12);
413 return val % candidate_count;
414}
415
416static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
417 struct flowi6 *fl6)
418{
419 struct rt6_info *sibling, *next_sibling;
420 int route_choosen;
421
422 route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
423 /* Don't change the route, if route_choosen == 0
424 * (siblings does not include ourself)
425 */
426 if (route_choosen)
427 list_for_each_entry_safe(sibling, next_sibling,
428 &match->rt6i_siblings, rt6i_siblings) {
429 route_choosen--;
430 if (route_choosen == 0) {
431 match = sibling;
432 break;
433 }
434 }
435 return match;
436}
437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700439 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 */
441
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800442static inline struct rt6_info *rt6_device_match(struct net *net,
443 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000444 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700446 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
448 struct rt6_info *local = NULL;
449 struct rt6_info *sprt;
450
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900451 if (!oif && ipv6_addr_any(saddr))
452 goto out;
453
Changli Gaod8d1f302010-06-10 23:31:35 -0700454 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500455 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900456
457 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 if (dev->ifindex == oif)
459 return sprt;
460 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500461 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700463 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900465 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 local->rt6i_idev->dev->ifindex == oif))
467 continue;
468 }
469 local = sprt;
470 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900471 } else {
472 if (ipv6_chk_addr(net, saddr, dev,
473 flags & RT6_LOOKUP_F_IFACE))
474 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900476 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900478 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 if (local)
480 return local;
481
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700482 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800483 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900485out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 return rt;
487}
488
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800489#ifdef CONFIG_IPV6_ROUTER_PREF
490static void rt6_probe(struct rt6_info *rt)
491{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000492 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800493 /*
494 * Okay, this does not seem to be appropriate
495 * for now, however, we need to check if it
496 * is really so; aka Router Reachability Probing.
497 *
498 * Router Reachability Probe MUST be rate-limited
499 * to no more than one per minute.
500 */
David S. Miller97cac082012-07-02 22:43:47 -0700501 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki / 吉藤英明7ff74a52013-01-17 12:53:02 +0000502 if (!neigh)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000503 return;
YOSHIFUJI Hideaki / 吉藤英明7ff74a52013-01-17 12:53:02 +0000504 write_lock_bh(&neigh->lock);
505 if (neigh->nud_state & NUD_VALID) {
506 write_unlock_bh(&neigh->lock);
507 return;
508 }
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800509 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800510 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800511 struct in6_addr mcaddr;
512 struct in6_addr *target;
513
514 neigh->updated = jiffies;
YOSHIFUJI Hideaki / 吉藤英明7ff74a52013-01-17 12:53:02 +0000515 write_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800516
517 target = (struct in6_addr *)&neigh->primary_key;
518 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500519 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000520 } else {
YOSHIFUJI Hideaki / 吉藤英明7ff74a52013-01-17 12:53:02 +0000521 write_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000522 }
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800523}
524#else
525static inline void rt6_probe(struct rt6_info *rt)
526{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800527}
528#endif
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800531 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700533static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
David S. Millerd1918542011-12-28 20:19:20 -0500535 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700536 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800537 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700538 if ((dev->flags & IFF_LOOPBACK) &&
539 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
540 return 1;
541 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542}
543
Paul Marksa5a81f02012-12-03 10:26:54 +0000544static inline bool rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000546 struct neighbour *neigh;
Paul Marksa5a81f02012-12-03 10:26:54 +0000547 bool ret = false;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000548
David S. Miller97cac082012-07-02 22:43:47 -0700549 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700550 if (rt->rt6i_flags & RTF_NONEXTHOP ||
551 !(rt->rt6i_flags & RTF_GATEWAY))
Paul Marksa5a81f02012-12-03 10:26:54 +0000552 ret = true;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700553 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800554 read_lock_bh(&neigh->lock);
555 if (neigh->nud_state & NUD_VALID)
Paul Marksa5a81f02012-12-03 10:26:54 +0000556 ret = true;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800557#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000558 else if (!(neigh->nud_state & NUD_FAILED))
559 ret = true;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800560#endif
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800561 read_unlock_bh(&neigh->lock);
Paul Marksa5a81f02012-12-03 10:26:54 +0000562 }
563 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800564}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800566static int rt6_score_route(struct rt6_info *rt, int oif,
567 int strict)
568{
Paul Marksa5a81f02012-12-03 10:26:54 +0000569 int m;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900570
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700571 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700572 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800573 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800574#ifdef CONFIG_IPV6_ROUTER_PREF
575 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
576#endif
Paul Marksa5a81f02012-12-03 10:26:54 +0000577 if (!rt6_check_neigh(rt) && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800578 return -1;
579 return m;
580}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
David S. Millerf11e6652007-03-24 20:36:25 -0700582static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
583 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800584{
David S. Millerf11e6652007-03-24 20:36:25 -0700585 int m;
586
587 if (rt6_check_expired(rt))
588 goto out;
589
590 m = rt6_score_route(rt, oif, strict);
591 if (m < 0)
592 goto out;
593
594 if (m > *mpri) {
595 if (strict & RT6_LOOKUP_F_REACHABLE)
596 rt6_probe(match);
597 *mpri = m;
598 match = rt;
599 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
600 rt6_probe(rt);
601 }
602
603out:
604 return match;
605}
606
607static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
608 struct rt6_info *rr_head,
609 u32 metric, int oif, int strict)
610{
611 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800612 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
David S. Millerf11e6652007-03-24 20:36:25 -0700614 match = NULL;
615 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700616 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700617 match = find_match(rt, oif, strict, &mpri, match);
618 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700619 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700620 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800621
David S. Millerf11e6652007-03-24 20:36:25 -0700622 return match;
623}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800624
David S. Millerf11e6652007-03-24 20:36:25 -0700625static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
626{
627 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800628 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
David S. Millerf11e6652007-03-24 20:36:25 -0700630 rt0 = fn->rr_ptr;
631 if (!rt0)
632 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
David S. Millerf11e6652007-03-24 20:36:25 -0700634 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800636 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700637 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700638 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700639
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800640 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700641 if (!next || next->rt6i_metric != rt0->rt6i_metric)
642 next = fn->leaf;
643
644 if (next != rt0)
645 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 }
647
David S. Millerd1918542011-12-28 20:19:20 -0500648 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000649 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650}
651
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800652#ifdef CONFIG_IPV6_ROUTE_INFO
653int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000654 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800655{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900656 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800657 struct route_info *rinfo = (struct route_info *) opt;
658 struct in6_addr prefix_buf, *prefix;
659 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900660 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800661 struct rt6_info *rt;
662
663 if (len < sizeof(struct route_info)) {
664 return -EINVAL;
665 }
666
667 /* Sanity check for prefix_len and length */
668 if (rinfo->length > 3) {
669 return -EINVAL;
670 } else if (rinfo->prefix_len > 128) {
671 return -EINVAL;
672 } else if (rinfo->prefix_len > 64) {
673 if (rinfo->length < 2) {
674 return -EINVAL;
675 }
676 } else if (rinfo->prefix_len > 0) {
677 if (rinfo->length < 1) {
678 return -EINVAL;
679 }
680 }
681
682 pref = rinfo->route_pref;
683 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000684 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800685
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900686 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800687
688 if (rinfo->length == 3)
689 prefix = (struct in6_addr *)rinfo->prefix;
690 else {
691 /* this function is safe */
692 ipv6_addr_prefix(&prefix_buf,
693 (struct in6_addr *)rinfo->prefix,
694 rinfo->prefix_len);
695 prefix = &prefix_buf;
696 }
697
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800698 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
699 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800700
701 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700702 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800703 rt = NULL;
704 }
705
706 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800707 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800708 pref);
709 else if (rt)
710 rt->rt6i_flags = RTF_ROUTEINFO |
711 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
712
713 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000714 if (!addrconf_finite_timeout(lifetime))
715 rt6_clean_expires(rt);
716 else
717 rt6_set_expires(rt, jiffies + HZ * lifetime);
718
Amerigo Wang94e187c2012-10-29 00:13:19 +0000719 ip6_rt_put(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800720 }
721 return 0;
722}
723#endif
724
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800725#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700726do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800727 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700728 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700729 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700730 if (fn->fn_flags & RTN_TL_ROOT) \
731 goto out; \
732 pn = fn->parent; \
733 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800734 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700735 else \
736 fn = pn; \
737 if (fn->fn_flags & RTN_RTINFO) \
738 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700739 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700740 } \
David S. Miller38308472011-12-03 18:02:47 -0500741} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700742
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800743static struct rt6_info *ip6_pol_route_lookup(struct net *net,
744 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500745 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746{
747 struct fib6_node *fn;
748 struct rt6_info *rt;
749
Thomas Grafc71099a2006-08-04 23:20:06 -0700750 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500751 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700752restart:
753 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500754 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000755 if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
756 rt = rt6_multipath_select(rt, fl6);
David S. Miller4c9483b2011-03-12 16:22:43 -0500757 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700758out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700759 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700760 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700761 return rt;
762
763}
764
Florian Westphalea6e5742011-09-05 16:05:44 +0200765struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
766 int flags)
767{
768 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
769}
770EXPORT_SYMBOL_GPL(ip6_route_lookup);
771
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900772struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
773 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700774{
David S. Miller4c9483b2011-03-12 16:22:43 -0500775 struct flowi6 fl6 = {
776 .flowi6_oif = oif,
777 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700778 };
779 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700780 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700781
Thomas Grafadaa70b2006-10-13 15:01:03 -0700782 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500783 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700784 flags |= RT6_LOOKUP_F_HAS_SADDR;
785 }
786
David S. Miller4c9483b2011-03-12 16:22:43 -0500787 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700788 if (dst->error == 0)
789 return (struct rt6_info *) dst;
790
791 dst_release(dst);
792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 return NULL;
794}
795
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900796EXPORT_SYMBOL(rt6_lookup);
797
Thomas Grafc71099a2006-08-04 23:20:06 -0700798/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 It takes new route entry, the addition fails by any reason the
800 route is freed. In any case, if caller does not hold it, it may
801 be destroyed.
802 */
803
Thomas Graf86872cb2006-08-22 00:01:08 -0700804static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805{
806 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700807 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Thomas Grafc71099a2006-08-04 23:20:06 -0700809 table = rt->rt6i_table;
810 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700811 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700812 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
814 return err;
815}
816
Thomas Graf40e22e82006-08-22 00:00:45 -0700817int ip6_ins_rt(struct rt6_info *rt)
818{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800819 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500820 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800821 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800822 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700823}
824
Gao feng1716a962012-04-06 00:13:10 +0000825static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000826 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000827 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 struct rt6_info *rt;
830
831 /*
832 * Clone the route.
833 */
834
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000835 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800838 int attempts = !in_softirq();
839
David S. Miller38308472011-12-03 18:02:47 -0500840 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500841 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000842 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900843 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000844 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900845 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
849#ifdef CONFIG_IPV6_SUBTREES
850 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000851 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 rt->rt6i_src.plen = 128;
853 }
854#endif
855
David S. Miller14deae42009-01-04 16:04:39 -0800856 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500857 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500858 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800859 int saved_rt_min_interval =
860 net->ipv6.sysctl.ip6_rt_gc_min_interval;
861 int saved_rt_elasticity =
862 net->ipv6.sysctl.ip6_rt_gc_elasticity;
863
864 if (attempts-- > 0) {
865 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
866 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
867
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000868 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800869
870 net->ipv6.sysctl.ip6_rt_gc_elasticity =
871 saved_rt_elasticity;
872 net->ipv6.sysctl.ip6_rt_gc_min_interval =
873 saved_rt_min_interval;
874 goto retry;
875 }
876
Joe Perchesf3213832012-05-15 14:11:53 +0000877 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700878 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800879 return NULL;
880 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800881 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800883 return rt;
884}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000886static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
887 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800888{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000889 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
890
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800891 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800892 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700893 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800894 }
895 return rt;
896}
897
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800898static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500899 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900{
901 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800902 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700903 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800905 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700906 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700908 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
910relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700911 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800913restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500914 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700917 rt = rt6_select(fn, oif, strict | reachable);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000918 if (rt->rt6i_nsiblings && oif == 0)
919 rt = rt6_multipath_select(rt, fl6);
David S. Miller4c9483b2011-03-12 16:22:43 -0500920 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800921 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800922 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800923 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
Changli Gaod8d1f302010-06-10 23:31:35 -0700925 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700926 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800927
David S. Miller97cac082012-07-02 22:43:47 -0700928 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500929 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800930 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500931 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800932 else
933 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800934
Amerigo Wang94e187c2012-10-29 00:13:19 +0000935 ip6_rt_put(rt);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800936 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800937
Changli Gaod8d1f302010-06-10 23:31:35 -0700938 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800939 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700940 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800941 if (!err)
942 goto out2;
943 }
944
945 if (--attempts <= 0)
946 goto out2;
947
948 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700949 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800950 * released someone could insert this route. Relookup.
951 */
Amerigo Wang94e187c2012-10-29 00:13:19 +0000952 ip6_rt_put(rt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800953 goto relookup;
954
955out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800956 if (reachable) {
957 reachable = 0;
958 goto restart_2;
959 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700960 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700961 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700963 rt->dst.lastuse = jiffies;
964 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700965
966 return rt;
967}
968
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800969static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500970 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700971{
David S. Miller4c9483b2011-03-12 16:22:43 -0500972 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700973}
974
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000975static struct dst_entry *ip6_route_input_lookup(struct net *net,
976 struct net_device *dev,
977 struct flowi6 *fl6, int flags)
978{
979 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
980 flags |= RT6_LOOKUP_F_IFACE;
981
982 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
983}
984
Thomas Grafc71099a2006-08-04 23:20:06 -0700985void ip6_route_input(struct sk_buff *skb)
986{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000987 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900988 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700989 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500990 struct flowi6 fl6 = {
991 .flowi6_iif = skb->dev->ifindex,
992 .daddr = iph->daddr,
993 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +0000994 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -0500995 .flowi6_mark = skb->mark,
996 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700997 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700998
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000999 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07001000}
1001
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001002static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001003 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07001004{
David S. Miller4c9483b2011-03-12 16:22:43 -05001005 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07001006}
1007
Florian Westphal9c7a4f92011-03-22 19:17:36 -07001008struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -05001009 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -07001010{
1011 int flags = 0;
1012
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00001013 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00001014
David S. Miller4c9483b2011-03-12 16:22:43 -05001015 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001016 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07001017
David S. Miller4c9483b2011-03-12 16:22:43 -05001018 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -07001019 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00001020 else if (sk)
1021 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07001022
David S. Miller4c9483b2011-03-12 16:22:43 -05001023 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024}
1025
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001026EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
David S. Miller2774c132011-03-01 14:59:04 -08001028struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07001029{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07001030 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -07001031 struct dst_entry *new = NULL;
1032
David S. Millerf5b0a872012-07-19 12:31:33 -07001033 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07001034 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001035 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07001036
Steffen Klassert81048912012-07-05 23:37:09 +00001037 memset(new + 1, 0, sizeof(*rt) - sizeof(*new));
1038 rt6_init_peer(rt, net->ipv6.peers);
1039
David S. Miller14e50e52007-05-24 18:17:54 -07001040 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08001041 new->input = dst_discard;
1042 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -07001043
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001044 if (dst_metrics_read_only(&ort->dst))
1045 new->_metrics = ort->dst._metrics;
1046 else
1047 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07001048 rt->rt6i_idev = ort->rt6i_idev;
1049 if (rt->rt6i_idev)
1050 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -07001051
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001052 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001053 rt->rt6i_flags = ort->rt6i_flags;
1054 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -07001055 rt->rt6i_metric = 0;
1056
1057 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1058#ifdef CONFIG_IPV6_SUBTREES
1059 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1060#endif
1061
1062 dst_free(new);
1063 }
1064
David S. Miller69ead7a2011-03-01 14:45:33 -08001065 dst_release(dst_orig);
1066 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001067}
David S. Miller14e50e52007-05-24 18:17:54 -07001068
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069/*
1070 * Destination cache support functions
1071 */
1072
1073static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1074{
1075 struct rt6_info *rt;
1076
1077 rt = (struct rt6_info *) dst;
1078
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00001079 /* All IPV6 dsts are created with ->obsolete set to the value
1080 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
1081 * into this function always.
1082 */
1083 if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
1084 return NULL;
1085
Li RongQinga4477c42012-11-07 21:56:33 +00001086 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 return dst;
Li RongQinga4477c42012-11-07 21:56:33 +00001088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 return NULL;
1090}
1091
1092static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1093{
1094 struct rt6_info *rt = (struct rt6_info *) dst;
1095
1096 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001097 if (rt->rt6i_flags & RTF_CACHE) {
1098 if (rt6_check_expired(rt)) {
1099 ip6_del_rt(rt);
1100 dst = NULL;
1101 }
1102 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001104 dst = NULL;
1105 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001107 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108}
1109
1110static void ip6_link_failure(struct sk_buff *skb)
1111{
1112 struct rt6_info *rt;
1113
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001114 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
Eric Dumazetadf30902009-06-02 05:19:30 +00001116 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001118 if (rt->rt6i_flags & RTF_CACHE)
1119 rt6_update_expires(rt, 0);
1120 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 rt->rt6i_node->fn_sernum = -1;
1122 }
1123}
1124
David S. Miller6700c272012-07-17 03:29:28 -07001125static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
1126 struct sk_buff *skb, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127{
1128 struct rt6_info *rt6 = (struct rt6_info*)dst;
1129
David S. Miller81aded22012-06-15 14:54:11 -07001130 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001132 struct net *net = dev_net(dst->dev);
1133
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 rt6->rt6i_flags |= RTF_MODIFIED;
1135 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001136 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001138 features |= RTAX_FEATURE_ALLFRAG;
1139 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 }
David S. Millerdefb3512010-12-08 21:16:57 -08001141 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001142 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 }
1144}
1145
David S. Miller42ae66c2012-06-15 20:01:57 -07001146void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1147 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001148{
1149 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1150 struct dst_entry *dst;
1151 struct flowi6 fl6;
1152
1153 memset(&fl6, 0, sizeof(fl6));
1154 fl6.flowi6_oif = oif;
1155 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001156 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001157 fl6.daddr = iph->daddr;
1158 fl6.saddr = iph->saddr;
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00001159 fl6.flowlabel = ip6_flowinfo(iph);
David S. Miller81aded22012-06-15 14:54:11 -07001160
1161 dst = ip6_route_output(net, NULL, &fl6);
1162 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001163 ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07001164 dst_release(dst);
1165}
1166EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1167
1168void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1169{
1170 ip6_update_pmtu(skb, sock_net(sk), mtu,
1171 sk->sk_bound_dev_if, sk->sk_mark);
1172}
1173EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1174
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001175void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
1176{
1177 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1178 struct dst_entry *dst;
1179 struct flowi6 fl6;
1180
1181 memset(&fl6, 0, sizeof(fl6));
1182 fl6.flowi6_oif = oif;
1183 fl6.flowi6_mark = mark;
1184 fl6.flowi6_flags = 0;
1185 fl6.daddr = iph->daddr;
1186 fl6.saddr = iph->saddr;
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00001187 fl6.flowlabel = ip6_flowinfo(iph);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001188
1189 dst = ip6_route_output(net, NULL, &fl6);
1190 if (!dst->error)
David S. Miller6700c272012-07-17 03:29:28 -07001191 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07001192 dst_release(dst);
1193}
1194EXPORT_SYMBOL_GPL(ip6_redirect);
1195
1196void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
1197{
1198 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
1199}
1200EXPORT_SYMBOL_GPL(ip6_sk_redirect);
1201
David S. Miller0dbaee32010-12-13 12:52:14 -08001202static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203{
David S. Miller0dbaee32010-12-13 12:52:14 -08001204 struct net_device *dev = dst->dev;
1205 unsigned int mtu = dst_mtu(dst);
1206 struct net *net = dev_net(dev);
1207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1209
Daniel Lezcano55786892008-03-04 13:47:47 -08001210 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1211 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
1213 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001214 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1215 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1216 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 * rely only on pmtu discovery"
1218 */
1219 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1220 mtu = IPV6_MAXPLEN;
1221 return mtu;
1222}
1223
Steffen Klassertebb762f2011-11-23 02:12:51 +00001224static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001225{
David S. Millerd33e4552010-12-14 13:01:14 -08001226 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001227 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1228
1229 if (mtu)
1230 return mtu;
1231
1232 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001233
1234 rcu_read_lock();
1235 idev = __in6_dev_get(dst->dev);
1236 if (idev)
1237 mtu = idev->cnf.mtu6;
1238 rcu_read_unlock();
1239
1240 return mtu;
1241}
1242
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001243static struct dst_entry *icmp6_dst_gc_list;
1244static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001245
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001246struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001248 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249{
David S. Miller87a11572011-12-06 17:04:13 -05001250 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 struct rt6_info *rt;
1252 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001253 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254
David S. Miller38308472011-12-03 18:02:47 -05001255 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001256 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
David S. Miller8b96d222012-06-11 02:01:56 -07001258 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001259 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001261 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 goto out;
1263 }
1264
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 if (neigh)
1266 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001267 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001268 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001269 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001270 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001271 dst_free(&rt->dst);
1272 return ERR_CAST(neigh);
1273 }
David S. Miller14deae42009-01-04 16:04:39 -08001274 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001276 rt->dst.flags |= DST_HOST;
1277 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001278 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001279 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001280 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001281 rt->rt6i_dst.plen = 128;
1282 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08001283 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001285 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001286 rt->dst.next = icmp6_dst_gc_list;
1287 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001288 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289
Daniel Lezcano55786892008-03-04 13:47:47 -08001290 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
David S. Miller87a11572011-12-06 17:04:13 -05001292 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1293
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294out:
David S. Miller87a11572011-12-06 17:04:13 -05001295 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296}
1297
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001298int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001300 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001301 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001303 spin_lock_bh(&icmp6_dst_lock);
1304 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001305
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 while ((dst = *pprev) != NULL) {
1307 if (!atomic_read(&dst->__refcnt)) {
1308 *pprev = dst->next;
1309 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 } else {
1311 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001312 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 }
1314 }
1315
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001316 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001317
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001318 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319}
1320
David S. Miller1e493d12008-09-10 17:27:15 -07001321static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1322 void *arg)
1323{
1324 struct dst_entry *dst, **pprev;
1325
1326 spin_lock_bh(&icmp6_dst_lock);
1327 pprev = &icmp6_dst_gc_list;
1328 while ((dst = *pprev) != NULL) {
1329 struct rt6_info *rt = (struct rt6_info *) dst;
1330 if (func(rt, arg)) {
1331 *pprev = dst->next;
1332 dst_free(dst);
1333 } else {
1334 pprev = &dst->next;
1335 }
1336 }
1337 spin_unlock_bh(&icmp6_dst_lock);
1338}
1339
Daniel Lezcano569d3642008-01-18 03:56:57 -08001340static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001343 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001344 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1345 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1346 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1347 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1348 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001349 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
Eric Dumazetfc66f952010-10-08 06:37:34 +00001351 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001352 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001353 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 goto out;
1355
Benjamin Thery6891a342008-03-04 13:49:47 -08001356 net->ipv6.ip6_rt_gc_expire++;
1357 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1358 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001359 entries = dst_entries_get_slow(ops);
1360 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001361 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001363 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001364 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365}
1366
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001367int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368{
David S. Miller5170ae82010-12-12 21:35:57 -08001369 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001370 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001371 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001372 struct inet6_dev *idev;
1373
1374 rcu_read_lock();
1375 idev = __in6_dev_get(dev);
1376 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001377 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001378 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001379 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001380 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 }
1382 return hoplimit;
1383}
David S. Millerabbf46a2010-12-12 21:14:46 -08001384EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
1386/*
1387 *
1388 */
1389
Thomas Graf86872cb2006-08-22 00:01:08 -07001390int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391{
1392 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001393 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 struct rt6_info *rt = NULL;
1395 struct net_device *dev = NULL;
1396 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001397 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 int addr_type;
1399
Thomas Graf86872cb2006-08-22 00:01:08 -07001400 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 return -EINVAL;
1402#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001403 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 return -EINVAL;
1405#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001406 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001408 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 if (!dev)
1410 goto out;
1411 idev = in6_dev_get(dev);
1412 if (!idev)
1413 goto out;
1414 }
1415
Thomas Graf86872cb2006-08-22 00:01:08 -07001416 if (cfg->fc_metric == 0)
1417 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
Matti Vaittinend71314b2011-11-14 00:14:49 +00001419 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001420 if (cfg->fc_nlinfo.nlh &&
1421 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001422 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001423 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001424 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001425 table = fib6_new_table(net, cfg->fc_table);
1426 }
1427 } else {
1428 table = fib6_new_table(net, cfg->fc_table);
1429 }
David S. Miller38308472011-12-03 18:02:47 -05001430
1431 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001432 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001433
David S. Miller8b96d222012-06-11 02:01:56 -07001434 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435
David S. Miller38308472011-12-03 18:02:47 -05001436 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 err = -ENOMEM;
1438 goto out;
1439 }
1440
Gao feng1716a962012-04-06 00:13:10 +00001441 if (cfg->fc_flags & RTF_EXPIRES)
1442 rt6_set_expires(rt, jiffies +
1443 clock_t_to_jiffies(cfg->fc_expires));
1444 else
1445 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446
Thomas Graf86872cb2006-08-22 00:01:08 -07001447 if (cfg->fc_protocol == RTPROT_UNSPEC)
1448 cfg->fc_protocol = RTPROT_BOOT;
1449 rt->rt6i_protocol = cfg->fc_protocol;
1450
1451 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
1453 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001454 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001455 else if (cfg->fc_flags & RTF_LOCAL)
1456 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001458 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459
Changli Gaod8d1f302010-06-10 23:31:35 -07001460 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461
Thomas Graf86872cb2006-08-22 00:01:08 -07001462 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1463 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001465 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001467 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1468 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1469 if (!metrics) {
1470 err = -ENOMEM;
1471 goto out;
1472 }
1473 dst_init_metrics(&rt->dst, metrics, 0);
1474 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001476 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1477 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478#endif
1479
Thomas Graf86872cb2006-08-22 00:01:08 -07001480 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
1482 /* We cannot add true routes via loopback here,
1483 they would result in kernel looping; promote them to reject routes
1484 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001485 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001486 (dev && (dev->flags & IFF_LOOPBACK) &&
1487 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1488 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001490 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 if (dev) {
1492 dev_put(dev);
1493 in6_dev_put(idev);
1494 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001495 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 dev_hold(dev);
1497 idev = in6_dev_get(dev);
1498 if (!idev) {
1499 err = -ENODEV;
1500 goto out;
1501 }
1502 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001503 rt->dst.output = ip6_pkt_discard_out;
1504 rt->dst.input = ip6_pkt_discard;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001506 switch (cfg->fc_type) {
1507 case RTN_BLACKHOLE:
1508 rt->dst.error = -EINVAL;
1509 break;
1510 case RTN_PROHIBIT:
1511 rt->dst.error = -EACCES;
1512 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00001513 case RTN_THROW:
1514 rt->dst.error = -EAGAIN;
1515 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00001516 default:
1517 rt->dst.error = -ENETUNREACH;
1518 break;
1519 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 goto install_route;
1521 }
1522
Thomas Graf86872cb2006-08-22 00:01:08 -07001523 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001524 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 int gwa_type;
1526
Thomas Graf86872cb2006-08-22 00:01:08 -07001527 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001528 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 gwa_type = ipv6_addr_type(gw_addr);
1530
1531 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1532 struct rt6_info *grt;
1533
1534 /* IPv6 strictly inhibits using not link-local
1535 addresses as nexthop address.
1536 Otherwise, router will not able to send redirects.
1537 It is very good, but in some (rare!) circumstances
1538 (SIT, PtP, NBMA NOARP links) it is handy to allow
1539 some exceptions. --ANK
1540 */
1541 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001542 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 goto out;
1544
Daniel Lezcano55786892008-03-04 13:47:47 -08001545 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
1547 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001548 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 goto out;
1550 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001551 if (dev != grt->dst.dev) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00001552 ip6_rt_put(grt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 goto out;
1554 }
1555 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001556 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 idev = grt->rt6i_idev;
1558 dev_hold(dev);
1559 in6_dev_hold(grt->rt6i_idev);
1560 }
David S. Miller38308472011-12-03 18:02:47 -05001561 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 err = 0;
Amerigo Wang94e187c2012-10-29 00:13:19 +00001563 ip6_rt_put(grt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564
1565 if (err)
1566 goto out;
1567 }
1568 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001569 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 goto out;
1571 }
1572
1573 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001574 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 goto out;
1576
Daniel Walterc3968a82011-04-13 21:10:57 +00001577 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1578 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1579 err = -EINVAL;
1580 goto out;
1581 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001582 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001583 rt->rt6i_prefsrc.plen = 128;
1584 } else
1585 rt->rt6i_prefsrc.plen = 0;
1586
Thomas Graf86872cb2006-08-22 00:01:08 -07001587 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001588 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001589 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 }
1592
Thomas Graf86872cb2006-08-22 00:01:08 -07001593 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594
1595install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001596 if (cfg->fc_mx) {
1597 struct nlattr *nla;
1598 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Thomas Graf86872cb2006-08-22 00:01:08 -07001600 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001601 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001602
1603 if (type) {
1604 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 err = -EINVAL;
1606 goto out;
1607 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001608
David S. Millerdefb3512010-12-08 21:16:57 -08001609 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 }
1612 }
1613
Changli Gaod8d1f302010-06-10 23:31:35 -07001614 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001616 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001617
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001618 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001619
Thomas Graf86872cb2006-08-22 00:01:08 -07001620 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
1622out:
1623 if (dev)
1624 dev_put(dev);
1625 if (idev)
1626 in6_dev_put(idev);
1627 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001628 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 return err;
1630}
1631
Thomas Graf86872cb2006-08-22 00:01:08 -07001632static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633{
1634 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001635 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001636 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
Gao feng6825a262012-09-19 19:25:34 +00001638 if (rt == net->ipv6.ip6_null_entry) {
1639 err = -ENOENT;
1640 goto out;
1641 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07001642
Thomas Grafc71099a2006-08-04 23:20:06 -07001643 table = rt->rt6i_table;
1644 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07001645 err = fib6_del(rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -07001646 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
Gao feng6825a262012-09-19 19:25:34 +00001648out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001649 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 return err;
1651}
1652
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001653int ip6_del_rt(struct rt6_info *rt)
1654{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001655 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001656 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001657 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001658 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001659}
1660
Thomas Graf86872cb2006-08-22 00:01:08 -07001661static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662{
Thomas Grafc71099a2006-08-04 23:20:06 -07001663 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 struct fib6_node *fn;
1665 struct rt6_info *rt;
1666 int err = -ESRCH;
1667
Daniel Lezcano55786892008-03-04 13:47:47 -08001668 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001669 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001670 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671
Thomas Grafc71099a2006-08-04 23:20:06 -07001672 read_lock_bh(&table->tb6_lock);
1673
1674 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001675 &cfg->fc_dst, cfg->fc_dst_len,
1676 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001677
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001679 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001680 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001681 (!rt->dst.dev ||
1682 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001684 if (cfg->fc_flags & RTF_GATEWAY &&
1685 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001687 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001689 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001690 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691
Thomas Graf86872cb2006-08-22 00:01:08 -07001692 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 }
1694 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001695 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696
1697 return err;
1698}
1699
David S. Miller6700c272012-07-17 03:29:28 -07001700static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001701{
David S. Millere8599ff2012-07-11 23:43:53 -07001702 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001703 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001704 struct rt6_info *rt, *nrt = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001705 struct ndisc_options ndopts;
David S. Miller6e157b62012-07-12 00:05:02 -07001706 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001707 struct inet6_dev *in6_dev;
1708 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001709 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07001710 int optlen, on_link;
1711 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07001712
1713 optlen = skb->tail - skb->transport_header;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001714 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07001715
1716 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07001717 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001718 return;
1719 }
1720
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001721 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07001722
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001723 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001724 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001725 return;
1726 }
1727
David S. Miller6e157b62012-07-12 00:05:02 -07001728 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001729 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07001730 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001731 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07001732 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07001733 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07001734 return;
1735 }
1736
1737 in6_dev = __in6_dev_get(skb->dev);
1738 if (!in6_dev)
1739 return;
1740 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1741 return;
1742
1743 /* RFC2461 8.1:
1744 * The IP source address of the Redirect MUST be the same as the current
1745 * first-hop router for the specified ICMP Destination Address.
1746 */
1747
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001748 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07001749 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1750 return;
1751 }
David S. Miller6e157b62012-07-12 00:05:02 -07001752
1753 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07001754 if (ndopts.nd_opts_tgt_lladdr) {
1755 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1756 skb->dev);
1757 if (!lladdr) {
1758 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1759 return;
1760 }
1761 }
1762
David S. Miller6e157b62012-07-12 00:05:02 -07001763 rt = (struct rt6_info *) dst;
1764 if (rt == net->ipv6.ip6_null_entry) {
1765 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
1766 return;
1767 }
1768
1769 /* Redirect received -> path was valid.
1770 * Look, redirects are sent only in response to data packets,
1771 * so that this nexthop apparently is reachable. --ANK
1772 */
1773 dst_confirm(&rt->dst);
1774
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001775 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07001776 if (!neigh)
1777 return;
1778
David S. Miller6e157b62012-07-12 00:05:02 -07001779 /* Duplicate redirect: silently ignore. */
1780 old_neigh = rt->n;
1781 if (neigh == old_neigh)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001782 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 /*
1785 * We have finally decided to accept it.
1786 */
1787
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001788 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1790 NEIGH_UPDATE_F_OVERRIDE|
1791 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1792 NEIGH_UPDATE_F_ISROUTER))
1793 );
1794
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001795 nrt = ip6_rt_copy(rt, &msg->dest);
David S. Miller38308472011-12-03 18:02:47 -05001796 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 goto out;
1798
1799 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1800 if (on_link)
1801 nrt->rt6i_flags &= ~RTF_GATEWAY;
1802
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001803 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001804 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805
Thomas Graf40e22e82006-08-22 00:00:45 -07001806 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 goto out;
1808
Changli Gaod8d1f302010-06-10 23:31:35 -07001809 netevent.old = &rt->dst;
1810 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001811 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00001812 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07001813 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1814
David S. Miller38308472011-12-03 18:02:47 -05001815 if (rt->rt6i_flags & RTF_CACHE) {
David S. Miller6e157b62012-07-12 00:05:02 -07001816 rt = (struct rt6_info *) dst_clone(&rt->dst);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001817 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 }
1819
1820out:
David S. Millere8599ff2012-07-11 23:43:53 -07001821 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07001822}
1823
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 * Misc support functions
1826 */
1827
Gao feng1716a962012-04-06 00:13:10 +00001828static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001829 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830{
David S. Millerd1918542011-12-28 20:19:20 -05001831 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001832 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1833 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834
1835 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001836 rt->dst.input = ort->dst.input;
1837 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001838 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001840 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001841 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001842 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001843 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 rt->rt6i_idev = ort->rt6i_idev;
1845 if (rt->rt6i_idev)
1846 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001847 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001849 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001850 rt->rt6i_flags = ort->rt6i_flags;
1851 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1852 (RTF_DEFAULT | RTF_ADDRCONF))
1853 rt6_set_from(rt, ort);
1854 else
1855 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 rt->rt6i_metric = 0;
1857
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858#ifdef CONFIG_IPV6_SUBTREES
1859 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1860#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001861 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001862 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863 }
1864 return rt;
1865}
1866
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001867#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001868static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001869 const struct in6_addr *prefix, int prefixlen,
1870 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001871{
1872 struct fib6_node *fn;
1873 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001874 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001875
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001876 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001877 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001878 return NULL;
1879
Li RongQing5744dd92012-09-11 21:59:01 +00001880 read_lock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -07001881 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001882 if (!fn)
1883 goto out;
1884
Changli Gaod8d1f302010-06-10 23:31:35 -07001885 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001886 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001887 continue;
1888 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1889 continue;
1890 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1891 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001892 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001893 break;
1894 }
1895out:
Li RongQing5744dd92012-09-11 21:59:01 +00001896 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001897 return rt;
1898}
1899
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001900static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001901 const struct in6_addr *prefix, int prefixlen,
1902 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001903 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001904{
Thomas Graf86872cb2006-08-22 00:01:08 -07001905 struct fib6_config cfg = {
1906 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001907 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001908 .fc_ifindex = ifindex,
1909 .fc_dst_len = prefixlen,
1910 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1911 RTF_UP | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001912 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001913 .fc_nlinfo.nlh = NULL,
1914 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001915 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001916
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001917 cfg.fc_dst = *prefix;
1918 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001919
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001920 /* We should treat it as a default route if prefix length is 0. */
1921 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001922 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001923
Thomas Graf86872cb2006-08-22 00:01:08 -07001924 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001925
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001926 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001927}
1928#endif
1929
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001930struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001931{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001933 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001935 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001936 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001937 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938
Li RongQing5744dd92012-09-11 21:59:01 +00001939 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001940 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001941 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001942 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1944 break;
1945 }
1946 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001947 dst_hold(&rt->dst);
Li RongQing5744dd92012-09-11 21:59:01 +00001948 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 return rt;
1950}
1951
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001952struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001953 struct net_device *dev,
1954 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Thomas Graf86872cb2006-08-22 00:01:08 -07001956 struct fib6_config cfg = {
1957 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001958 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001959 .fc_ifindex = dev->ifindex,
1960 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1961 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Eric W. Biederman15e47302012-09-07 20:12:54 +00001962 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08001963 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001964 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001965 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001967 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968
Thomas Graf86872cb2006-08-22 00:01:08 -07001969 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 return rt6_get_dflt_router(gwaddr, dev);
1972}
1973
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001974void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975{
1976 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001977 struct fib6_table *table;
1978
1979 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001980 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001981 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001982 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
1984restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001985 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001986 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001988 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001989 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001990 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 goto restart;
1992 }
1993 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001994 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995}
1996
Daniel Lezcano55786892008-03-04 13:47:47 -08001997static void rtmsg_to_fib6_config(struct net *net,
1998 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001999 struct fib6_config *cfg)
2000{
2001 memset(cfg, 0, sizeof(*cfg));
2002
2003 cfg->fc_table = RT6_TABLE_MAIN;
2004 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
2005 cfg->fc_metric = rtmsg->rtmsg_metric;
2006 cfg->fc_expires = rtmsg->rtmsg_info;
2007 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
2008 cfg->fc_src_len = rtmsg->rtmsg_src_len;
2009 cfg->fc_flags = rtmsg->rtmsg_flags;
2010
Daniel Lezcano55786892008-03-04 13:47:47 -08002011 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08002012
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002013 cfg->fc_dst = rtmsg->rtmsg_dst;
2014 cfg->fc_src = rtmsg->rtmsg_src;
2015 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07002016}
2017
Daniel Lezcano55786892008-03-04 13:47:47 -08002018int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019{
Thomas Graf86872cb2006-08-22 00:01:08 -07002020 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 struct in6_rtmsg rtmsg;
2022 int err;
2023
2024 switch(cmd) {
2025 case SIOCADDRT: /* Add a route */
2026 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00002027 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028 return -EPERM;
2029 err = copy_from_user(&rtmsg, arg,
2030 sizeof(struct in6_rtmsg));
2031 if (err)
2032 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07002033
Daniel Lezcano55786892008-03-04 13:47:47 -08002034 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07002035
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 rtnl_lock();
2037 switch (cmd) {
2038 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002039 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 break;
2041 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002042 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 break;
2044 default:
2045 err = -EINVAL;
2046 }
2047 rtnl_unlock();
2048
2049 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002050 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051
2052 return -EINVAL;
2053}
2054
2055/*
2056 * Drop the packet on the floor
2057 */
2058
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002059static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002061 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002062 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002063 switch (ipstats_mib_noroutes) {
2064 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002065 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002066 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002067 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2068 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002069 break;
2070 }
2071 /* FALLTHROUGH */
2072 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002073 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2074 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002075 break;
2076 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002077 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 kfree_skb(skb);
2079 return 0;
2080}
2081
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002082static int ip6_pkt_discard(struct sk_buff *skb)
2083{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002084 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002085}
2086
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002087static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088{
Eric Dumazetadf30902009-06-02 05:19:30 +00002089 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002090 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091}
2092
David S. Miller6723ab52006-10-18 21:20:57 -07002093#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2094
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002095static int ip6_pkt_prohibit(struct sk_buff *skb)
2096{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002097 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002098}
2099
2100static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2101{
Eric Dumazetadf30902009-06-02 05:19:30 +00002102 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002103 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002104}
2105
David S. Miller6723ab52006-10-18 21:20:57 -07002106#endif
2107
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108/*
2109 * Allocate a dst for local (unicast / anycast) address.
2110 */
2111
2112struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2113 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002114 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002116 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002117 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002118 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
David S. Miller38308472011-12-03 18:02:47 -05002120 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002121 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002123 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 in6_dev_hold(idev);
2126
David S. Miller11d53b42011-06-24 15:23:34 -07002127 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002128 rt->dst.input = ip6_input;
2129 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 rt->rt6i_idev = idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131
2132 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002133 if (anycast)
2134 rt->rt6i_flags |= RTF_ANYCAST;
2135 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002137 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002138 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002139 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002140 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 }
2142
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002143 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002145 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146
Changli Gaod8d1f302010-06-10 23:31:35 -07002147 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148
2149 return rt;
2150}
2151
Daniel Walterc3968a82011-04-13 21:10:57 +00002152int ip6_route_get_saddr(struct net *net,
2153 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002154 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002155 unsigned int prefs,
2156 struct in6_addr *saddr)
2157{
2158 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2159 int err = 0;
2160 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002161 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002162 else
2163 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2164 daddr, prefs, saddr);
2165 return err;
2166}
2167
2168/* remove deleted ip from prefsrc entries */
2169struct arg_dev_net_ip {
2170 struct net_device *dev;
2171 struct net *net;
2172 struct in6_addr *addr;
2173};
2174
2175static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2176{
2177 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2178 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2179 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2180
David S. Millerd1918542011-12-28 20:19:20 -05002181 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002182 rt != net->ipv6.ip6_null_entry &&
2183 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2184 /* remove prefsrc entry */
2185 rt->rt6i_prefsrc.plen = 0;
2186 }
2187 return 0;
2188}
2189
2190void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2191{
2192 struct net *net = dev_net(ifp->idev->dev);
2193 struct arg_dev_net_ip adni = {
2194 .dev = ifp->idev->dev,
2195 .net = net,
2196 .addr = &ifp->addr,
2197 };
2198 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2199}
2200
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002201struct arg_dev_net {
2202 struct net_device *dev;
2203 struct net *net;
2204};
2205
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206static int fib6_ifdown(struct rt6_info *rt, void *arg)
2207{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002208 const struct arg_dev_net *adn = arg;
2209 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002210
David S. Millerd1918542011-12-28 20:19:20 -05002211 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002212 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002214
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 return 0;
2216}
2217
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002218void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002220 struct arg_dev_net adn = {
2221 .dev = dev,
2222 .net = net,
2223 };
2224
2225 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002226 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227}
2228
Eric Dumazet95c96172012-04-15 05:58:06 +00002229struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002231 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232};
2233
2234static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2235{
2236 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2237 struct inet6_dev *idev;
2238
2239 /* In IPv6 pmtu discovery is not optional,
2240 so that RTAX_MTU lock cannot disable it.
2241 We still use this lock to block changes
2242 caused by addrconf/ndisc.
2243 */
2244
2245 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002246 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 return 0;
2248
2249 /* For administrative MTU increase, there is no way to discover
2250 IPv6 PMTU increase, so PMTU increase should be updated here.
2251 Since RFC 1981 doesn't include administrative MTU increase
2252 update PMTU increase is a MUST. (i.e. jumbo frame)
2253 */
2254 /*
2255 If new MTU is less than route PMTU, this new MTU will be the
2256 lowest MTU in the path, update the route PMTU to reflect PMTU
2257 decreases; if new MTU is greater than route PMTU, and the
2258 old MTU is the lowest MTU in the path, update the route PMTU
2259 to reflect the increase. In this case if the other nodes' MTU
2260 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2261 PMTU discouvery.
2262 */
David S. Millerd1918542011-12-28 20:19:20 -05002263 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002264 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2265 (dst_mtu(&rt->dst) >= arg->mtu ||
2266 (dst_mtu(&rt->dst) < arg->mtu &&
2267 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002268 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002269 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 return 0;
2271}
2272
Eric Dumazet95c96172012-04-15 05:58:06 +00002273void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274{
Thomas Grafc71099a2006-08-04 23:20:06 -07002275 struct rt6_mtu_change_arg arg = {
2276 .dev = dev,
2277 .mtu = mtu,
2278 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002280 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281}
2282
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002283static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002284 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002285 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002286 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002287 [RTA_PRIORITY] = { .type = NLA_U32 },
2288 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00002289 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002290};
2291
2292static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2293 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294{
Thomas Graf86872cb2006-08-22 00:01:08 -07002295 struct rtmsg *rtm;
2296 struct nlattr *tb[RTA_MAX+1];
2297 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298
Thomas Graf86872cb2006-08-22 00:01:08 -07002299 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2300 if (err < 0)
2301 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302
Thomas Graf86872cb2006-08-22 00:01:08 -07002303 err = -EINVAL;
2304 rtm = nlmsg_data(nlh);
2305 memset(cfg, 0, sizeof(*cfg));
2306
2307 cfg->fc_table = rtm->rtm_table;
2308 cfg->fc_dst_len = rtm->rtm_dst_len;
2309 cfg->fc_src_len = rtm->rtm_src_len;
2310 cfg->fc_flags = RTF_UP;
2311 cfg->fc_protocol = rtm->rtm_protocol;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002312 cfg->fc_type = rtm->rtm_type;
Thomas Graf86872cb2006-08-22 00:01:08 -07002313
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002314 if (rtm->rtm_type == RTN_UNREACHABLE ||
2315 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002316 rtm->rtm_type == RTN_PROHIBIT ||
2317 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07002318 cfg->fc_flags |= RTF_REJECT;
2319
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002320 if (rtm->rtm_type == RTN_LOCAL)
2321 cfg->fc_flags |= RTF_LOCAL;
2322
Eric W. Biederman15e47302012-09-07 20:12:54 +00002323 cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid;
Thomas Graf86872cb2006-08-22 00:01:08 -07002324 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002325 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002326
2327 if (tb[RTA_GATEWAY]) {
2328 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2329 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002331
2332 if (tb[RTA_DST]) {
2333 int plen = (rtm->rtm_dst_len + 7) >> 3;
2334
2335 if (nla_len(tb[RTA_DST]) < plen)
2336 goto errout;
2337
2338 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002340
2341 if (tb[RTA_SRC]) {
2342 int plen = (rtm->rtm_src_len + 7) >> 3;
2343
2344 if (nla_len(tb[RTA_SRC]) < plen)
2345 goto errout;
2346
2347 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002349
Daniel Walterc3968a82011-04-13 21:10:57 +00002350 if (tb[RTA_PREFSRC])
2351 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2352
Thomas Graf86872cb2006-08-22 00:01:08 -07002353 if (tb[RTA_OIF])
2354 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2355
2356 if (tb[RTA_PRIORITY])
2357 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2358
2359 if (tb[RTA_METRICS]) {
2360 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2361 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002363
2364 if (tb[RTA_TABLE])
2365 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2366
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00002367 if (tb[RTA_MULTIPATH]) {
2368 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
2369 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
2370 }
2371
Thomas Graf86872cb2006-08-22 00:01:08 -07002372 err = 0;
2373errout:
2374 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375}
2376
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00002377static int ip6_route_multipath(struct fib6_config *cfg, int add)
2378{
2379 struct fib6_config r_cfg;
2380 struct rtnexthop *rtnh;
2381 int remaining;
2382 int attrlen;
2383 int err = 0, last_err = 0;
2384
2385beginning:
2386 rtnh = (struct rtnexthop *)cfg->fc_mp;
2387 remaining = cfg->fc_mp_len;
2388
2389 /* Parse a Multipath Entry */
2390 while (rtnh_ok(rtnh, remaining)) {
2391 memcpy(&r_cfg, cfg, sizeof(*cfg));
2392 if (rtnh->rtnh_ifindex)
2393 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
2394
2395 attrlen = rtnh_attrlen(rtnh);
2396 if (attrlen > 0) {
2397 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
2398
2399 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
2400 if (nla) {
2401 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
2402 r_cfg.fc_flags |= RTF_GATEWAY;
2403 }
2404 }
2405 err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
2406 if (err) {
2407 last_err = err;
2408 /* If we are trying to remove a route, do not stop the
2409 * loop when ip6_route_del() fails (because next hop is
2410 * already gone), we should try to remove all next hops.
2411 */
2412 if (add) {
2413 /* If add fails, we should try to delete all
2414 * next hops that have been already added.
2415 */
2416 add = 0;
2417 goto beginning;
2418 }
2419 }
Nicolas Dichtel1a724182012-11-01 22:58:22 +00002420 /* Because each route is added like a single route we remove
2421 * this flag after the first nexthop (if there is a collision,
2422 * we have already fail to add the first nexthop:
2423 * fib6_add_rt2node() has reject it).
2424 */
2425 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00002426 rtnh = rtnh_next(rtnh, &remaining);
2427 }
2428
2429 return last_err;
2430}
2431
Thomas Grafc127ea22007-03-22 11:58:32 -07002432static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433{
Thomas Graf86872cb2006-08-22 00:01:08 -07002434 struct fib6_config cfg;
2435 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436
Thomas Graf86872cb2006-08-22 00:01:08 -07002437 err = rtm_to_fib6_config(skb, nlh, &cfg);
2438 if (err < 0)
2439 return err;
2440
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00002441 if (cfg.fc_mp)
2442 return ip6_route_multipath(&cfg, 0);
2443 else
2444 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445}
2446
Thomas Grafc127ea22007-03-22 11:58:32 -07002447static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448{
Thomas Graf86872cb2006-08-22 00:01:08 -07002449 struct fib6_config cfg;
2450 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451
Thomas Graf86872cb2006-08-22 00:01:08 -07002452 err = rtm_to_fib6_config(skb, nlh, &cfg);
2453 if (err < 0)
2454 return err;
2455
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00002456 if (cfg.fc_mp)
2457 return ip6_route_multipath(&cfg, 1);
2458 else
2459 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460}
2461
Thomas Graf339bf982006-11-10 14:10:15 -08002462static inline size_t rt6_nlmsg_size(void)
2463{
2464 return NLMSG_ALIGN(sizeof(struct rtmsg))
2465 + nla_total_size(16) /* RTA_SRC */
2466 + nla_total_size(16) /* RTA_DST */
2467 + nla_total_size(16) /* RTA_GATEWAY */
2468 + nla_total_size(16) /* RTA_PREFSRC */
2469 + nla_total_size(4) /* RTA_TABLE */
2470 + nla_total_size(4) /* RTA_IIF */
2471 + nla_total_size(4) /* RTA_OIF */
2472 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002473 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002474 + nla_total_size(sizeof(struct rta_cacheinfo));
2475}
2476
Brian Haley191cd582008-08-14 15:33:21 -07002477static int rt6_fill_node(struct net *net,
2478 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002479 struct in6_addr *dst, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002480 int iif, int type, u32 portid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002481 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482{
2483 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002484 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002485 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002486 u32 table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487
2488 if (prefix) { /* user wants prefix routes only */
2489 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2490 /* success since this is not a prefix route */
2491 return 1;
2492 }
2493 }
2494
Eric W. Biederman15e47302012-09-07 20:12:54 +00002495 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002496 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002497 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002498
2499 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500 rtm->rtm_family = AF_INET6;
2501 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2502 rtm->rtm_src_len = rt->rt6i_src.plen;
2503 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002504 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002505 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002506 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002507 table = RT6_TABLE_UNSPEC;
2508 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002509 if (nla_put_u32(skb, RTA_TABLE, table))
2510 goto nla_put_failure;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002511 if (rt->rt6i_flags & RTF_REJECT) {
2512 switch (rt->dst.error) {
2513 case -EINVAL:
2514 rtm->rtm_type = RTN_BLACKHOLE;
2515 break;
2516 case -EACCES:
2517 rtm->rtm_type = RTN_PROHIBIT;
2518 break;
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00002519 case -EAGAIN:
2520 rtm->rtm_type = RTN_THROW;
2521 break;
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00002522 default:
2523 rtm->rtm_type = RTN_UNREACHABLE;
2524 break;
2525 }
2526 }
David S. Miller38308472011-12-03 18:02:47 -05002527 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002528 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002529 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 rtm->rtm_type = RTN_LOCAL;
2531 else
2532 rtm->rtm_type = RTN_UNICAST;
2533 rtm->rtm_flags = 0;
2534 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2535 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002536 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537 rtm->rtm_protocol = RTPROT_REDIRECT;
Denis Ovsienkof0396f602012-07-10 04:45:50 +00002538 else if (rt->rt6i_flags & RTF_ADDRCONF) {
2539 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO))
2540 rtm->rtm_protocol = RTPROT_RA;
2541 else
2542 rtm->rtm_protocol = RTPROT_KERNEL;
2543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544
David S. Miller38308472011-12-03 18:02:47 -05002545 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 rtm->rtm_flags |= RTM_F_CLONED;
2547
2548 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002549 if (nla_put(skb, RTA_DST, 16, dst))
2550 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002551 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002553 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2554 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555#ifdef CONFIG_IPV6_SUBTREES
2556 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002557 if (nla_put(skb, RTA_SRC, 16, src))
2558 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002559 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002560 } else if (rtm->rtm_src_len &&
2561 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2562 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002564 if (iif) {
2565#ifdef CONFIG_IPV6_MROUTE
2566 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002567 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002568 if (err <= 0) {
2569 if (!nowait) {
2570 if (err == 0)
2571 return 0;
2572 goto nla_put_failure;
2573 } else {
2574 if (err == -EMSGSIZE)
2575 goto nla_put_failure;
2576 }
2577 }
2578 } else
2579#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002580 if (nla_put_u32(skb, RTA_IIF, iif))
2581 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002582 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002584 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2585 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2586 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002588
Daniel Walterc3968a82011-04-13 21:10:57 +00002589 if (rt->rt6i_prefsrc.plen) {
2590 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002591 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002592 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2593 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002594 }
2595
David S. Millerdefb3512010-12-08 21:16:57 -08002596 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002597 goto nla_put_failure;
2598
YOSHIFUJI Hideaki / 吉藤英明dd0cbf22013-01-17 12:53:15 +00002599 if (rt->rt6i_flags & RTF_GATEWAY) {
2600 if (nla_put(skb, RTA_GATEWAY, 16, &rt->rt6i_gateway) < 0)
Eric Dumazet94f826b2012-03-27 09:53:52 +00002601 goto nla_put_failure;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002602 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002603
David S. Millerc78679e2012-04-01 20:27:33 -04002604 if (rt->dst.dev &&
2605 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2606 goto nla_put_failure;
2607 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2608 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00002609
2610 expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002611
David S. Miller87a50692012-07-10 05:06:14 -07002612 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002613 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614
Thomas Graf2d7202b2006-08-22 00:01:27 -07002615 return nlmsg_end(skb, nlh);
2616
2617nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002618 nlmsg_cancel(skb, nlh);
2619 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620}
2621
Patrick McHardy1b43af52006-08-10 23:11:17 -07002622int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623{
2624 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2625 int prefix;
2626
Thomas Graf2d7202b2006-08-22 00:01:27 -07002627 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2628 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2630 } else
2631 prefix = 0;
2632
Brian Haley191cd582008-08-14 15:33:21 -07002633 return rt6_fill_node(arg->net,
2634 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002635 NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002636 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637}
2638
Thomas Grafc127ea22007-03-22 11:58:32 -07002639static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002641 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002642 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002644 struct sk_buff *skb;
2645 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002646 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002647 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002648
2649 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2650 if (err < 0)
2651 goto errout;
2652
2653 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002654 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002655
2656 if (tb[RTA_SRC]) {
2657 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2658 goto errout;
2659
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002660 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002661 }
2662
2663 if (tb[RTA_DST]) {
2664 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2665 goto errout;
2666
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002667 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002668 }
2669
2670 if (tb[RTA_IIF])
2671 iif = nla_get_u32(tb[RTA_IIF]);
2672
2673 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002674 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002675
2676 if (iif) {
2677 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002678 int flags = 0;
2679
Daniel Lezcano55786892008-03-04 13:47:47 -08002680 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002681 if (!dev) {
2682 err = -ENODEV;
2683 goto errout;
2684 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002685
2686 fl6.flowi6_iif = iif;
2687
2688 if (!ipv6_addr_any(&fl6.saddr))
2689 flags |= RT6_LOOKUP_F_HAS_SADDR;
2690
2691 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2692 flags);
2693 } else {
2694 fl6.flowi6_oif = oif;
2695
2696 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002697 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698
2699 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002700 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00002701 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07002702 err = -ENOBUFS;
2703 goto errout;
2704 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705
2706 /* Reserve room for dummy headers, this skb can pass
2707 through good chunk of routing engine.
2708 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002709 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2711
Changli Gaod8d1f302010-06-10 23:31:35 -07002712 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713
David S. Miller4c9483b2011-03-12 16:22:43 -05002714 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002715 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002716 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002718 kfree_skb(skb);
2719 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720 }
2721
Eric W. Biederman15e47302012-09-07 20:12:54 +00002722 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07002723errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725}
2726
Thomas Graf86872cb2006-08-22 00:01:08 -07002727void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728{
2729 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002730 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002731 u32 seq;
2732 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002734 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002735 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002736
Thomas Graf339bf982006-11-10 14:10:15 -08002737 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002738 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002739 goto errout;
2740
Brian Haley191cd582008-08-14 15:33:21 -07002741 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
Eric W. Biederman15e47302012-09-07 20:12:54 +00002742 event, info->portid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002743 if (err < 0) {
2744 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2745 WARN_ON(err == -EMSGSIZE);
2746 kfree_skb(skb);
2747 goto errout;
2748 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00002749 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002750 info->nlh, gfp_any());
2751 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002752errout:
2753 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002754 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755}
2756
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002757static int ip6_route_dev_notify(struct notifier_block *this,
2758 unsigned long event, void *data)
2759{
2760 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002761 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002762
2763 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002764 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002765 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2766#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002767 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002768 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002769 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002770 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2771#endif
2772 }
2773
2774 return NOTIFY_OK;
2775}
2776
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777/*
2778 * /proc
2779 */
2780
2781#ifdef CONFIG_PROC_FS
2782
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783struct rt6_proc_arg
2784{
2785 char *buffer;
2786 int offset;
2787 int length;
2788 int skip;
2789 int len;
2790};
2791
2792static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2793{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002794 struct seq_file *m = p_arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002796 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797
2798#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002799 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002801 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802#endif
YOSHIFUJI Hideaki / 吉藤英明dd0cbf22013-01-17 12:53:15 +00002803 if (rt->rt6i_flags & RTF_GATEWAY) {
2804 seq_printf(m, "%pi6", &rt->rt6i_gateway);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002806 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 }
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002808 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002809 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2810 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002811 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 return 0;
2813}
2814
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002815static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002817 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002818 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002819 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820}
2821
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002822static int ipv6_route_open(struct inode *inode, struct file *file)
2823{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002824 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002825}
2826
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002827static const struct file_operations ipv6_route_proc_fops = {
2828 .owner = THIS_MODULE,
2829 .open = ipv6_route_open,
2830 .read = seq_read,
2831 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002832 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002833};
2834
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2836{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002837 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002839 net->ipv6.rt6_stats->fib_nodes,
2840 net->ipv6.rt6_stats->fib_route_nodes,
2841 net->ipv6.rt6_stats->fib_rt_alloc,
2842 net->ipv6.rt6_stats->fib_rt_entries,
2843 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002844 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002845 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846
2847 return 0;
2848}
2849
2850static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2851{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002852 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002853}
2854
Arjan van de Ven9a321442007-02-12 00:55:35 -08002855static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856 .owner = THIS_MODULE,
2857 .open = rt6_stats_seq_open,
2858 .read = seq_read,
2859 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002860 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861};
2862#endif /* CONFIG_PROC_FS */
2863
2864#ifdef CONFIG_SYSCTL
2865
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002867int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 void __user *buffer, size_t *lenp, loff_t *ppos)
2869{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002870 struct net *net;
2871 int delay;
2872 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002874
2875 net = (struct net *)ctl->extra1;
2876 delay = net->ipv6.sysctl.flush_delay;
2877 proc_dointvec(ctl, write, buffer, lenp, ppos);
2878 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2879 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880}
2881
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002882ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002883 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002885 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002887 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002888 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 },
2890 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002892 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 .maxlen = sizeof(int),
2894 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002895 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 },
2897 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002899 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 .maxlen = sizeof(int),
2901 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002902 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903 },
2904 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002906 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 .maxlen = sizeof(int),
2908 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002909 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910 },
2911 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002913 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 .maxlen = sizeof(int),
2915 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002916 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 },
2918 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002920 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 .maxlen = sizeof(int),
2922 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002923 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924 },
2925 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002927 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 .maxlen = sizeof(int),
2929 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002930 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002931 },
2932 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002934 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935 .maxlen = sizeof(int),
2936 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002937 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938 },
2939 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002941 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942 .maxlen = sizeof(int),
2943 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002944 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 },
2946 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002948 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 .maxlen = sizeof(int),
2950 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002951 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002953 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954};
2955
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002956struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002957{
2958 struct ctl_table *table;
2959
2960 table = kmemdup(ipv6_route_table_template,
2961 sizeof(ipv6_route_table_template),
2962 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002963
2964 if (table) {
2965 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002966 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002967 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002968 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2969 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2970 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2971 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2972 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2973 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2974 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002975 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
Eric W. Biederman464dc802012-11-16 03:02:59 +00002976
2977 /* Don't export sysctls to unprivileged users */
2978 if (net->user_ns != &init_user_ns)
2979 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002980 }
2981
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002982 return table;
2983}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002984#endif
2985
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002986static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002987{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002988 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002989
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002990 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2991 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002992
Eric Dumazetfc66f952010-10-08 06:37:34 +00002993 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2994 goto out_ip6_dst_ops;
2995
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002996 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2997 sizeof(*net->ipv6.ip6_null_entry),
2998 GFP_KERNEL);
2999 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00003000 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07003001 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003002 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07003003 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08003004 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
3005 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003006
3007#ifdef CONFIG_IPV6_MULTIPLE_TABLES
3008 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
3009 sizeof(*net->ipv6.ip6_prohibit_entry),
3010 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07003011 if (!net->ipv6.ip6_prohibit_entry)
3012 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07003013 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003014 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07003015 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08003016 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
3017 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003018
3019 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
3020 sizeof(*net->ipv6.ip6_blk_hole_entry),
3021 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07003022 if (!net->ipv6.ip6_blk_hole_entry)
3023 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07003024 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003025 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07003026 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08003027 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
3028 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003029#endif
3030
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07003031 net->ipv6.sysctl.flush_delay = 0;
3032 net->ipv6.sysctl.ip6_rt_max_size = 4096;
3033 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
3034 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
3035 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
3036 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
3037 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
3038 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
3039
Benjamin Thery6891a342008-03-04 13:49:47 -08003040 net->ipv6.ip6_rt_gc_expire = 30*HZ;
3041
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003042 ret = 0;
3043out:
3044 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003045
Peter Zijlstra68fffc62008-10-07 14:12:10 -07003046#ifdef CONFIG_IPV6_MULTIPLE_TABLES
3047out_ip6_prohibit_entry:
3048 kfree(net->ipv6.ip6_prohibit_entry);
3049out_ip6_null_entry:
3050 kfree(net->ipv6.ip6_null_entry);
3051#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00003052out_ip6_dst_entries:
3053 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003054out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003055 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003056}
3057
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00003058static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003059{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003060 kfree(net->ipv6.ip6_null_entry);
3061#ifdef CONFIG_IPV6_MULTIPLE_TABLES
3062 kfree(net->ipv6.ip6_prohibit_entry);
3063 kfree(net->ipv6.ip6_blk_hole_entry);
3064#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003065 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003066}
3067
Thomas Grafd1896342012-06-18 12:08:33 +00003068static int __net_init ip6_route_net_init_late(struct net *net)
3069{
3070#ifdef CONFIG_PROC_FS
3071 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
3072 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
3073#endif
3074 return 0;
3075}
3076
3077static void __net_exit ip6_route_net_exit_late(struct net *net)
3078{
3079#ifdef CONFIG_PROC_FS
3080 proc_net_remove(net, "ipv6_route");
3081 proc_net_remove(net, "rt6_stats");
3082#endif
3083}
3084
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003085static struct pernet_operations ip6_route_net_ops = {
3086 .init = ip6_route_net_init,
3087 .exit = ip6_route_net_exit,
3088};
3089
David S. Millerc3426b42012-06-09 16:27:05 -07003090static int __net_init ipv6_inetpeer_init(struct net *net)
3091{
3092 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3093
3094 if (!bp)
3095 return -ENOMEM;
3096 inet_peer_base_init(bp);
3097 net->ipv6.peers = bp;
3098 return 0;
3099}
3100
3101static void __net_exit ipv6_inetpeer_exit(struct net *net)
3102{
3103 struct inet_peer_base *bp = net->ipv6.peers;
3104
3105 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07003106 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07003107 kfree(bp);
3108}
3109
David S. Miller2b823f72012-06-09 19:00:16 -07003110static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003111 .init = ipv6_inetpeer_init,
3112 .exit = ipv6_inetpeer_exit,
3113};
3114
Thomas Grafd1896342012-06-18 12:08:33 +00003115static struct pernet_operations ip6_route_net_late_ops = {
3116 .init = ip6_route_net_init_late,
3117 .exit = ip6_route_net_exit_late,
3118};
3119
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003120static struct notifier_block ip6_route_dev_notifier = {
3121 .notifier_call = ip6_route_dev_notify,
3122 .priority = 0,
3123};
3124
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003125int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003127 int ret;
3128
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003129 ret = -ENOMEM;
3130 ip6_dst_ops_template.kmem_cachep =
3131 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3132 SLAB_HWCACHE_ALIGN, NULL);
3133 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003134 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003135
Eric Dumazetfc66f952010-10-08 06:37:34 +00003136 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003137 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003138 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003139
David S. Millerc3426b42012-06-09 16:27:05 -07003140 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3141 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003142 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003143
David S. Miller7e52b332012-06-15 15:51:55 -07003144 ret = register_pernet_subsys(&ip6_route_net_ops);
3145 if (ret)
3146 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003147
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003148 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3149
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003150 /* Registering of the loopback is done before this portion of code,
3151 * the loopback reference in rt6_info will not be taken, do it
3152 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003153 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003154 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3155 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003156 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003157 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003158 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003159 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3160 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003161 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003162 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003163 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003164
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003165 ret = xfrm6_init();
3166 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003167 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003168
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003169 ret = fib6_rules_init();
3170 if (ret)
3171 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003172
Thomas Grafd1896342012-06-18 12:08:33 +00003173 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3174 if (ret)
3175 goto fib6_rules_init;
3176
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003177 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003178 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3179 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3180 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003181 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003182
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003183 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003184 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003185 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003186
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003187out:
3188 return ret;
3189
Thomas Grafd1896342012-06-18 12:08:33 +00003190out_register_late_subsys:
3191 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003192fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003193 fib6_rules_cleanup();
3194xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003195 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003196out_fib6_init:
3197 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003198out_register_subsys:
3199 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003200out_register_inetpeer:
3201 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003202out_dst_entries:
3203 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003204out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003205 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003206 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003207}
3208
3209void ip6_route_cleanup(void)
3210{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003211 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003212 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003213 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003215 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003216 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003217 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003218 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003219 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003220}