blob: 73cf3f78aaa8ba361e47bc7d2ef5baf90e86dab7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020047#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <net/snmp.h>
49#include <net/ipv6.h>
50#include <net/ip6_fib.h>
51#include <net/ip6_route.h>
52#include <net/ndisc.h>
53#include <net/addrconf.h>
54#include <net/tcp.h>
55#include <linux/rtnetlink.h>
56#include <net/dst.h>
57#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070058#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070059#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61#include <asm/uaccess.h>
62
63#ifdef CONFIG_SYSCTL
64#include <linux/sysctl.h>
65#endif
66
Gao feng1716a962012-04-06 00:13:10 +000067static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +000068 const struct in6_addr *dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080070static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000071static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072static struct dst_entry *ip6_negative_advice(struct dst_entry *);
73static void ip6_dst_destroy(struct dst_entry *);
74static void ip6_dst_ifdown(struct dst_entry *,
75 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080076static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78static int ip6_pkt_discard(struct sk_buff *skb);
79static int ip6_pkt_discard_out(struct sk_buff *skb);
80static void ip6_link_failure(struct sk_buff *skb);
81static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
82
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080083#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080084static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000085 const struct in6_addr *prefix, int prefixlen,
86 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +000087 unsigned int pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080088static struct rt6_info *rt6_get_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);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080091#endif
92
David S. Miller06582542011-01-27 14:58:42 -080093static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
94{
95 struct rt6_info *rt = (struct rt6_info *) dst;
96 struct inet_peer *peer;
97 u32 *p = NULL;
98
Yan, Zheng8e2ec632011-09-05 21:34:30 +000099 if (!(rt->dst.flags & DST_HOST))
100 return NULL;
101
David S. Millerfbfe95a2012-06-08 23:24:18 -0700102 peer = rt6_get_peer_create(rt);
David S. Miller06582542011-01-27 14:58:42 -0800103 if (peer) {
104 u32 *old_p = __DST_METRICS_PTR(old);
105 unsigned long prev, new;
106
107 p = peer->metrics;
108 if (inet_metrics_new(peer))
109 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
110
111 new = (unsigned long) p;
112 prev = cmpxchg(&dst->_metrics, old, new);
113
114 if (prev != old) {
115 p = __DST_METRICS_PTR(prev);
116 if (prev & DST_METRICS_READ_ONLY)
117 p = NULL;
118 }
119 }
120 return p;
121}
122
David S. Millerf894cbf2012-07-02 21:52:24 -0700123static inline const void *choose_neigh_daddr(struct rt6_info *rt,
124 struct sk_buff *skb,
125 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500126{
127 struct in6_addr *p = &rt->rt6i_gateway;
128
David S. Millera7563f32012-01-26 16:29:16 -0500129 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500130 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700131 else if (skb)
132 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500133 return daddr;
134}
135
David S. Millerf894cbf2012-07-02 21:52:24 -0700136static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
137 struct sk_buff *skb,
138 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700139{
David S. Miller39232972012-01-26 15:22:32 -0500140 struct rt6_info *rt = (struct rt6_info *) dst;
141 struct neighbour *n;
142
David S. Millerf894cbf2012-07-02 21:52:24 -0700143 daddr = choose_neigh_daddr(rt, skb, daddr);
David S. Miller39232972012-01-26 15:22:32 -0500144 n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500145 if (n)
146 return n;
147 return neigh_create(&nd_tbl, daddr, dst->dev);
148}
149
David S. Miller8ade06c2011-12-29 18:51:57 -0500150static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
David S. Millerf83c7792011-12-28 15:41:23 -0500151{
David S. Miller8ade06c2011-12-29 18:51:57 -0500152 struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
153 if (!n) {
154 n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
155 if (IS_ERR(n))
156 return PTR_ERR(n);
157 }
David S. Miller97cac082012-07-02 22:43:47 -0700158 rt->n = n;
David S. Millerf83c7792011-12-28 15:41:23 -0500159
160 return 0;
David S. Millerd3aaeb32011-07-18 00:40:17 -0700161}
162
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800163static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800165 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 .gc = ip6_dst_gc,
167 .gc_thresh = 1024,
168 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800169 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000170 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800171 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .destroy = ip6_dst_destroy,
173 .ifdown = ip6_dst_ifdown,
174 .negative_advice = ip6_negative_advice,
175 .link_failure = ip6_link_failure,
176 .update_pmtu = ip6_rt_update_pmtu,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700177 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700178 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179};
180
Steffen Klassertebb762f2011-11-23 02:12:51 +0000181static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800182{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000183 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
184
185 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800186}
187
David S. Miller14e50e52007-05-24 18:17:54 -0700188static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
189{
190}
191
Held Bernhard0972ddb2011-04-24 22:07:32 +0000192static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
193 unsigned long old)
194{
195 return NULL;
196}
197
David S. Miller14e50e52007-05-24 18:17:54 -0700198static struct dst_ops ip6_dst_blackhole_ops = {
199 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800200 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700201 .destroy = ip6_dst_destroy,
202 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000203 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800204 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700205 .update_pmtu = ip6_rt_blackhole_update_pmtu,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000206 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700207 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700208};
209
David S. Miller62fa8a82011-01-26 20:51:05 -0800210static const u32 ip6_template_metrics[RTAX_MAX] = {
211 [RTAX_HOPLIMIT - 1] = 255,
212};
213
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800214static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700215 .dst = {
216 .__refcnt = ATOMIC_INIT(1),
217 .__use = 1,
218 .obsolete = -1,
219 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700220 .input = ip6_pkt_discard,
221 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 },
223 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700224 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 .rt6i_metric = ~(u32) 0,
226 .rt6i_ref = ATOMIC_INIT(1),
227};
228
Thomas Graf101367c2006-08-04 03:39:02 -0700229#ifdef CONFIG_IPV6_MULTIPLE_TABLES
230
David S. Miller6723ab52006-10-18 21:20:57 -0700231static int ip6_pkt_prohibit(struct sk_buff *skb);
232static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700233
Adrian Bunk280a34c2008-04-21 02:29:32 -0700234static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700235 .dst = {
236 .__refcnt = ATOMIC_INIT(1),
237 .__use = 1,
238 .obsolete = -1,
239 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700240 .input = ip6_pkt_prohibit,
241 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700242 },
243 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700244 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700245 .rt6i_metric = ~(u32) 0,
246 .rt6i_ref = ATOMIC_INIT(1),
247};
248
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800249static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700250 .dst = {
251 .__refcnt = ATOMIC_INIT(1),
252 .__use = 1,
253 .obsolete = -1,
254 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700255 .input = dst_discard,
256 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700257 },
258 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700259 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700260 .rt6i_metric = ~(u32) 0,
261 .rt6i_ref = ATOMIC_INIT(1),
262};
263
264#endif
265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700267static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700268 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700269 int flags,
270 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
David S. Miller97bab732012-06-09 22:36:36 -0700272 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
273 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700274
David S. Miller97bab732012-06-09 22:36:36 -0700275 if (rt) {
Steffen Klasserta2de86f2012-07-05 03:18:28 +0000276 memset(&rt->n, 0,
David S. Miller38308472011-12-03 18:02:47 -0500277 sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller8b96d222012-06-11 02:01:56 -0700278 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
David S. Miller97bab732012-06-09 22:36:36 -0700279 }
David S. Millercf911662011-04-28 14:31:47 -0700280 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281}
282
283static void ip6_dst_destroy(struct dst_entry *dst)
284{
285 struct rt6_info *rt = (struct rt6_info *)dst;
286 struct inet6_dev *idev = rt->rt6i_idev;
287
David S. Miller97cac082012-07-02 22:43:47 -0700288 if (rt->n)
289 neigh_release(rt->n);
290
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000291 if (!(rt->dst.flags & DST_HOST))
292 dst_destroy_metrics_generic(dst);
293
David S. Miller38308472011-12-03 18:02:47 -0500294 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 rt->rt6i_idev = NULL;
296 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900297 }
Gao feng1716a962012-04-06 00:13:10 +0000298
299 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
300 dst_release(dst->from);
301
David S. Miller97bab732012-06-09 22:36:36 -0700302 if (rt6_has_peer(rt)) {
303 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800304 inet_putpeer(peer);
305 }
306}
307
David S. Miller6431cbc2011-02-07 20:38:06 -0800308static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
309
310static u32 rt6_peer_genid(void)
311{
312 return atomic_read(&__rt6_peer_genid);
313}
314
David S. Millerb3419362010-11-30 12:27:11 -0800315void rt6_bind_peer(struct rt6_info *rt, int create)
316{
David S. Miller97bab732012-06-09 22:36:36 -0700317 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800318 struct inet_peer *peer;
319
David S. Miller97bab732012-06-09 22:36:36 -0700320 base = inetpeer_base_ptr(rt->_rt6i_peer);
321 if (!base)
322 return;
323
324 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700325 if (peer) {
326 if (!rt6_set_peer(rt, peer))
327 inet_putpeer(peer);
328 else
329 rt->rt6i_peer_genid = rt6_peer_genid();
330 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331}
332
333static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
334 int how)
335{
336 struct rt6_info *rt = (struct rt6_info *)dst;
337 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800338 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900339 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
David S. Miller97cac082012-07-02 22:43:47 -0700341 if (dev != loopback_dev) {
342 if (idev && idev->dev == dev) {
343 struct inet6_dev *loopback_idev =
344 in6_dev_get(loopback_dev);
345 if (loopback_idev) {
346 rt->rt6i_idev = loopback_idev;
347 in6_dev_put(idev);
348 }
349 }
350 if (rt->n && rt->n->dev == dev) {
351 rt->n->dev = loopback_dev;
352 dev_hold(loopback_dev);
353 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 }
355 }
356}
357
Eric Dumazeta50feda2012-05-18 18:57:34 +0000358static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359{
Gao feng1716a962012-04-06 00:13:10 +0000360 struct rt6_info *ort = NULL;
361
362 if (rt->rt6i_flags & RTF_EXPIRES) {
363 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000364 return true;
Gao feng1716a962012-04-06 00:13:10 +0000365 } else if (rt->dst.from) {
366 ort = (struct rt6_info *) rt->dst.from;
367 return (ort->rt6i_flags & RTF_EXPIRES) &&
368 time_after(jiffies, ort->dst.expires);
369 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000370 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371}
372
Eric Dumazeta50feda2012-05-18 18:57:34 +0000373static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700374{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000375 return ipv6_addr_type(daddr) &
376 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700377}
378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700380 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 */
382
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800383static inline struct rt6_info *rt6_device_match(struct net *net,
384 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000385 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700387 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388{
389 struct rt6_info *local = NULL;
390 struct rt6_info *sprt;
391
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900392 if (!oif && ipv6_addr_any(saddr))
393 goto out;
394
Changli Gaod8d1f302010-06-10 23:31:35 -0700395 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500396 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900397
398 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 if (dev->ifindex == oif)
400 return sprt;
401 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500402 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700404 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900406 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 local->rt6i_idev->dev->ifindex == oif))
408 continue;
409 }
410 local = sprt;
411 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900412 } else {
413 if (ipv6_chk_addr(net, saddr, dev,
414 flags & RT6_LOOKUP_F_IFACE))
415 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900419 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 if (local)
421 return local;
422
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700423 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800424 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900426out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 return rt;
428}
429
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800430#ifdef CONFIG_IPV6_ROUTER_PREF
431static void rt6_probe(struct rt6_info *rt)
432{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000433 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800434 /*
435 * Okay, this does not seem to be appropriate
436 * for now, however, we need to check if it
437 * is really so; aka Router Reachability Probing.
438 *
439 * Router Reachability Probe MUST be rate-limited
440 * to no more than one per minute.
441 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000442 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700443 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800444 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000445 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800446 read_lock_bh(&neigh->lock);
447 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800448 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800449 struct in6_addr mcaddr;
450 struct in6_addr *target;
451
452 neigh->updated = jiffies;
453 read_unlock_bh(&neigh->lock);
454
455 target = (struct in6_addr *)&neigh->primary_key;
456 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500457 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000458 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800459 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000460 }
461out:
462 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800463}
464#else
465static inline void rt6_probe(struct rt6_info *rt)
466{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800467}
468#endif
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800471 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700473static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
David S. Millerd1918542011-12-28 20:19:20 -0500475 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700476 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800477 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700478 if ((dev->flags & IFF_LOOPBACK) &&
479 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
480 return 1;
481 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482}
483
Dave Jonesb6f99a22007-03-22 12:27:49 -0700484static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000486 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800487 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000488
489 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700490 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700491 if (rt->rt6i_flags & RTF_NONEXTHOP ||
492 !(rt->rt6i_flags & RTF_GATEWAY))
493 m = 1;
494 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800495 read_lock_bh(&neigh->lock);
496 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700497 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800498#ifdef CONFIG_IPV6_ROUTER_PREF
499 else if (neigh->nud_state & NUD_FAILED)
500 m = 0;
501#endif
502 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800503 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800504 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800505 } else
506 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000507 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800508 return m;
509}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800511static int rt6_score_route(struct rt6_info *rt, int oif,
512 int strict)
513{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700514 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900515
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700516 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700517 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800518 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800519#ifdef CONFIG_IPV6_ROUTER_PREF
520 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
521#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700522 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800523 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800524 return -1;
525 return m;
526}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
David S. Millerf11e6652007-03-24 20:36:25 -0700528static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
529 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800530{
David S. Millerf11e6652007-03-24 20:36:25 -0700531 int m;
532
533 if (rt6_check_expired(rt))
534 goto out;
535
536 m = rt6_score_route(rt, oif, strict);
537 if (m < 0)
538 goto out;
539
540 if (m > *mpri) {
541 if (strict & RT6_LOOKUP_F_REACHABLE)
542 rt6_probe(match);
543 *mpri = m;
544 match = rt;
545 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
546 rt6_probe(rt);
547 }
548
549out:
550 return match;
551}
552
553static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
554 struct rt6_info *rr_head,
555 u32 metric, int oif, int strict)
556{
557 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800558 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
David S. Millerf11e6652007-03-24 20:36:25 -0700560 match = NULL;
561 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700562 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700563 match = find_match(rt, oif, strict, &mpri, match);
564 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700565 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700566 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800567
David S. Millerf11e6652007-03-24 20:36:25 -0700568 return match;
569}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800570
David S. Millerf11e6652007-03-24 20:36:25 -0700571static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
572{
573 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800574 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
David S. Millerf11e6652007-03-24 20:36:25 -0700576 rt0 = fn->rr_ptr;
577 if (!rt0)
578 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
David S. Millerf11e6652007-03-24 20:36:25 -0700580 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800582 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700583 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700584 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700585
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800586 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700587 if (!next || next->rt6i_metric != rt0->rt6i_metric)
588 next = fn->leaf;
589
590 if (next != rt0)
591 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 }
593
David S. Millerd1918542011-12-28 20:19:20 -0500594 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000595 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596}
597
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800598#ifdef CONFIG_IPV6_ROUTE_INFO
599int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000600 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800601{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900602 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800603 struct route_info *rinfo = (struct route_info *) opt;
604 struct in6_addr prefix_buf, *prefix;
605 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900606 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800607 struct rt6_info *rt;
608
609 if (len < sizeof(struct route_info)) {
610 return -EINVAL;
611 }
612
613 /* Sanity check for prefix_len and length */
614 if (rinfo->length > 3) {
615 return -EINVAL;
616 } else if (rinfo->prefix_len > 128) {
617 return -EINVAL;
618 } else if (rinfo->prefix_len > 64) {
619 if (rinfo->length < 2) {
620 return -EINVAL;
621 }
622 } else if (rinfo->prefix_len > 0) {
623 if (rinfo->length < 1) {
624 return -EINVAL;
625 }
626 }
627
628 pref = rinfo->route_pref;
629 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000630 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800631
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900632 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800633
634 if (rinfo->length == 3)
635 prefix = (struct in6_addr *)rinfo->prefix;
636 else {
637 /* this function is safe */
638 ipv6_addr_prefix(&prefix_buf,
639 (struct in6_addr *)rinfo->prefix,
640 rinfo->prefix_len);
641 prefix = &prefix_buf;
642 }
643
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800644 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
645 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800646
647 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700648 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800649 rt = NULL;
650 }
651
652 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800653 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800654 pref);
655 else if (rt)
656 rt->rt6i_flags = RTF_ROUTEINFO |
657 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
658
659 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000660 if (!addrconf_finite_timeout(lifetime))
661 rt6_clean_expires(rt);
662 else
663 rt6_set_expires(rt, jiffies + HZ * lifetime);
664
Changli Gaod8d1f302010-06-10 23:31:35 -0700665 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800666 }
667 return 0;
668}
669#endif
670
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800671#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700672do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800673 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700674 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700675 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700676 if (fn->fn_flags & RTN_TL_ROOT) \
677 goto out; \
678 pn = fn->parent; \
679 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800680 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700681 else \
682 fn = pn; \
683 if (fn->fn_flags & RTN_RTINFO) \
684 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700685 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700686 } \
David S. Miller38308472011-12-03 18:02:47 -0500687} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700688
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800689static struct rt6_info *ip6_pol_route_lookup(struct net *net,
690 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500691 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
693 struct fib6_node *fn;
694 struct rt6_info *rt;
695
Thomas Grafc71099a2006-08-04 23:20:06 -0700696 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500697 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700698restart:
699 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500700 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
701 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700702out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700703 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700704 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700705 return rt;
706
707}
708
Florian Westphalea6e5742011-09-05 16:05:44 +0200709struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
710 int flags)
711{
712 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
713}
714EXPORT_SYMBOL_GPL(ip6_route_lookup);
715
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900716struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
717 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700718{
David S. Miller4c9483b2011-03-12 16:22:43 -0500719 struct flowi6 fl6 = {
720 .flowi6_oif = oif,
721 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700722 };
723 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700724 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700725
Thomas Grafadaa70b2006-10-13 15:01:03 -0700726 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500727 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700728 flags |= RT6_LOOKUP_F_HAS_SADDR;
729 }
730
David S. Miller4c9483b2011-03-12 16:22:43 -0500731 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700732 if (dst->error == 0)
733 return (struct rt6_info *) dst;
734
735 dst_release(dst);
736
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 return NULL;
738}
739
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900740EXPORT_SYMBOL(rt6_lookup);
741
Thomas Grafc71099a2006-08-04 23:20:06 -0700742/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 It takes new route entry, the addition fails by any reason the
744 route is freed. In any case, if caller does not hold it, it may
745 be destroyed.
746 */
747
Thomas Graf86872cb2006-08-22 00:01:08 -0700748static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749{
750 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700751 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Thomas Grafc71099a2006-08-04 23:20:06 -0700753 table = rt->rt6i_table;
754 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700755 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700756 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
758 return err;
759}
760
Thomas Graf40e22e82006-08-22 00:00:45 -0700761int ip6_ins_rt(struct rt6_info *rt)
762{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800763 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500764 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800765 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800766 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700767}
768
Gao feng1716a962012-04-06 00:13:10 +0000769static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000770 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000771 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 struct rt6_info *rt;
774
775 /*
776 * Clone the route.
777 */
778
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000779 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
781 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800782 int attempts = !in_softirq();
783
David S. Miller38308472011-12-03 18:02:47 -0500784 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500785 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000786 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900787 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000788 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900789 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793#ifdef CONFIG_IPV6_SUBTREES
794 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000795 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 rt->rt6i_src.plen = 128;
797 }
798#endif
799
David S. Miller14deae42009-01-04 16:04:39 -0800800 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500801 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500802 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800803 int saved_rt_min_interval =
804 net->ipv6.sysctl.ip6_rt_gc_min_interval;
805 int saved_rt_elasticity =
806 net->ipv6.sysctl.ip6_rt_gc_elasticity;
807
808 if (attempts-- > 0) {
809 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
810 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
811
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000812 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800813
814 net->ipv6.sysctl.ip6_rt_gc_elasticity =
815 saved_rt_elasticity;
816 net->ipv6.sysctl.ip6_rt_gc_min_interval =
817 saved_rt_min_interval;
818 goto retry;
819 }
820
Joe Perchesf3213832012-05-15 14:11:53 +0000821 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700822 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800823 return NULL;
824 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800825 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800827 return rt;
828}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000830static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
831 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800832{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000833 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
834
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800835 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800836 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700837 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800838 }
839 return rt;
840}
841
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800842static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500843 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844{
845 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800846 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700847 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800849 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700850 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700852 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
854relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700855 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800857restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500858 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
860restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700861 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800862
David S. Miller4c9483b2011-03-12 16:22:43 -0500863 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800864 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800865 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800866 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Changli Gaod8d1f302010-06-10 23:31:35 -0700868 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700869 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800870
David S. Miller97cac082012-07-02 22:43:47 -0700871 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500872 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800873 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500874 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800875 else
876 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800877
Changli Gaod8d1f302010-06-10 23:31:35 -0700878 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800879 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800880
Changli Gaod8d1f302010-06-10 23:31:35 -0700881 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800882 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700883 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800884 if (!err)
885 goto out2;
886 }
887
888 if (--attempts <= 0)
889 goto out2;
890
891 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700892 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800893 * released someone could insert this route. Relookup.
894 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700895 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800896 goto relookup;
897
898out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800899 if (reachable) {
900 reachable = 0;
901 goto restart_2;
902 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700903 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700904 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700906 rt->dst.lastuse = jiffies;
907 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700908
909 return rt;
910}
911
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800912static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500913 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700914{
David S. Miller4c9483b2011-03-12 16:22:43 -0500915 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700916}
917
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000918static struct dst_entry *ip6_route_input_lookup(struct net *net,
919 struct net_device *dev,
920 struct flowi6 *fl6, int flags)
921{
922 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
923 flags |= RT6_LOOKUP_F_IFACE;
924
925 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
926}
927
Thomas Grafc71099a2006-08-04 23:20:06 -0700928void ip6_route_input(struct sk_buff *skb)
929{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000930 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900931 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700932 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500933 struct flowi6 fl6 = {
934 .flowi6_iif = skb->dev->ifindex,
935 .daddr = iph->daddr,
936 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500937 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500938 .flowi6_mark = skb->mark,
939 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700940 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700941
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000942 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700943}
944
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800945static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500946 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700947{
David S. Miller4c9483b2011-03-12 16:22:43 -0500948 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700949}
950
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700951struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500952 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700953{
954 int flags = 0;
955
David McCullough4dc27d1c2012-06-25 15:42:26 +0000956 fl6->flowi6_iif = net->loopback_dev->ifindex;
957
David S. Miller4c9483b2011-03-12 16:22:43 -0500958 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700959 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700960
David S. Miller4c9483b2011-03-12 16:22:43 -0500961 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700962 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000963 else if (sk)
964 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700965
David S. Miller4c9483b2011-03-12 16:22:43 -0500966 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967}
968
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900969EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
David S. Miller2774c132011-03-01 14:59:04 -0800971struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700972{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700973 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700974 struct dst_entry *new = NULL;
975
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700976 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700977 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700978 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller97bab732012-06-09 22:36:36 -0700979 rt6_init_peer(rt, net->ipv6.peers);
David S. Millercf911662011-04-28 14:31:47 -0700980
Changli Gaod8d1f302010-06-10 23:31:35 -0700981 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700982
David S. Miller14e50e52007-05-24 18:17:54 -0700983 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800984 new->input = dst_discard;
985 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700986
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000987 if (dst_metrics_read_only(&ort->dst))
988 new->_metrics = ort->dst._metrics;
989 else
990 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700991 rt->rt6i_idev = ort->rt6i_idev;
992 if (rt->rt6i_idev)
993 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -0700994
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000995 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +0000996 rt->rt6i_flags = ort->rt6i_flags;
997 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -0700998 rt->rt6i_metric = 0;
999
1000 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1001#ifdef CONFIG_IPV6_SUBTREES
1002 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1003#endif
1004
1005 dst_free(new);
1006 }
1007
David S. Miller69ead7a2011-03-01 14:45:33 -08001008 dst_release(dst_orig);
1009 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001010}
David S. Miller14e50e52007-05-24 18:17:54 -07001011
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012/*
1013 * Destination cache support functions
1014 */
1015
1016static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1017{
1018 struct rt6_info *rt;
1019
1020 rt = (struct rt6_info *) dst;
1021
David S. Miller6431cbc2011-02-07 20:38:06 -08001022 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1023 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001024 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001025 rt6_bind_peer(rt, 0);
1026 rt->rt6i_peer_genid = rt6_peer_genid();
1027 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001029 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 return NULL;
1031}
1032
1033static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1034{
1035 struct rt6_info *rt = (struct rt6_info *) dst;
1036
1037 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001038 if (rt->rt6i_flags & RTF_CACHE) {
1039 if (rt6_check_expired(rt)) {
1040 ip6_del_rt(rt);
1041 dst = NULL;
1042 }
1043 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001045 dst = NULL;
1046 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001048 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049}
1050
1051static void ip6_link_failure(struct sk_buff *skb)
1052{
1053 struct rt6_info *rt;
1054
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001055 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
Eric Dumazetadf30902009-06-02 05:19:30 +00001057 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001059 if (rt->rt6i_flags & RTF_CACHE)
1060 rt6_update_expires(rt, 0);
1061 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 rt->rt6i_node->fn_sernum = -1;
1063 }
1064}
1065
1066static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1067{
1068 struct rt6_info *rt6 = (struct rt6_info*)dst;
1069
David S. Miller81aded22012-06-15 14:54:11 -07001070 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001072 struct net *net = dev_net(dst->dev);
1073
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 rt6->rt6i_flags |= RTF_MODIFIED;
1075 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001076 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001078 features |= RTAX_FEATURE_ALLFRAG;
1079 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 }
David S. Millerdefb3512010-12-08 21:16:57 -08001081 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001082 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 }
1084}
1085
David S. Miller42ae66c2012-06-15 20:01:57 -07001086void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1087 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001088{
1089 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1090 struct dst_entry *dst;
1091 struct flowi6 fl6;
1092
1093 memset(&fl6, 0, sizeof(fl6));
1094 fl6.flowi6_oif = oif;
1095 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001096 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001097 fl6.daddr = iph->daddr;
1098 fl6.saddr = iph->saddr;
1099 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1100
1101 dst = ip6_route_output(net, NULL, &fl6);
1102 if (!dst->error)
1103 ip6_rt_update_pmtu(dst, ntohl(mtu));
1104 dst_release(dst);
1105}
1106EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1107
1108void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1109{
1110 ip6_update_pmtu(skb, sock_net(sk), mtu,
1111 sk->sk_bound_dev_if, sk->sk_mark);
1112}
1113EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1114
David S. Miller0dbaee32010-12-13 12:52:14 -08001115static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116{
David S. Miller0dbaee32010-12-13 12:52:14 -08001117 struct net_device *dev = dst->dev;
1118 unsigned int mtu = dst_mtu(dst);
1119 struct net *net = dev_net(dev);
1120
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1122
Daniel Lezcano55786892008-03-04 13:47:47 -08001123 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1124 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
1126 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001127 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1128 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1129 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 * rely only on pmtu discovery"
1131 */
1132 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1133 mtu = IPV6_MAXPLEN;
1134 return mtu;
1135}
1136
Steffen Klassertebb762f2011-11-23 02:12:51 +00001137static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001138{
David S. Millerd33e4552010-12-14 13:01:14 -08001139 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001140 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1141
1142 if (mtu)
1143 return mtu;
1144
1145 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001146
1147 rcu_read_lock();
1148 idev = __in6_dev_get(dst->dev);
1149 if (idev)
1150 mtu = idev->cnf.mtu6;
1151 rcu_read_unlock();
1152
1153 return mtu;
1154}
1155
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001156static struct dst_entry *icmp6_dst_gc_list;
1157static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001158
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001159struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001161 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162{
David S. Miller87a11572011-12-06 17:04:13 -05001163 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 struct rt6_info *rt;
1165 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001166 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167
David S. Miller38308472011-12-03 18:02:47 -05001168 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001169 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170
David S. Miller8b96d222012-06-11 02:01:56 -07001171 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001172 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001174 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 goto out;
1176 }
1177
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 if (neigh)
1179 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001180 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001181 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001182 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001183 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001184 dst_free(&rt->dst);
1185 return ERR_CAST(neigh);
1186 }
David S. Miller14deae42009-01-04 16:04:39 -08001187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001189 rt->dst.flags |= DST_HOST;
1190 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001191 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001192 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001193 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001194 rt->rt6i_dst.plen = 128;
1195 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001196 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001198 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001199 rt->dst.next = icmp6_dst_gc_list;
1200 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001201 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
Daniel Lezcano55786892008-03-04 13:47:47 -08001203 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
David S. Miller87a11572011-12-06 17:04:13 -05001205 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1206
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207out:
David S. Miller87a11572011-12-06 17:04:13 -05001208 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209}
1210
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001211int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001213 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001214 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001216 spin_lock_bh(&icmp6_dst_lock);
1217 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001218
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 while ((dst = *pprev) != NULL) {
1220 if (!atomic_read(&dst->__refcnt)) {
1221 *pprev = dst->next;
1222 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 } else {
1224 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001225 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 }
1227 }
1228
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001229 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001230
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001231 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232}
1233
David S. Miller1e493d12008-09-10 17:27:15 -07001234static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1235 void *arg)
1236{
1237 struct dst_entry *dst, **pprev;
1238
1239 spin_lock_bh(&icmp6_dst_lock);
1240 pprev = &icmp6_dst_gc_list;
1241 while ((dst = *pprev) != NULL) {
1242 struct rt6_info *rt = (struct rt6_info *) dst;
1243 if (func(rt, arg)) {
1244 *pprev = dst->next;
1245 dst_free(dst);
1246 } else {
1247 pprev = &dst->next;
1248 }
1249 }
1250 spin_unlock_bh(&icmp6_dst_lock);
1251}
1252
Daniel Lezcano569d3642008-01-18 03:56:57 -08001253static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001256 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001257 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1258 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1259 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1260 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1261 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001262 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
Eric Dumazetfc66f952010-10-08 06:37:34 +00001264 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001265 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001266 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 goto out;
1268
Benjamin Thery6891a342008-03-04 13:49:47 -08001269 net->ipv6.ip6_rt_gc_expire++;
1270 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1271 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001272 entries = dst_entries_get_slow(ops);
1273 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001274 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001276 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001277 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278}
1279
1280/* Clean host part of a prefix. Not necessary in radix tree,
1281 but results in cleaner routing tables.
1282
1283 Remove it only when all the things will work!
1284 */
1285
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001286int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287{
David S. Miller5170ae82010-12-12 21:35:57 -08001288 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001289 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001290 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001291 struct inet6_dev *idev;
1292
1293 rcu_read_lock();
1294 idev = __in6_dev_get(dev);
1295 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001296 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001297 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001298 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001299 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 }
1301 return hoplimit;
1302}
David S. Millerabbf46a2010-12-12 21:14:46 -08001303EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
1305/*
1306 *
1307 */
1308
Thomas Graf86872cb2006-08-22 00:01:08 -07001309int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310{
1311 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001312 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 struct rt6_info *rt = NULL;
1314 struct net_device *dev = NULL;
1315 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001316 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 int addr_type;
1318
Thomas Graf86872cb2006-08-22 00:01:08 -07001319 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 return -EINVAL;
1321#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001322 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 return -EINVAL;
1324#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001325 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001327 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 if (!dev)
1329 goto out;
1330 idev = in6_dev_get(dev);
1331 if (!idev)
1332 goto out;
1333 }
1334
Thomas Graf86872cb2006-08-22 00:01:08 -07001335 if (cfg->fc_metric == 0)
1336 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
Matti Vaittinend71314b2011-11-14 00:14:49 +00001338 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001339 if (cfg->fc_nlinfo.nlh &&
1340 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001341 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001342 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001343 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001344 table = fib6_new_table(net, cfg->fc_table);
1345 }
1346 } else {
1347 table = fib6_new_table(net, cfg->fc_table);
1348 }
David S. Miller38308472011-12-03 18:02:47 -05001349
1350 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001351 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001352
David S. Miller8b96d222012-06-11 02:01:56 -07001353 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
David S. Miller38308472011-12-03 18:02:47 -05001355 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 err = -ENOMEM;
1357 goto out;
1358 }
1359
Changli Gaod8d1f302010-06-10 23:31:35 -07001360 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001361
1362 if (cfg->fc_flags & RTF_EXPIRES)
1363 rt6_set_expires(rt, jiffies +
1364 clock_t_to_jiffies(cfg->fc_expires));
1365 else
1366 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Thomas Graf86872cb2006-08-22 00:01:08 -07001368 if (cfg->fc_protocol == RTPROT_UNSPEC)
1369 cfg->fc_protocol = RTPROT_BOOT;
1370 rt->rt6i_protocol = cfg->fc_protocol;
1371
1372 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001375 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001376 else if (cfg->fc_flags & RTF_LOCAL)
1377 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001379 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Changli Gaod8d1f302010-06-10 23:31:35 -07001381 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Thomas Graf86872cb2006-08-22 00:01:08 -07001383 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1384 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001386 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001388 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1389 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1390 if (!metrics) {
1391 err = -ENOMEM;
1392 goto out;
1393 }
1394 dst_init_metrics(&rt->dst, metrics, 0);
1395 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001397 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1398 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399#endif
1400
Thomas Graf86872cb2006-08-22 00:01:08 -07001401 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 /* We cannot add true routes via loopback here,
1404 they would result in kernel looping; promote them to reject routes
1405 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001406 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001407 (dev && (dev->flags & IFF_LOOPBACK) &&
1408 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1409 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001411 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 if (dev) {
1413 dev_put(dev);
1414 in6_dev_put(idev);
1415 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001416 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 dev_hold(dev);
1418 idev = in6_dev_get(dev);
1419 if (!idev) {
1420 err = -ENODEV;
1421 goto out;
1422 }
1423 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001424 rt->dst.output = ip6_pkt_discard_out;
1425 rt->dst.input = ip6_pkt_discard;
1426 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1428 goto install_route;
1429 }
1430
Thomas Graf86872cb2006-08-22 00:01:08 -07001431 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001432 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 int gwa_type;
1434
Thomas Graf86872cb2006-08-22 00:01:08 -07001435 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001436 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 gwa_type = ipv6_addr_type(gw_addr);
1438
1439 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1440 struct rt6_info *grt;
1441
1442 /* IPv6 strictly inhibits using not link-local
1443 addresses as nexthop address.
1444 Otherwise, router will not able to send redirects.
1445 It is very good, but in some (rare!) circumstances
1446 (SIT, PtP, NBMA NOARP links) it is handy to allow
1447 some exceptions. --ANK
1448 */
1449 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001450 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 goto out;
1452
Daniel Lezcano55786892008-03-04 13:47:47 -08001453 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
1455 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001456 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 goto out;
1458 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001459 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001460 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 goto out;
1462 }
1463 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001464 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 idev = grt->rt6i_idev;
1466 dev_hold(dev);
1467 in6_dev_hold(grt->rt6i_idev);
1468 }
David S. Miller38308472011-12-03 18:02:47 -05001469 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001471 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472
1473 if (err)
1474 goto out;
1475 }
1476 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001477 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 goto out;
1479 }
1480
1481 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001482 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 goto out;
1484
Daniel Walterc3968a82011-04-13 21:10:57 +00001485 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1486 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1487 err = -EINVAL;
1488 goto out;
1489 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001490 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001491 rt->rt6i_prefsrc.plen = 128;
1492 } else
1493 rt->rt6i_prefsrc.plen = 0;
1494
Thomas Graf86872cb2006-08-22 00:01:08 -07001495 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001496 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001497 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 }
1500
Thomas Graf86872cb2006-08-22 00:01:08 -07001501 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001504 if (cfg->fc_mx) {
1505 struct nlattr *nla;
1506 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507
Thomas Graf86872cb2006-08-22 00:01:08 -07001508 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001509 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001510
1511 if (type) {
1512 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 err = -EINVAL;
1514 goto out;
1515 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001516
David S. Millerdefb3512010-12-08 21:16:57 -08001517 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 }
1520 }
1521
Changli Gaod8d1f302010-06-10 23:31:35 -07001522 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001524 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001525
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001526 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001527
Thomas Graf86872cb2006-08-22 00:01:08 -07001528 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
1530out:
1531 if (dev)
1532 dev_put(dev);
1533 if (idev)
1534 in6_dev_put(idev);
1535 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001536 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 return err;
1538}
1539
Thomas Graf86872cb2006-08-22 00:01:08 -07001540static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541{
1542 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001543 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001544 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001546 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001547 return -ENOENT;
1548
Thomas Grafc71099a2006-08-04 23:20:06 -07001549 table = rt->rt6i_table;
1550 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
Thomas Graf86872cb2006-08-22 00:01:08 -07001552 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001553 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Thomas Grafc71099a2006-08-04 23:20:06 -07001555 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
1557 return err;
1558}
1559
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001560int ip6_del_rt(struct rt6_info *rt)
1561{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001562 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001563 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001564 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001565 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001566}
1567
Thomas Graf86872cb2006-08-22 00:01:08 -07001568static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569{
Thomas Grafc71099a2006-08-04 23:20:06 -07001570 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 struct fib6_node *fn;
1572 struct rt6_info *rt;
1573 int err = -ESRCH;
1574
Daniel Lezcano55786892008-03-04 13:47:47 -08001575 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001576 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001577 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578
Thomas Grafc71099a2006-08-04 23:20:06 -07001579 read_lock_bh(&table->tb6_lock);
1580
1581 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001582 &cfg->fc_dst, cfg->fc_dst_len,
1583 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001584
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001586 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001587 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001588 (!rt->dst.dev ||
1589 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001591 if (cfg->fc_flags & RTF_GATEWAY &&
1592 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001594 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001596 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001597 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
Thomas Graf86872cb2006-08-22 00:01:08 -07001599 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 }
1601 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001602 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603
1604 return err;
1605}
1606
1607/*
1608 * Handle redirects
1609 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001610struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001611 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001612 struct in6_addr gateway;
1613};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001615static struct rt6_info *__ip6_route_redirect(struct net *net,
1616 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001617 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001618 int flags)
1619{
David S. Miller4c9483b2011-03-12 16:22:43 -05001620 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001621 struct rt6_info *rt;
1622 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001623
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001625 * Get the "current" route for this destination and
1626 * check if the redirect has come from approriate router.
1627 *
1628 * RFC 2461 specifies that redirects should only be
1629 * accepted if they come from the nexthop to the target.
1630 * Due to the way the routes are chosen, this notion
1631 * is a bit fuzzy and one might need to check all possible
1632 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634
Thomas Grafc71099a2006-08-04 23:20:06 -07001635 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001636 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001637restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001638 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001639 /*
1640 * Current route is on-link; redirect is always invalid.
1641 *
1642 * Seems, previous statement is not true. It could
1643 * be node, which looks for us as on-link (f.e. proxy ndisc)
1644 * But then router serving it might decide, that we should
1645 * know truth 8)8) --ANK (980726).
1646 */
1647 if (rt6_check_expired(rt))
1648 continue;
1649 if (!(rt->rt6i_flags & RTF_GATEWAY))
1650 continue;
David S. Millerd1918542011-12-28 20:19:20 -05001651 if (fl6->flowi6_oif != rt->dst.dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001652 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001653 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001654 continue;
1655 break;
1656 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001657
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001658 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001659 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001660 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001661out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001662 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001663
1664 read_unlock_bh(&table->tb6_lock);
1665
1666 return rt;
1667};
1668
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001669static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1670 const struct in6_addr *src,
1671 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001672 struct net_device *dev)
1673{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001674 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001675 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001676 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001677 .fl6 = {
1678 .flowi6_oif = dev->ifindex,
1679 .daddr = *dest,
1680 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001681 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001682 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001683
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001684 rdfl.gateway = *gateway;
Brian Haley86c36ce2009-10-07 13:58:01 -07001685
Thomas Grafadaa70b2006-10-13 15:01:03 -07001686 if (rt6_need_strict(dest))
1687 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001688
David S. Miller4c9483b2011-03-12 16:22:43 -05001689 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001690 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001691}
1692
David S. Millere8599ff2012-07-11 23:43:53 -07001693void rt6_redirect(struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001694{
David S. Millere8599ff2012-07-11 23:43:53 -07001695 struct net *net = dev_net(skb->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001696 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07001697 struct rt6_info *rt, *nrt = NULL;
1698 const struct in6_addr *target;
David S. Miller1d248b12012-07-03 01:01:51 -07001699 struct neighbour *old_neigh;
David S. Millere8599ff2012-07-11 23:43:53 -07001700 const struct in6_addr *dest;
1701 const struct in6_addr *src;
1702 const struct in6_addr *saddr;
1703 struct ndisc_options ndopts;
1704 struct inet6_dev *in6_dev;
1705 struct neighbour *neigh;
1706 struct icmp6hdr *icmph;
1707 int on_link, optlen;
1708 u8 *lladdr = NULL;
1709
1710 optlen = skb->tail - skb->transport_header;
1711 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1712
1713 if (optlen < 0) {
1714 net_dbg_ratelimited("rt6_redirect: packet too short\n");
1715 return;
1716 }
1717
1718 icmph = icmp6_hdr(skb);
1719 target = (const struct in6_addr *) (icmph + 1);
1720 dest = target + 1;
1721
1722 if (ipv6_addr_is_multicast(dest)) {
1723 net_dbg_ratelimited("rt6_redirect: destination address is multicast\n");
1724 return;
1725 }
1726
1727 if (ipv6_addr_equal(dest, target)) {
1728 on_link = 1;
1729 } else if (ipv6_addr_type(target) !=
1730 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
1731 net_dbg_ratelimited("rt6_redirect: target address is not link-local unicast\n");
1732 return;
1733 }
1734
1735 in6_dev = __in6_dev_get(skb->dev);
1736 if (!in6_dev)
1737 return;
1738 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
1739 return;
1740
1741 /* RFC2461 8.1:
1742 * The IP source address of the Redirect MUST be the same as the current
1743 * first-hop router for the specified ICMP Destination Address.
1744 */
1745
1746 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1747 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
1748 return;
1749 }
1750 if (ndopts.nd_opts_tgt_lladdr) {
1751 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1752 skb->dev);
1753 if (!lladdr) {
1754 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
1755 return;
1756 }
1757 }
1758
1759 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1760 if (!neigh)
1761 return;
1762
1763 src = &ipv6_hdr(skb)->daddr;
1764 saddr = &ipv6_hdr(skb)->saddr;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001765
1766 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1767
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001768 if (rt == net->ipv6.ip6_null_entry) {
Joe Perchese87cc472012-05-13 21:56:26 +00001769 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001770 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 }
1772
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 /*
1774 * We have finally decided to accept it.
1775 */
1776
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001777 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1779 NEIGH_UPDATE_F_OVERRIDE|
1780 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1781 NEIGH_UPDATE_F_ISROUTER))
1782 );
1783
1784 /*
1785 * Redirect received -> path was valid.
1786 * Look, redirects are sent only in response to data packets,
1787 * so that this nexthop apparently is reachable. --ANK
1788 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001789 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
1791 /* Duplicate redirect: silently ignore. */
David S. Miller97cac082012-07-02 22:43:47 -07001792 old_neigh = rt->n;
David S. Miller1d248b12012-07-03 01:01:51 -07001793 if (neigh == old_neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 goto out;
1795
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001796 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001797 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 goto out;
1799
1800 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1801 if (on_link)
1802 nrt->rt6i_flags &= ~RTF_GATEWAY;
1803
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001804 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001805 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
Thomas Graf40e22e82006-08-22 00:00:45 -07001807 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808 goto out;
1809
Changli Gaod8d1f302010-06-10 23:31:35 -07001810 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001811 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001812 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001813 netevent.new_neigh = neigh;
1814 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001815 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1816
David S. Miller38308472011-12-03 18:02:47 -05001817 if (rt->rt6i_flags & RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001818 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 return;
1820 }
1821
1822out:
David S. Millere8599ff2012-07-11 23:43:53 -07001823 neigh_release(neigh);
Changli Gaod8d1f302010-06-10 23:31:35 -07001824 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825}
1826
1827/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828 * Misc support functions
1829 */
1830
Gao feng1716a962012-04-06 00:13:10 +00001831static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001832 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833{
David S. Millerd1918542011-12-28 20:19:20 -05001834 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001835 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1836 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
1838 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001839 rt->dst.input = ort->dst.input;
1840 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001841 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001843 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001844 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001845 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001846 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 rt->rt6i_idev = ort->rt6i_idev;
1848 if (rt->rt6i_idev)
1849 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001850 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001852 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001853 rt->rt6i_flags = ort->rt6i_flags;
1854 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1855 (RTF_DEFAULT | RTF_ADDRCONF))
1856 rt6_set_from(rt, ort);
1857 else
1858 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 rt->rt6i_metric = 0;
1860
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861#ifdef CONFIG_IPV6_SUBTREES
1862 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1863#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001864 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001865 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 }
1867 return rt;
1868}
1869
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001870#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001871static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001872 const struct in6_addr *prefix, int prefixlen,
1873 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001874{
1875 struct fib6_node *fn;
1876 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001877 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001878
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001879 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001880 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001881 return NULL;
1882
1883 write_lock_bh(&table->tb6_lock);
1884 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001885 if (!fn)
1886 goto out;
1887
Changli Gaod8d1f302010-06-10 23:31:35 -07001888 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001889 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001890 continue;
1891 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1892 continue;
1893 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1894 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001895 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001896 break;
1897 }
1898out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001899 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001900 return rt;
1901}
1902
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001903static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001904 const struct in6_addr *prefix, int prefixlen,
1905 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001906 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001907{
Thomas Graf86872cb2006-08-22 00:01:08 -07001908 struct fib6_config cfg = {
1909 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001910 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001911 .fc_ifindex = ifindex,
1912 .fc_dst_len = prefixlen,
1913 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1914 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001915 .fc_nlinfo.pid = 0,
1916 .fc_nlinfo.nlh = NULL,
1917 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001918 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001919
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001920 cfg.fc_dst = *prefix;
1921 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001922
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001923 /* We should treat it as a default route if prefix length is 0. */
1924 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001925 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001926
Thomas Graf86872cb2006-08-22 00:01:08 -07001927 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001928
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001929 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001930}
1931#endif
1932
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001933struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001934{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001936 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001938 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001939 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001940 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941
Thomas Grafc71099a2006-08-04 23:20:06 -07001942 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001943 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001944 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001945 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1947 break;
1948 }
1949 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001950 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001951 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 return rt;
1953}
1954
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001955struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001956 struct net_device *dev,
1957 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958{
Thomas Graf86872cb2006-08-22 00:01:08 -07001959 struct fib6_config cfg = {
1960 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001961 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001962 .fc_ifindex = dev->ifindex,
1963 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1964 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001965 .fc_nlinfo.pid = 0,
1966 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001967 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001968 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001970 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971
Thomas Graf86872cb2006-08-22 00:01:08 -07001972 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 return rt6_get_dflt_router(gwaddr, dev);
1975}
1976
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001977void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978{
1979 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001980 struct fib6_table *table;
1981
1982 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001983 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001984 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001985 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986
1987restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001988 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001989 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001991 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001992 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001993 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 goto restart;
1995 }
1996 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001997 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998}
1999
Daniel Lezcano55786892008-03-04 13:47:47 -08002000static void rtmsg_to_fib6_config(struct net *net,
2001 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07002002 struct fib6_config *cfg)
2003{
2004 memset(cfg, 0, sizeof(*cfg));
2005
2006 cfg->fc_table = RT6_TABLE_MAIN;
2007 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
2008 cfg->fc_metric = rtmsg->rtmsg_metric;
2009 cfg->fc_expires = rtmsg->rtmsg_info;
2010 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
2011 cfg->fc_src_len = rtmsg->rtmsg_src_len;
2012 cfg->fc_flags = rtmsg->rtmsg_flags;
2013
Daniel Lezcano55786892008-03-04 13:47:47 -08002014 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08002015
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002016 cfg->fc_dst = rtmsg->rtmsg_dst;
2017 cfg->fc_src = rtmsg->rtmsg_src;
2018 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07002019}
2020
Daniel Lezcano55786892008-03-04 13:47:47 -08002021int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022{
Thomas Graf86872cb2006-08-22 00:01:08 -07002023 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 struct in6_rtmsg rtmsg;
2025 int err;
2026
2027 switch(cmd) {
2028 case SIOCADDRT: /* Add a route */
2029 case SIOCDELRT: /* Delete a route */
2030 if (!capable(CAP_NET_ADMIN))
2031 return -EPERM;
2032 err = copy_from_user(&rtmsg, arg,
2033 sizeof(struct in6_rtmsg));
2034 if (err)
2035 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07002036
Daniel Lezcano55786892008-03-04 13:47:47 -08002037 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07002038
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 rtnl_lock();
2040 switch (cmd) {
2041 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002042 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 break;
2044 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07002045 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 break;
2047 default:
2048 err = -EINVAL;
2049 }
2050 rtnl_unlock();
2051
2052 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07002053 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054
2055 return -EINVAL;
2056}
2057
2058/*
2059 * Drop the packet on the floor
2060 */
2061
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002062static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002064 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002065 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002066 switch (ipstats_mib_noroutes) {
2067 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002068 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002069 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002070 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2071 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002072 break;
2073 }
2074 /* FALLTHROUGH */
2075 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002076 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2077 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002078 break;
2079 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002080 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 kfree_skb(skb);
2082 return 0;
2083}
2084
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002085static int ip6_pkt_discard(struct sk_buff *skb)
2086{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002087 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002088}
2089
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002090static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091{
Eric Dumazetadf30902009-06-02 05:19:30 +00002092 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002093 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094}
2095
David S. Miller6723ab52006-10-18 21:20:57 -07002096#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2097
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002098static int ip6_pkt_prohibit(struct sk_buff *skb)
2099{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002100 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002101}
2102
2103static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2104{
Eric Dumazetadf30902009-06-02 05:19:30 +00002105 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002106 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002107}
2108
David S. Miller6723ab52006-10-18 21:20:57 -07002109#endif
2110
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111/*
2112 * Allocate a dst for local (unicast / anycast) address.
2113 */
2114
2115struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2116 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002117 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002119 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002120 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002121 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122
David S. Miller38308472011-12-03 18:02:47 -05002123 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002124 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002126 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 in6_dev_hold(idev);
2129
David S. Miller11d53b42011-06-24 15:23:34 -07002130 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002131 rt->dst.input = ip6_input;
2132 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002134 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
2136 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002137 if (anycast)
2138 rt->rt6i_flags |= RTF_ANYCAST;
2139 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002141 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002142 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002143 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002144 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 }
2146
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002147 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002149 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150
Changli Gaod8d1f302010-06-10 23:31:35 -07002151 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152
2153 return rt;
2154}
2155
Daniel Walterc3968a82011-04-13 21:10:57 +00002156int ip6_route_get_saddr(struct net *net,
2157 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002158 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002159 unsigned int prefs,
2160 struct in6_addr *saddr)
2161{
2162 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2163 int err = 0;
2164 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002165 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002166 else
2167 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2168 daddr, prefs, saddr);
2169 return err;
2170}
2171
2172/* remove deleted ip from prefsrc entries */
2173struct arg_dev_net_ip {
2174 struct net_device *dev;
2175 struct net *net;
2176 struct in6_addr *addr;
2177};
2178
2179static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2180{
2181 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2182 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2183 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2184
David S. Millerd1918542011-12-28 20:19:20 -05002185 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002186 rt != net->ipv6.ip6_null_entry &&
2187 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2188 /* remove prefsrc entry */
2189 rt->rt6i_prefsrc.plen = 0;
2190 }
2191 return 0;
2192}
2193
2194void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2195{
2196 struct net *net = dev_net(ifp->idev->dev);
2197 struct arg_dev_net_ip adni = {
2198 .dev = ifp->idev->dev,
2199 .net = net,
2200 .addr = &ifp->addr,
2201 };
2202 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2203}
2204
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002205struct arg_dev_net {
2206 struct net_device *dev;
2207 struct net *net;
2208};
2209
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210static int fib6_ifdown(struct rt6_info *rt, void *arg)
2211{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002212 const struct arg_dev_net *adn = arg;
2213 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002214
David S. Millerd1918542011-12-28 20:19:20 -05002215 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002216 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002218
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219 return 0;
2220}
2221
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002222void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002224 struct arg_dev_net adn = {
2225 .dev = dev,
2226 .net = net,
2227 };
2228
2229 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002230 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231}
2232
Eric Dumazet95c96172012-04-15 05:58:06 +00002233struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002235 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236};
2237
2238static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2239{
2240 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2241 struct inet6_dev *idev;
2242
2243 /* In IPv6 pmtu discovery is not optional,
2244 so that RTAX_MTU lock cannot disable it.
2245 We still use this lock to block changes
2246 caused by addrconf/ndisc.
2247 */
2248
2249 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002250 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 return 0;
2252
2253 /* For administrative MTU increase, there is no way to discover
2254 IPv6 PMTU increase, so PMTU increase should be updated here.
2255 Since RFC 1981 doesn't include administrative MTU increase
2256 update PMTU increase is a MUST. (i.e. jumbo frame)
2257 */
2258 /*
2259 If new MTU is less than route PMTU, this new MTU will be the
2260 lowest MTU in the path, update the route PMTU to reflect PMTU
2261 decreases; if new MTU is greater than route PMTU, and the
2262 old MTU is the lowest MTU in the path, update the route PMTU
2263 to reflect the increase. In this case if the other nodes' MTU
2264 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2265 PMTU discouvery.
2266 */
David S. Millerd1918542011-12-28 20:19:20 -05002267 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002268 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2269 (dst_mtu(&rt->dst) >= arg->mtu ||
2270 (dst_mtu(&rt->dst) < arg->mtu &&
2271 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002272 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002273 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 return 0;
2275}
2276
Eric Dumazet95c96172012-04-15 05:58:06 +00002277void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278{
Thomas Grafc71099a2006-08-04 23:20:06 -07002279 struct rt6_mtu_change_arg arg = {
2280 .dev = dev,
2281 .mtu = mtu,
2282 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002284 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285}
2286
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002287static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002288 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002289 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002290 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002291 [RTA_PRIORITY] = { .type = NLA_U32 },
2292 [RTA_METRICS] = { .type = NLA_NESTED },
2293};
2294
2295static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2296 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297{
Thomas Graf86872cb2006-08-22 00:01:08 -07002298 struct rtmsg *rtm;
2299 struct nlattr *tb[RTA_MAX+1];
2300 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301
Thomas Graf86872cb2006-08-22 00:01:08 -07002302 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2303 if (err < 0)
2304 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305
Thomas Graf86872cb2006-08-22 00:01:08 -07002306 err = -EINVAL;
2307 rtm = nlmsg_data(nlh);
2308 memset(cfg, 0, sizeof(*cfg));
2309
2310 cfg->fc_table = rtm->rtm_table;
2311 cfg->fc_dst_len = rtm->rtm_dst_len;
2312 cfg->fc_src_len = rtm->rtm_src_len;
2313 cfg->fc_flags = RTF_UP;
2314 cfg->fc_protocol = rtm->rtm_protocol;
2315
2316 if (rtm->rtm_type == RTN_UNREACHABLE)
2317 cfg->fc_flags |= RTF_REJECT;
2318
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002319 if (rtm->rtm_type == RTN_LOCAL)
2320 cfg->fc_flags |= RTF_LOCAL;
2321
Thomas Graf86872cb2006-08-22 00:01:08 -07002322 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2323 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002324 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002325
2326 if (tb[RTA_GATEWAY]) {
2327 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2328 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002330
2331 if (tb[RTA_DST]) {
2332 int plen = (rtm->rtm_dst_len + 7) >> 3;
2333
2334 if (nla_len(tb[RTA_DST]) < plen)
2335 goto errout;
2336
2337 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002339
2340 if (tb[RTA_SRC]) {
2341 int plen = (rtm->rtm_src_len + 7) >> 3;
2342
2343 if (nla_len(tb[RTA_SRC]) < plen)
2344 goto errout;
2345
2346 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002348
Daniel Walterc3968a82011-04-13 21:10:57 +00002349 if (tb[RTA_PREFSRC])
2350 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2351
Thomas Graf86872cb2006-08-22 00:01:08 -07002352 if (tb[RTA_OIF])
2353 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2354
2355 if (tb[RTA_PRIORITY])
2356 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2357
2358 if (tb[RTA_METRICS]) {
2359 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2360 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002362
2363 if (tb[RTA_TABLE])
2364 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2365
2366 err = 0;
2367errout:
2368 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369}
2370
Thomas Grafc127ea22007-03-22 11:58:32 -07002371static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372{
Thomas Graf86872cb2006-08-22 00:01:08 -07002373 struct fib6_config cfg;
2374 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375
Thomas Graf86872cb2006-08-22 00:01:08 -07002376 err = rtm_to_fib6_config(skb, nlh, &cfg);
2377 if (err < 0)
2378 return err;
2379
2380 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381}
2382
Thomas Grafc127ea22007-03-22 11:58:32 -07002383static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384{
Thomas Graf86872cb2006-08-22 00:01:08 -07002385 struct fib6_config cfg;
2386 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387
Thomas Graf86872cb2006-08-22 00:01:08 -07002388 err = rtm_to_fib6_config(skb, nlh, &cfg);
2389 if (err < 0)
2390 return err;
2391
2392 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393}
2394
Thomas Graf339bf982006-11-10 14:10:15 -08002395static inline size_t rt6_nlmsg_size(void)
2396{
2397 return NLMSG_ALIGN(sizeof(struct rtmsg))
2398 + nla_total_size(16) /* RTA_SRC */
2399 + nla_total_size(16) /* RTA_DST */
2400 + nla_total_size(16) /* RTA_GATEWAY */
2401 + nla_total_size(16) /* RTA_PREFSRC */
2402 + nla_total_size(4) /* RTA_TABLE */
2403 + nla_total_size(4) /* RTA_IIF */
2404 + nla_total_size(4) /* RTA_OIF */
2405 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002406 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002407 + nla_total_size(sizeof(struct rta_cacheinfo));
2408}
2409
Brian Haley191cd582008-08-14 15:33:21 -07002410static int rt6_fill_node(struct net *net,
2411 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002412 struct in6_addr *dst, struct in6_addr *src,
2413 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002414 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415{
2416 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002417 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002418 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002419 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002420 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421
2422 if (prefix) { /* user wants prefix routes only */
2423 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2424 /* success since this is not a prefix route */
2425 return 1;
2426 }
2427 }
2428
Thomas Graf2d7202b2006-08-22 00:01:27 -07002429 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002430 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002431 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002432
2433 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 rtm->rtm_family = AF_INET6;
2435 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2436 rtm->rtm_src_len = rt->rt6i_src.plen;
2437 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002438 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002439 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002440 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002441 table = RT6_TABLE_UNSPEC;
2442 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002443 if (nla_put_u32(skb, RTA_TABLE, table))
2444 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002445 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002447 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002448 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002449 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450 rtm->rtm_type = RTN_LOCAL;
2451 else
2452 rtm->rtm_type = RTN_UNICAST;
2453 rtm->rtm_flags = 0;
2454 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2455 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002456 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457 rtm->rtm_protocol = RTPROT_REDIRECT;
2458 else if (rt->rt6i_flags & RTF_ADDRCONF)
2459 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002460 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 rtm->rtm_protocol = RTPROT_RA;
2462
David S. Miller38308472011-12-03 18:02:47 -05002463 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 rtm->rtm_flags |= RTM_F_CLONED;
2465
2466 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002467 if (nla_put(skb, RTA_DST, 16, dst))
2468 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002469 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002471 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2472 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473#ifdef CONFIG_IPV6_SUBTREES
2474 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002475 if (nla_put(skb, RTA_SRC, 16, src))
2476 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002477 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002478 } else if (rtm->rtm_src_len &&
2479 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2480 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002482 if (iif) {
2483#ifdef CONFIG_IPV6_MROUTE
2484 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002485 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002486 if (err <= 0) {
2487 if (!nowait) {
2488 if (err == 0)
2489 return 0;
2490 goto nla_put_failure;
2491 } else {
2492 if (err == -EMSGSIZE)
2493 goto nla_put_failure;
2494 }
2495 }
2496 } else
2497#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002498 if (nla_put_u32(skb, RTA_IIF, iif))
2499 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002500 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002502 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2503 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2504 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002506
Daniel Walterc3968a82011-04-13 21:10:57 +00002507 if (rt->rt6i_prefsrc.plen) {
2508 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002509 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002510 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2511 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002512 }
2513
David S. Millerdefb3512010-12-08 21:16:57 -08002514 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002515 goto nla_put_failure;
2516
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002517 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002518 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002519 if (n) {
2520 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2521 rcu_read_unlock();
2522 goto nla_put_failure;
2523 }
2524 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002525 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002526
David S. Millerc78679e2012-04-01 20:27:33 -04002527 if (rt->dst.dev &&
2528 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2529 goto nla_put_failure;
2530 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2531 goto nla_put_failure;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002532 if (!(rt->rt6i_flags & RTF_EXPIRES))
2533 expires = 0;
David S. Millerd1918542011-12-28 20:19:20 -05002534 else if (rt->dst.expires - jiffies < INT_MAX)
2535 expires = rt->dst.expires - jiffies;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002536 else
2537 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002538
David S. Miller87a50692012-07-10 05:06:14 -07002539 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002540 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541
Thomas Graf2d7202b2006-08-22 00:01:27 -07002542 return nlmsg_end(skb, nlh);
2543
2544nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002545 nlmsg_cancel(skb, nlh);
2546 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547}
2548
Patrick McHardy1b43af52006-08-10 23:11:17 -07002549int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550{
2551 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2552 int prefix;
2553
Thomas Graf2d7202b2006-08-22 00:01:27 -07002554 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2555 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2557 } else
2558 prefix = 0;
2559
Brian Haley191cd582008-08-14 15:33:21 -07002560 return rt6_fill_node(arg->net,
2561 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002563 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564}
2565
Thomas Grafc127ea22007-03-22 11:58:32 -07002566static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002568 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002569 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002571 struct sk_buff *skb;
2572 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002573 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002574 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002575
2576 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2577 if (err < 0)
2578 goto errout;
2579
2580 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002581 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002582
2583 if (tb[RTA_SRC]) {
2584 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2585 goto errout;
2586
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002587 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002588 }
2589
2590 if (tb[RTA_DST]) {
2591 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2592 goto errout;
2593
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002594 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002595 }
2596
2597 if (tb[RTA_IIF])
2598 iif = nla_get_u32(tb[RTA_IIF]);
2599
2600 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002601 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002602
2603 if (iif) {
2604 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002605 int flags = 0;
2606
Daniel Lezcano55786892008-03-04 13:47:47 -08002607 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002608 if (!dev) {
2609 err = -ENODEV;
2610 goto errout;
2611 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002612
2613 fl6.flowi6_iif = iif;
2614
2615 if (!ipv6_addr_any(&fl6.saddr))
2616 flags |= RT6_LOOKUP_F_HAS_SADDR;
2617
2618 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2619 flags);
2620 } else {
2621 fl6.flowi6_oif = oif;
2622
2623 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002624 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625
2626 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002627 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002628 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002629 err = -ENOBUFS;
2630 goto errout;
2631 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632
2633 /* Reserve room for dummy headers, this skb can pass
2634 through good chunk of routing engine.
2635 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002636 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2638
Changli Gaod8d1f302010-06-10 23:31:35 -07002639 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640
David S. Miller4c9483b2011-03-12 16:22:43 -05002641 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002643 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002645 kfree_skb(skb);
2646 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 }
2648
Daniel Lezcano55786892008-03-04 13:47:47 -08002649 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002650errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652}
2653
Thomas Graf86872cb2006-08-22 00:01:08 -07002654void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655{
2656 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002657 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002658 u32 seq;
2659 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002661 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002662 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002663
Thomas Graf339bf982006-11-10 14:10:15 -08002664 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002665 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002666 goto errout;
2667
Brian Haley191cd582008-08-14 15:33:21 -07002668 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002669 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002670 if (err < 0) {
2671 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2672 WARN_ON(err == -EMSGSIZE);
2673 kfree_skb(skb);
2674 goto errout;
2675 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002676 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2677 info->nlh, gfp_any());
2678 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002679errout:
2680 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002681 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682}
2683
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002684static int ip6_route_dev_notify(struct notifier_block *this,
2685 unsigned long event, void *data)
2686{
2687 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002688 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002689
2690 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002691 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002692 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2693#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002694 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002695 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002696 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002697 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2698#endif
2699 }
2700
2701 return NOTIFY_OK;
2702}
2703
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704/*
2705 * /proc
2706 */
2707
2708#ifdef CONFIG_PROC_FS
2709
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710struct rt6_proc_arg
2711{
2712 char *buffer;
2713 int offset;
2714 int length;
2715 int skip;
2716 int len;
2717};
2718
2719static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2720{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002721 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002722 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002724 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725
2726#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002727 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002729 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002731 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002732 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002733 if (n) {
2734 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002736 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002738 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002739 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002740 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2741 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002742 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743 return 0;
2744}
2745
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002746static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002748 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002749 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002750 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751}
2752
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002753static int ipv6_route_open(struct inode *inode, struct file *file)
2754{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002755 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002756}
2757
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002758static const struct file_operations ipv6_route_proc_fops = {
2759 .owner = THIS_MODULE,
2760 .open = ipv6_route_open,
2761 .read = seq_read,
2762 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002763 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002764};
2765
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2767{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002768 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002770 net->ipv6.rt6_stats->fib_nodes,
2771 net->ipv6.rt6_stats->fib_route_nodes,
2772 net->ipv6.rt6_stats->fib_rt_alloc,
2773 net->ipv6.rt6_stats->fib_rt_entries,
2774 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002775 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002776 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777
2778 return 0;
2779}
2780
2781static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2782{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002783 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002784}
2785
Arjan van de Ven9a321442007-02-12 00:55:35 -08002786static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 .owner = THIS_MODULE,
2788 .open = rt6_stats_seq_open,
2789 .read = seq_read,
2790 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002791 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792};
2793#endif /* CONFIG_PROC_FS */
2794
2795#ifdef CONFIG_SYSCTL
2796
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002798int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 void __user *buffer, size_t *lenp, loff_t *ppos)
2800{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002801 struct net *net;
2802 int delay;
2803 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002805
2806 net = (struct net *)ctl->extra1;
2807 delay = net->ipv6.sysctl.flush_delay;
2808 proc_dointvec(ctl, write, buffer, lenp, ppos);
2809 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2810 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811}
2812
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002813ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002814 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002816 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002818 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002819 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 },
2821 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002823 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 .maxlen = sizeof(int),
2825 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002826 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 },
2828 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002830 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 .maxlen = sizeof(int),
2832 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002833 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 },
2835 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002837 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 .maxlen = sizeof(int),
2839 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002840 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 },
2842 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002844 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 .maxlen = sizeof(int),
2846 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002847 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 },
2849 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002851 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 .maxlen = sizeof(int),
2853 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002854 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 },
2856 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002858 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 .maxlen = sizeof(int),
2860 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002861 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862 },
2863 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002865 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 .maxlen = sizeof(int),
2867 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002868 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869 },
2870 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002872 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 .maxlen = sizeof(int),
2874 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002875 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 },
2877 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002879 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 .maxlen = sizeof(int),
2881 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002882 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002884 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885};
2886
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002887struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002888{
2889 struct ctl_table *table;
2890
2891 table = kmemdup(ipv6_route_table_template,
2892 sizeof(ipv6_route_table_template),
2893 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002894
2895 if (table) {
2896 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002897 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002898 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002899 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2900 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2901 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2902 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2903 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2904 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2905 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002906 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002907 }
2908
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002909 return table;
2910}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911#endif
2912
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002913static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002914{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002915 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002916
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002917 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2918 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002919
Eric Dumazetfc66f952010-10-08 06:37:34 +00002920 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2921 goto out_ip6_dst_ops;
2922
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002923 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2924 sizeof(*net->ipv6.ip6_null_entry),
2925 GFP_KERNEL);
2926 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002927 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002928 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002929 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002930 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002931 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2932 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002933
2934#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2935 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2936 sizeof(*net->ipv6.ip6_prohibit_entry),
2937 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002938 if (!net->ipv6.ip6_prohibit_entry)
2939 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002940 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002941 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002942 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002943 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2944 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002945
2946 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2947 sizeof(*net->ipv6.ip6_blk_hole_entry),
2948 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002949 if (!net->ipv6.ip6_blk_hole_entry)
2950 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002951 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002952 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002953 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002954 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2955 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002956#endif
2957
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002958 net->ipv6.sysctl.flush_delay = 0;
2959 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2960 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2961 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2962 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2963 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2964 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2965 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2966
Benjamin Thery6891a342008-03-04 13:49:47 -08002967 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2968
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002969 ret = 0;
2970out:
2971 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002972
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002973#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2974out_ip6_prohibit_entry:
2975 kfree(net->ipv6.ip6_prohibit_entry);
2976out_ip6_null_entry:
2977 kfree(net->ipv6.ip6_null_entry);
2978#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002979out_ip6_dst_entries:
2980 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002981out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002982 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002983}
2984
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002985static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002986{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002987 kfree(net->ipv6.ip6_null_entry);
2988#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2989 kfree(net->ipv6.ip6_prohibit_entry);
2990 kfree(net->ipv6.ip6_blk_hole_entry);
2991#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002992 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002993}
2994
Thomas Grafd1896342012-06-18 12:08:33 +00002995static int __net_init ip6_route_net_init_late(struct net *net)
2996{
2997#ifdef CONFIG_PROC_FS
2998 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2999 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
3000#endif
3001 return 0;
3002}
3003
3004static void __net_exit ip6_route_net_exit_late(struct net *net)
3005{
3006#ifdef CONFIG_PROC_FS
3007 proc_net_remove(net, "ipv6_route");
3008 proc_net_remove(net, "rt6_stats");
3009#endif
3010}
3011
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003012static struct pernet_operations ip6_route_net_ops = {
3013 .init = ip6_route_net_init,
3014 .exit = ip6_route_net_exit,
3015};
3016
David S. Millerc3426b42012-06-09 16:27:05 -07003017static int __net_init ipv6_inetpeer_init(struct net *net)
3018{
3019 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3020
3021 if (!bp)
3022 return -ENOMEM;
3023 inet_peer_base_init(bp);
3024 net->ipv6.peers = bp;
3025 return 0;
3026}
3027
3028static void __net_exit ipv6_inetpeer_exit(struct net *net)
3029{
3030 struct inet_peer_base *bp = net->ipv6.peers;
3031
3032 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07003033 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07003034 kfree(bp);
3035}
3036
David S. Miller2b823f72012-06-09 19:00:16 -07003037static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07003038 .init = ipv6_inetpeer_init,
3039 .exit = ipv6_inetpeer_exit,
3040};
3041
Thomas Grafd1896342012-06-18 12:08:33 +00003042static struct pernet_operations ip6_route_net_late_ops = {
3043 .init = ip6_route_net_init_late,
3044 .exit = ip6_route_net_exit_late,
3045};
3046
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003047static struct notifier_block ip6_route_dev_notifier = {
3048 .notifier_call = ip6_route_dev_notify,
3049 .priority = 0,
3050};
3051
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003052int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003053{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003054 int ret;
3055
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08003056 ret = -ENOMEM;
3057 ip6_dst_ops_template.kmem_cachep =
3058 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
3059 SLAB_HWCACHE_ALIGN, NULL);
3060 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08003061 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07003062
Eric Dumazetfc66f952010-10-08 06:37:34 +00003063 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003064 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003065 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003066
David S. Millerc3426b42012-06-09 16:27:05 -07003067 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3068 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003069 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003070
David S. Miller7e52b332012-06-15 15:51:55 -07003071 ret = register_pernet_subsys(&ip6_route_net_ops);
3072 if (ret)
3073 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003074
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003075 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3076
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003077 /* Registering of the loopback is done before this portion of code,
3078 * the loopback reference in rt6_info will not be taken, do it
3079 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003080 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003081 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3082 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003083 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003084 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003085 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003086 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3087 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003088 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003089 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003090 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003091
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003092 ret = xfrm6_init();
3093 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003094 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003095
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003096 ret = fib6_rules_init();
3097 if (ret)
3098 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003099
Thomas Grafd1896342012-06-18 12:08:33 +00003100 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3101 if (ret)
3102 goto fib6_rules_init;
3103
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003104 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003105 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3106 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3107 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003108 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003109
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003110 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003111 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003112 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003113
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003114out:
3115 return ret;
3116
Thomas Grafd1896342012-06-18 12:08:33 +00003117out_register_late_subsys:
3118 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003119fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003120 fib6_rules_cleanup();
3121xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003122 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003123out_fib6_init:
3124 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003125out_register_subsys:
3126 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003127out_register_inetpeer:
3128 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003129out_dst_entries:
3130 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003131out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003132 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003133 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134}
3135
3136void ip6_route_cleanup(void)
3137{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003138 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003139 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003140 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003142 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003143 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003144 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003145 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003146 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147}