blob: 34b29881e22dfff27ccbb3308bd83ec0c26dc203 [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. Millerf83c7792011-12-28 15:41:23 -0500158 dst_set_neighbour(&rt->dst, n);
159
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 Harrison09640e632009-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 Harrison09640e632009-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) {
Madalin Bucurfbe58182011-09-26 07:04:56 +0000276 memset(&rt->rt6i_table, 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
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000288 if (!(rt->dst.flags & DST_HOST))
289 dst_destroy_metrics_generic(dst);
290
David S. Miller38308472011-12-03 18:02:47 -0500291 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 rt->rt6i_idev = NULL;
293 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900294 }
Gao feng1716a962012-04-06 00:13:10 +0000295
296 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
297 dst_release(dst->from);
298
David S. Miller97bab732012-06-09 22:36:36 -0700299 if (rt6_has_peer(rt)) {
300 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800301 inet_putpeer(peer);
302 }
303}
304
David S. Miller6431cbc2011-02-07 20:38:06 -0800305static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
306
307static u32 rt6_peer_genid(void)
308{
309 return atomic_read(&__rt6_peer_genid);
310}
311
David S. Millerb3419362010-11-30 12:27:11 -0800312void rt6_bind_peer(struct rt6_info *rt, int create)
313{
David S. Miller97bab732012-06-09 22:36:36 -0700314 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800315 struct inet_peer *peer;
316
David S. Miller97bab732012-06-09 22:36:36 -0700317 base = inetpeer_base_ptr(rt->_rt6i_peer);
318 if (!base)
319 return;
320
321 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700322 if (peer) {
323 if (!rt6_set_peer(rt, peer))
324 inet_putpeer(peer);
325 else
326 rt->rt6i_peer_genid = rt6_peer_genid();
327 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
330static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
331 int how)
332{
333 struct rt6_info *rt = (struct rt6_info *)dst;
334 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800335 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900336 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
David S. Miller38308472011-12-03 18:02:47 -0500338 if (dev != loopback_dev && idev && idev->dev == dev) {
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800339 struct inet6_dev *loopback_idev =
340 in6_dev_get(loopback_dev);
David S. Miller38308472011-12-03 18:02:47 -0500341 if (loopback_idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 rt->rt6i_idev = loopback_idev;
343 in6_dev_put(idev);
344 }
345 }
346}
347
Eric Dumazeta50feda2012-05-18 18:57:34 +0000348static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
Gao feng1716a962012-04-06 00:13:10 +0000350 struct rt6_info *ort = NULL;
351
352 if (rt->rt6i_flags & RTF_EXPIRES) {
353 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000354 return true;
Gao feng1716a962012-04-06 00:13:10 +0000355 } else if (rt->dst.from) {
356 ort = (struct rt6_info *) rt->dst.from;
357 return (ort->rt6i_flags & RTF_EXPIRES) &&
358 time_after(jiffies, ort->dst.expires);
359 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000360 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361}
362
Eric Dumazeta50feda2012-05-18 18:57:34 +0000363static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700364{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000365 return ipv6_addr_type(daddr) &
366 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700367}
368
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700370 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 */
372
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800373static inline struct rt6_info *rt6_device_match(struct net *net,
374 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000375 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700377 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378{
379 struct rt6_info *local = NULL;
380 struct rt6_info *sprt;
381
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900382 if (!oif && ipv6_addr_any(saddr))
383 goto out;
384
Changli Gaod8d1f302010-06-10 23:31:35 -0700385 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500386 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900387
388 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 if (dev->ifindex == oif)
390 return sprt;
391 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500392 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700394 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900396 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 local->rt6i_idev->dev->ifindex == oif))
398 continue;
399 }
400 local = sprt;
401 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900402 } else {
403 if (ipv6_chk_addr(net, saddr, dev,
404 flags & RT6_LOOKUP_F_IFACE))
405 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900407 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900409 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 if (local)
411 return local;
412
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700413 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800414 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900416out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 return rt;
418}
419
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800420#ifdef CONFIG_IPV6_ROUTER_PREF
421static void rt6_probe(struct rt6_info *rt)
422{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000423 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800424 /*
425 * Okay, this does not seem to be appropriate
426 * for now, however, we need to check if it
427 * is really so; aka Router Reachability Probing.
428 *
429 * Router Reachability Probe MUST be rate-limited
430 * to no more than one per minute.
431 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000432 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +0000433 neigh = rt ? dst_get_neighbour_noref(&rt->dst) : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800434 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000435 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800436 read_lock_bh(&neigh->lock);
437 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e163562006-03-20 17:05:47 -0800438 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800439 struct in6_addr mcaddr;
440 struct in6_addr *target;
441
442 neigh->updated = jiffies;
443 read_unlock_bh(&neigh->lock);
444
445 target = (struct in6_addr *)&neigh->primary_key;
446 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500447 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000448 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800449 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000450 }
451out:
452 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800453}
454#else
455static inline void rt6_probe(struct rt6_info *rt)
456{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800457}
458#endif
459
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800461 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700463static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
David S. Millerd1918542011-12-28 20:19:20 -0500465 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700466 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800467 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700468 if ((dev->flags & IFF_LOOPBACK) &&
469 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
470 return 1;
471 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472}
473
Dave Jonesb6f99a22007-03-22 12:27:49 -0700474static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000476 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800477 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000478
479 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +0000480 neigh = dst_get_neighbour_noref(&rt->dst);
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700481 if (rt->rt6i_flags & RTF_NONEXTHOP ||
482 !(rt->rt6i_flags & RTF_GATEWAY))
483 m = 1;
484 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800485 read_lock_bh(&neigh->lock);
486 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700487 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800488#ifdef CONFIG_IPV6_ROUTER_PREF
489 else if (neigh->nud_state & NUD_FAILED)
490 m = 0;
491#endif
492 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800493 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800494 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800495 } else
496 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000497 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800498 return m;
499}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800501static int rt6_score_route(struct rt6_info *rt, int oif,
502 int strict)
503{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700504 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900505
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700506 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700507 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800508 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800509#ifdef CONFIG_IPV6_ROUTER_PREF
510 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
511#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700512 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800513 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800514 return -1;
515 return m;
516}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
David S. Millerf11e6652007-03-24 20:36:25 -0700518static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
519 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800520{
David S. Millerf11e6652007-03-24 20:36:25 -0700521 int m;
522
523 if (rt6_check_expired(rt))
524 goto out;
525
526 m = rt6_score_route(rt, oif, strict);
527 if (m < 0)
528 goto out;
529
530 if (m > *mpri) {
531 if (strict & RT6_LOOKUP_F_REACHABLE)
532 rt6_probe(match);
533 *mpri = m;
534 match = rt;
535 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
536 rt6_probe(rt);
537 }
538
539out:
540 return match;
541}
542
543static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
544 struct rt6_info *rr_head,
545 u32 metric, int oif, int strict)
546{
547 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800548 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
David S. Millerf11e6652007-03-24 20:36:25 -0700550 match = NULL;
551 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700552 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700553 match = find_match(rt, oif, strict, &mpri, match);
554 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700555 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700556 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800557
David S. Millerf11e6652007-03-24 20:36:25 -0700558 return match;
559}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800560
David S. Millerf11e6652007-03-24 20:36:25 -0700561static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
562{
563 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800564 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
David S. Millerf11e6652007-03-24 20:36:25 -0700566 rt0 = fn->rr_ptr;
567 if (!rt0)
568 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
David S. Millerf11e6652007-03-24 20:36:25 -0700570 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800572 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700573 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700574 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700575
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800576 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700577 if (!next || next->rt6i_metric != rt0->rt6i_metric)
578 next = fn->leaf;
579
580 if (next != rt0)
581 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 }
583
David S. Millerd1918542011-12-28 20:19:20 -0500584 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000585 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800588#ifdef CONFIG_IPV6_ROUTE_INFO
589int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000590 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800591{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900592 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800593 struct route_info *rinfo = (struct route_info *) opt;
594 struct in6_addr prefix_buf, *prefix;
595 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900596 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800597 struct rt6_info *rt;
598
599 if (len < sizeof(struct route_info)) {
600 return -EINVAL;
601 }
602
603 /* Sanity check for prefix_len and length */
604 if (rinfo->length > 3) {
605 return -EINVAL;
606 } else if (rinfo->prefix_len > 128) {
607 return -EINVAL;
608 } else if (rinfo->prefix_len > 64) {
609 if (rinfo->length < 2) {
610 return -EINVAL;
611 }
612 } else if (rinfo->prefix_len > 0) {
613 if (rinfo->length < 1) {
614 return -EINVAL;
615 }
616 }
617
618 pref = rinfo->route_pref;
619 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000620 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800621
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900622 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800623
624 if (rinfo->length == 3)
625 prefix = (struct in6_addr *)rinfo->prefix;
626 else {
627 /* this function is safe */
628 ipv6_addr_prefix(&prefix_buf,
629 (struct in6_addr *)rinfo->prefix,
630 rinfo->prefix_len);
631 prefix = &prefix_buf;
632 }
633
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800634 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
635 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800636
637 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700638 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800639 rt = NULL;
640 }
641
642 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800643 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800644 pref);
645 else if (rt)
646 rt->rt6i_flags = RTF_ROUTEINFO |
647 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
648
649 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000650 if (!addrconf_finite_timeout(lifetime))
651 rt6_clean_expires(rt);
652 else
653 rt6_set_expires(rt, jiffies + HZ * lifetime);
654
Changli Gaod8d1f302010-06-10 23:31:35 -0700655 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800656 }
657 return 0;
658}
659#endif
660
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800661#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700662do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800663 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700664 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700665 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700666 if (fn->fn_flags & RTN_TL_ROOT) \
667 goto out; \
668 pn = fn->parent; \
669 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800670 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700671 else \
672 fn = pn; \
673 if (fn->fn_flags & RTN_RTINFO) \
674 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700675 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700676 } \
David S. Miller38308472011-12-03 18:02:47 -0500677} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700678
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800679static struct rt6_info *ip6_pol_route_lookup(struct net *net,
680 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500681 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682{
683 struct fib6_node *fn;
684 struct rt6_info *rt;
685
Thomas Grafc71099a2006-08-04 23:20:06 -0700686 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500687 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700688restart:
689 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500690 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
691 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700692out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700693 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700694 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700695 return rt;
696
697}
698
Florian Westphalea6e5742011-09-05 16:05:44 +0200699struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
700 int flags)
701{
702 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
703}
704EXPORT_SYMBOL_GPL(ip6_route_lookup);
705
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900706struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
707 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700708{
David S. Miller4c9483b2011-03-12 16:22:43 -0500709 struct flowi6 fl6 = {
710 .flowi6_oif = oif,
711 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700712 };
713 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700714 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700715
Thomas Grafadaa70b2006-10-13 15:01:03 -0700716 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500717 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700718 flags |= RT6_LOOKUP_F_HAS_SADDR;
719 }
720
David S. Miller4c9483b2011-03-12 16:22:43 -0500721 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700722 if (dst->error == 0)
723 return (struct rt6_info *) dst;
724
725 dst_release(dst);
726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 return NULL;
728}
729
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900730EXPORT_SYMBOL(rt6_lookup);
731
Thomas Grafc71099a2006-08-04 23:20:06 -0700732/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 It takes new route entry, the addition fails by any reason the
734 route is freed. In any case, if caller does not hold it, it may
735 be destroyed.
736 */
737
Thomas Graf86872cb2006-08-22 00:01:08 -0700738static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
740 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700741 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Thomas Grafc71099a2006-08-04 23:20:06 -0700743 table = rt->rt6i_table;
744 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700745 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700746 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
748 return err;
749}
750
Thomas Graf40e22e82006-08-22 00:00:45 -0700751int ip6_ins_rt(struct rt6_info *rt)
752{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800753 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500754 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800755 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800756 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700757}
758
Gao feng1716a962012-04-06 00:13:10 +0000759static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000760 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000761 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 struct rt6_info *rt;
764
765 /*
766 * Clone the route.
767 */
768
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000769 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
771 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800772 int attempts = !in_softirq();
773
David S. Miller38308472011-12-03 18:02:47 -0500774 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500775 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000776 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900777 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000778 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900779 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783#ifdef CONFIG_IPV6_SUBTREES
784 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000785 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 rt->rt6i_src.plen = 128;
787 }
788#endif
789
David S. Miller14deae42009-01-04 16:04:39 -0800790 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500791 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500792 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800793 int saved_rt_min_interval =
794 net->ipv6.sysctl.ip6_rt_gc_min_interval;
795 int saved_rt_elasticity =
796 net->ipv6.sysctl.ip6_rt_gc_elasticity;
797
798 if (attempts-- > 0) {
799 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
800 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
801
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000802 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800803
804 net->ipv6.sysctl.ip6_rt_gc_elasticity =
805 saved_rt_elasticity;
806 net->ipv6.sysctl.ip6_rt_gc_min_interval =
807 saved_rt_min_interval;
808 goto retry;
809 }
810
Joe Perchesf3213832012-05-15 14:11:53 +0000811 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700812 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800813 return NULL;
814 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800815 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800817 return rt;
818}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000820static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
821 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800822{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000823 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
824
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800825 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800826 rt->rt6i_flags |= RTF_CACHE;
David Miller27217452011-12-02 16:52:08 +0000827 dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_noref_raw(&ort->dst)));
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800828 }
829 return rt;
830}
831
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800832static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500833 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
835 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800836 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700837 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800839 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700840 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700842 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
844relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700845 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800847restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500848 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
850restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700851 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800852
David S. Miller4c9483b2011-03-12 16:22:43 -0500853 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800854 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800855 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800856 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Changli Gaod8d1f302010-06-10 23:31:35 -0700858 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700859 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800860
David Miller27217452011-12-02 16:52:08 +0000861 if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500862 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800863 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500864 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800865 else
866 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800867
Changli Gaod8d1f302010-06-10 23:31:35 -0700868 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800869 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800870
Changli Gaod8d1f302010-06-10 23:31:35 -0700871 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800872 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700873 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800874 if (!err)
875 goto out2;
876 }
877
878 if (--attempts <= 0)
879 goto out2;
880
881 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700882 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800883 * released someone could insert this route. Relookup.
884 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700885 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800886 goto relookup;
887
888out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800889 if (reachable) {
890 reachable = 0;
891 goto restart_2;
892 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700893 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700894 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700896 rt->dst.lastuse = jiffies;
897 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700898
899 return rt;
900}
901
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800902static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500903 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700904{
David S. Miller4c9483b2011-03-12 16:22:43 -0500905 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700906}
907
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000908static struct dst_entry *ip6_route_input_lookup(struct net *net,
909 struct net_device *dev,
910 struct flowi6 *fl6, int flags)
911{
912 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
913 flags |= RT6_LOOKUP_F_IFACE;
914
915 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
916}
917
Thomas Grafc71099a2006-08-04 23:20:06 -0700918void ip6_route_input(struct sk_buff *skb)
919{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000920 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900921 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700922 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500923 struct flowi6 fl6 = {
924 .flowi6_iif = skb->dev->ifindex,
925 .daddr = iph->daddr,
926 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500927 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500928 .flowi6_mark = skb->mark,
929 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700930 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700931
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000932 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700933}
934
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800935static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500936 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700937{
David S. Miller4c9483b2011-03-12 16:22:43 -0500938 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700939}
940
Florian Westphal9c7a4f9c2011-03-22 19:17:36 -0700941struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500942 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700943{
944 int flags = 0;
945
David McCullough4dc27d1c2012-06-25 15:42:26 +0000946 fl6->flowi6_iif = net->loopback_dev->ifindex;
947
David S. Miller4c9483b2011-03-12 16:22:43 -0500948 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700949 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700950
David S. Miller4c9483b2011-03-12 16:22:43 -0500951 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700952 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000953 else if (sk)
954 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700955
David S. Miller4c9483b2011-03-12 16:22:43 -0500956 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957}
958
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900959EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
David S. Miller2774c132011-03-01 14:59:04 -0800961struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700962{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700963 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700964 struct dst_entry *new = NULL;
965
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700966 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700967 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700968 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller97bab732012-06-09 22:36:36 -0700969 rt6_init_peer(rt, net->ipv6.peers);
David S. Millercf911662011-04-28 14:31:47 -0700970
Changli Gaod8d1f302010-06-10 23:31:35 -0700971 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700972
David S. Miller14e50e52007-05-24 18:17:54 -0700973 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800974 new->input = dst_discard;
975 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700976
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000977 if (dst_metrics_read_only(&ort->dst))
978 new->_metrics = ort->dst._metrics;
979 else
980 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700981 rt->rt6i_idev = ort->rt6i_idev;
982 if (rt->rt6i_idev)
983 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -0700984
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000985 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +0000986 rt->rt6i_flags = ort->rt6i_flags;
987 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -0700988 rt->rt6i_metric = 0;
989
990 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
991#ifdef CONFIG_IPV6_SUBTREES
992 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
993#endif
994
995 dst_free(new);
996 }
997
David S. Miller69ead7a2011-03-01 14:45:33 -0800998 dst_release(dst_orig);
999 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001000}
David S. Miller14e50e52007-05-24 18:17:54 -07001001
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002/*
1003 * Destination cache support functions
1004 */
1005
1006static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1007{
1008 struct rt6_info *rt;
1009
1010 rt = (struct rt6_info *) dst;
1011
David S. Miller6431cbc2011-02-07 20:38:06 -08001012 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1013 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001014 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001015 rt6_bind_peer(rt, 0);
1016 rt->rt6i_peer_genid = rt6_peer_genid();
1017 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001019 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 return NULL;
1021}
1022
1023static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1024{
1025 struct rt6_info *rt = (struct rt6_info *) dst;
1026
1027 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001028 if (rt->rt6i_flags & RTF_CACHE) {
1029 if (rt6_check_expired(rt)) {
1030 ip6_del_rt(rt);
1031 dst = NULL;
1032 }
1033 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001035 dst = NULL;
1036 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001038 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039}
1040
1041static void ip6_link_failure(struct sk_buff *skb)
1042{
1043 struct rt6_info *rt;
1044
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001045 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
Eric Dumazetadf30902009-06-02 05:19:30 +00001047 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001049 if (rt->rt6i_flags & RTF_CACHE)
1050 rt6_update_expires(rt, 0);
1051 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 rt->rt6i_node->fn_sernum = -1;
1053 }
1054}
1055
1056static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1057{
1058 struct rt6_info *rt6 = (struct rt6_info*)dst;
1059
David S. Miller81aded22012-06-15 14:54:11 -07001060 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001062 struct net *net = dev_net(dst->dev);
1063
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 rt6->rt6i_flags |= RTF_MODIFIED;
1065 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001066 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001068 features |= RTAX_FEATURE_ALLFRAG;
1069 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 }
David S. Millerdefb3512010-12-08 21:16:57 -08001071 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001072 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 }
1074}
1075
David S. Miller42ae66c2012-06-15 20:01:57 -07001076void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1077 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001078{
1079 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1080 struct dst_entry *dst;
1081 struct flowi6 fl6;
1082
1083 memset(&fl6, 0, sizeof(fl6));
1084 fl6.flowi6_oif = oif;
1085 fl6.flowi6_mark = mark;
1086 fl6.flowi6_flags = FLOWI_FLAG_PRECOW_METRICS;
1087 fl6.daddr = iph->daddr;
1088 fl6.saddr = iph->saddr;
1089 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1090
1091 dst = ip6_route_output(net, NULL, &fl6);
1092 if (!dst->error)
1093 ip6_rt_update_pmtu(dst, ntohl(mtu));
1094 dst_release(dst);
1095}
1096EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1097
1098void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1099{
1100 ip6_update_pmtu(skb, sock_net(sk), mtu,
1101 sk->sk_bound_dev_if, sk->sk_mark);
1102}
1103EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1104
David S. Miller0dbaee32010-12-13 12:52:14 -08001105static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106{
David S. Miller0dbaee32010-12-13 12:52:14 -08001107 struct net_device *dev = dst->dev;
1108 unsigned int mtu = dst_mtu(dst);
1109 struct net *net = dev_net(dev);
1110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1112
Daniel Lezcano55786892008-03-04 13:47:47 -08001113 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1114 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
1116 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001117 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1118 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1119 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 * rely only on pmtu discovery"
1121 */
1122 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1123 mtu = IPV6_MAXPLEN;
1124 return mtu;
1125}
1126
Steffen Klassertebb762f2011-11-23 02:12:51 +00001127static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001128{
David S. Millerd33e4552010-12-14 13:01:14 -08001129 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001130 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1131
1132 if (mtu)
1133 return mtu;
1134
1135 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001136
1137 rcu_read_lock();
1138 idev = __in6_dev_get(dst->dev);
1139 if (idev)
1140 mtu = idev->cnf.mtu6;
1141 rcu_read_unlock();
1142
1143 return mtu;
1144}
1145
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001146static struct dst_entry *icmp6_dst_gc_list;
1147static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001148
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001149struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001151 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152{
David S. Miller87a11572011-12-06 17:04:13 -05001153 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 struct rt6_info *rt;
1155 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001156 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
David S. Miller38308472011-12-03 18:02:47 -05001158 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001159 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
David S. Miller8b96d222012-06-11 02:01:56 -07001161 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001162 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001164 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 goto out;
1166 }
1167
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 if (neigh)
1169 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001170 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001171 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001172 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001173 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001174 dst_free(&rt->dst);
1175 return ERR_CAST(neigh);
1176 }
David S. Miller14deae42009-01-04 16:04:39 -08001177 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001179 rt->dst.flags |= DST_HOST;
1180 rt->dst.output = ip6_output;
David S. Miller69cce1d2011-07-17 23:09:49 -07001181 dst_set_neighbour(&rt->dst, neigh);
Changli Gaod8d1f302010-06-10 23:31:35 -07001182 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001183 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001184 rt->rt6i_dst.plen = 128;
1185 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001186 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001188 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001189 rt->dst.next = icmp6_dst_gc_list;
1190 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001191 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192
Daniel Lezcano55786892008-03-04 13:47:47 -08001193 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194
David S. Miller87a11572011-12-06 17:04:13 -05001195 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1196
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197out:
David S. Miller87a11572011-12-06 17:04:13 -05001198 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199}
1200
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001201int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202{
Hagen Paul Pfeifere9476e952011-02-25 05:45:19 +00001203 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001204 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001206 spin_lock_bh(&icmp6_dst_lock);
1207 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 while ((dst = *pprev) != NULL) {
1210 if (!atomic_read(&dst->__refcnt)) {
1211 *pprev = dst->next;
1212 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 } else {
1214 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001215 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 }
1217 }
1218
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001219 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001220
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001221 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222}
1223
David S. Miller1e493d12008-09-10 17:27:15 -07001224static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1225 void *arg)
1226{
1227 struct dst_entry *dst, **pprev;
1228
1229 spin_lock_bh(&icmp6_dst_lock);
1230 pprev = &icmp6_dst_gc_list;
1231 while ((dst = *pprev) != NULL) {
1232 struct rt6_info *rt = (struct rt6_info *) dst;
1233 if (func(rt, arg)) {
1234 *pprev = dst->next;
1235 dst_free(dst);
1236 } else {
1237 pprev = &dst->next;
1238 }
1239 }
1240 spin_unlock_bh(&icmp6_dst_lock);
1241}
1242
Daniel Lezcano569d3642008-01-18 03:56:57 -08001243static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001246 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001247 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1248 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1249 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1250 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1251 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001252 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
Eric Dumazetfc66f952010-10-08 06:37:34 +00001254 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001255 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001256 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 goto out;
1258
Benjamin Thery6891a342008-03-04 13:49:47 -08001259 net->ipv6.ip6_rt_gc_expire++;
1260 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1261 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001262 entries = dst_entries_get_slow(ops);
1263 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001264 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001266 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001267 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268}
1269
1270/* Clean host part of a prefix. Not necessary in radix tree,
1271 but results in cleaner routing tables.
1272
1273 Remove it only when all the things will work!
1274 */
1275
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001276int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277{
David S. Miller5170ae82010-12-12 21:35:57 -08001278 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001279 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001280 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001281 struct inet6_dev *idev;
1282
1283 rcu_read_lock();
1284 idev = __in6_dev_get(dev);
1285 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001286 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001287 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001288 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001289 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 }
1291 return hoplimit;
1292}
David S. Millerabbf46a2010-12-12 21:14:46 -08001293EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
1295/*
1296 *
1297 */
1298
Thomas Graf86872cb2006-08-22 00:01:08 -07001299int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300{
1301 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001302 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 struct rt6_info *rt = NULL;
1304 struct net_device *dev = NULL;
1305 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001306 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 int addr_type;
1308
Thomas Graf86872cb2006-08-22 00:01:08 -07001309 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 return -EINVAL;
1311#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001312 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 return -EINVAL;
1314#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001315 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001317 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 if (!dev)
1319 goto out;
1320 idev = in6_dev_get(dev);
1321 if (!idev)
1322 goto out;
1323 }
1324
Thomas Graf86872cb2006-08-22 00:01:08 -07001325 if (cfg->fc_metric == 0)
1326 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
Matti Vaittinend71314b2011-11-14 00:14:49 +00001328 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001329 if (cfg->fc_nlinfo.nlh &&
1330 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001331 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001332 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001333 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001334 table = fib6_new_table(net, cfg->fc_table);
1335 }
1336 } else {
1337 table = fib6_new_table(net, cfg->fc_table);
1338 }
David S. Miller38308472011-12-03 18:02:47 -05001339
1340 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001341 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001342
David S. Miller8b96d222012-06-11 02:01:56 -07001343 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
David S. Miller38308472011-12-03 18:02:47 -05001345 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 err = -ENOMEM;
1347 goto out;
1348 }
1349
Changli Gaod8d1f302010-06-10 23:31:35 -07001350 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001351
1352 if (cfg->fc_flags & RTF_EXPIRES)
1353 rt6_set_expires(rt, jiffies +
1354 clock_t_to_jiffies(cfg->fc_expires));
1355 else
1356 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Thomas Graf86872cb2006-08-22 00:01:08 -07001358 if (cfg->fc_protocol == RTPROT_UNSPEC)
1359 cfg->fc_protocol = RTPROT_BOOT;
1360 rt->rt6i_protocol = cfg->fc_protocol;
1361
1362 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001365 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001366 else if (cfg->fc_flags & RTF_LOCAL)
1367 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001369 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Changli Gaod8d1f302010-06-10 23:31:35 -07001371 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Thomas Graf86872cb2006-08-22 00:01:08 -07001373 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1374 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001376 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001378 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1379 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1380 if (!metrics) {
1381 err = -ENOMEM;
1382 goto out;
1383 }
1384 dst_init_metrics(&rt->dst, metrics, 0);
1385 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001387 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1388 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389#endif
1390
Thomas Graf86872cb2006-08-22 00:01:08 -07001391 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
1393 /* We cannot add true routes via loopback here,
1394 they would result in kernel looping; promote them to reject routes
1395 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001396 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001397 (dev && (dev->flags & IFF_LOOPBACK) &&
1398 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1399 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001401 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 if (dev) {
1403 dev_put(dev);
1404 in6_dev_put(idev);
1405 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001406 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 dev_hold(dev);
1408 idev = in6_dev_get(dev);
1409 if (!idev) {
1410 err = -ENODEV;
1411 goto out;
1412 }
1413 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001414 rt->dst.output = ip6_pkt_discard_out;
1415 rt->dst.input = ip6_pkt_discard;
1416 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1418 goto install_route;
1419 }
1420
Thomas Graf86872cb2006-08-22 00:01:08 -07001421 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001422 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 int gwa_type;
1424
Thomas Graf86872cb2006-08-22 00:01:08 -07001425 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001426 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 gwa_type = ipv6_addr_type(gw_addr);
1428
1429 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1430 struct rt6_info *grt;
1431
1432 /* IPv6 strictly inhibits using not link-local
1433 addresses as nexthop address.
1434 Otherwise, router will not able to send redirects.
1435 It is very good, but in some (rare!) circumstances
1436 (SIT, PtP, NBMA NOARP links) it is handy to allow
1437 some exceptions. --ANK
1438 */
1439 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001440 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 goto out;
1442
Daniel Lezcano55786892008-03-04 13:47:47 -08001443 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444
1445 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001446 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 goto out;
1448 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001449 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001450 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 goto out;
1452 }
1453 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001454 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 idev = grt->rt6i_idev;
1456 dev_hold(dev);
1457 in6_dev_hold(grt->rt6i_idev);
1458 }
David S. Miller38308472011-12-03 18:02:47 -05001459 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001461 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 if (err)
1464 goto out;
1465 }
1466 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001467 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 goto out;
1469 }
1470
1471 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001472 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 goto out;
1474
Daniel Walterc3968a82011-04-13 21:10:57 +00001475 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1476 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1477 err = -EINVAL;
1478 goto out;
1479 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001480 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001481 rt->rt6i_prefsrc.plen = 128;
1482 } else
1483 rt->rt6i_prefsrc.plen = 0;
1484
Thomas Graf86872cb2006-08-22 00:01:08 -07001485 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001486 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001487 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 }
1490
Thomas Graf86872cb2006-08-22 00:01:08 -07001491 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
1493install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001494 if (cfg->fc_mx) {
1495 struct nlattr *nla;
1496 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
Thomas Graf86872cb2006-08-22 00:01:08 -07001498 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001499 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001500
1501 if (type) {
1502 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 err = -EINVAL;
1504 goto out;
1505 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001506
David S. Millerdefb3512010-12-08 21:16:57 -08001507 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 }
1510 }
1511
Changli Gaod8d1f302010-06-10 23:31:35 -07001512 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001514 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001515
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001516 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001517
Thomas Graf86872cb2006-08-22 00:01:08 -07001518 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
1520out:
1521 if (dev)
1522 dev_put(dev);
1523 if (idev)
1524 in6_dev_put(idev);
1525 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001526 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 return err;
1528}
1529
Thomas Graf86872cb2006-08-22 00:01:08 -07001530static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531{
1532 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001533 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001534 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001536 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001537 return -ENOENT;
1538
Thomas Grafc71099a2006-08-04 23:20:06 -07001539 table = rt->rt6i_table;
1540 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
Thomas Graf86872cb2006-08-22 00:01:08 -07001542 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001543 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
Thomas Grafc71099a2006-08-04 23:20:06 -07001545 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
1547 return err;
1548}
1549
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001550int ip6_del_rt(struct rt6_info *rt)
1551{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001552 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001553 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001554 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001555 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001556}
1557
Thomas Graf86872cb2006-08-22 00:01:08 -07001558static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559{
Thomas Grafc71099a2006-08-04 23:20:06 -07001560 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 struct fib6_node *fn;
1562 struct rt6_info *rt;
1563 int err = -ESRCH;
1564
Daniel Lezcano55786892008-03-04 13:47:47 -08001565 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001566 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001567 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
Thomas Grafc71099a2006-08-04 23:20:06 -07001569 read_lock_bh(&table->tb6_lock);
1570
1571 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001572 &cfg->fc_dst, cfg->fc_dst_len,
1573 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001574
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001576 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001577 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001578 (!rt->dst.dev ||
1579 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001581 if (cfg->fc_flags & RTF_GATEWAY &&
1582 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001584 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001586 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001587 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
Thomas Graf86872cb2006-08-22 00:01:08 -07001589 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 }
1591 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001592 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593
1594 return err;
1595}
1596
1597/*
1598 * Handle redirects
1599 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001600struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001601 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001602 struct in6_addr gateway;
1603};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001605static struct rt6_info *__ip6_route_redirect(struct net *net,
1606 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001607 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001608 int flags)
1609{
David S. Miller4c9483b2011-03-12 16:22:43 -05001610 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001611 struct rt6_info *rt;
1612 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001613
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001615 * Get the "current" route for this destination and
1616 * check if the redirect has come from approriate router.
1617 *
1618 * RFC 2461 specifies that redirects should only be
1619 * accepted if they come from the nexthop to the target.
1620 * Due to the way the routes are chosen, this notion
1621 * is a bit fuzzy and one might need to check all possible
1622 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
Thomas Grafc71099a2006-08-04 23:20:06 -07001625 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001626 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001627restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001628 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001629 /*
1630 * Current route is on-link; redirect is always invalid.
1631 *
1632 * Seems, previous statement is not true. It could
1633 * be node, which looks for us as on-link (f.e. proxy ndisc)
1634 * But then router serving it might decide, that we should
1635 * know truth 8)8) --ANK (980726).
1636 */
1637 if (rt6_check_expired(rt))
1638 continue;
1639 if (!(rt->rt6i_flags & RTF_GATEWAY))
1640 continue;
David S. Millerd1918542011-12-28 20:19:20 -05001641 if (fl6->flowi6_oif != rt->dst.dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001642 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001643 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001644 continue;
1645 break;
1646 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001647
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001648 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001649 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001650 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001651out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001652 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001653
1654 read_unlock_bh(&table->tb6_lock);
1655
1656 return rt;
1657};
1658
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001659static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1660 const struct in6_addr *src,
1661 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001662 struct net_device *dev)
1663{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001664 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001665 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001666 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001667 .fl6 = {
1668 .flowi6_oif = dev->ifindex,
1669 .daddr = *dest,
1670 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001671 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001672 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001673
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001674 rdfl.gateway = *gateway;
Brian Haley86c36ce2009-10-07 13:58:01 -07001675
Thomas Grafadaa70b2006-10-13 15:01:03 -07001676 if (rt6_need_strict(dest))
1677 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001678
David S. Miller4c9483b2011-03-12 16:22:43 -05001679 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001680 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001681}
1682
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001683void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
1684 const struct in6_addr *saddr,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001685 struct neighbour *neigh, u8 *lladdr, int on_link)
1686{
1687 struct rt6_info *rt, *nrt = NULL;
1688 struct netevent_redirect netevent;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001689 struct net *net = dev_net(neigh->dev);
David S. Miller1d248b12012-07-03 01:01:51 -07001690 struct neighbour *old_neigh;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001691
1692 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1693
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001694 if (rt == net->ipv6.ip6_null_entry) {
Joe Perchese87cc472012-05-13 21:56:26 +00001695 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001696 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 }
1698
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 /*
1700 * We have finally decided to accept it.
1701 */
1702
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001703 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1705 NEIGH_UPDATE_F_OVERRIDE|
1706 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1707 NEIGH_UPDATE_F_ISROUTER))
1708 );
1709
1710 /*
1711 * Redirect received -> path was valid.
1712 * Look, redirects are sent only in response to data packets,
1713 * so that this nexthop apparently is reachable. --ANK
1714 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001715 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
1717 /* Duplicate redirect: silently ignore. */
David S. Miller1d248b12012-07-03 01:01:51 -07001718 old_neigh = dst_get_neighbour_noref_raw(&rt->dst);
1719 if (neigh == old_neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 goto out;
1721
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001722 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001723 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 goto out;
1725
1726 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1727 if (on_link)
1728 nrt->rt6i_flags &= ~RTF_GATEWAY;
1729
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001730 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller69cce1d2011-07-17 23:09:49 -07001731 dst_set_neighbour(&nrt->dst, neigh_clone(neigh));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732
Thomas Graf40e22e82006-08-22 00:00:45 -07001733 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 goto out;
1735
Changli Gaod8d1f302010-06-10 23:31:35 -07001736 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001737 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001738 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001739 netevent.new_neigh = neigh;
1740 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001741 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1742
David S. Miller38308472011-12-03 18:02:47 -05001743 if (rt->rt6i_flags & RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001744 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 return;
1746 }
1747
1748out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001749 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750}
1751
1752/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 * Misc support functions
1754 */
1755
Gao feng1716a962012-04-06 00:13:10 +00001756static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001757 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758{
David S. Millerd1918542011-12-28 20:19:20 -05001759 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001760 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1761 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762
1763 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001764 rt->dst.input = ort->dst.input;
1765 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001766 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001768 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001769 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001770 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001771 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 rt->rt6i_idev = ort->rt6i_idev;
1773 if (rt->rt6i_idev)
1774 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001775 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001777 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001778 rt->rt6i_flags = ort->rt6i_flags;
1779 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1780 (RTF_DEFAULT | RTF_ADDRCONF))
1781 rt6_set_from(rt, ort);
1782 else
1783 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 rt->rt6i_metric = 0;
1785
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786#ifdef CONFIG_IPV6_SUBTREES
1787 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1788#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001789 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001790 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 }
1792 return rt;
1793}
1794
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001795#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001796static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001797 const struct in6_addr *prefix, int prefixlen,
1798 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001799{
1800 struct fib6_node *fn;
1801 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001802 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001803
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001804 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001805 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001806 return NULL;
1807
1808 write_lock_bh(&table->tb6_lock);
1809 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001810 if (!fn)
1811 goto out;
1812
Changli Gaod8d1f302010-06-10 23:31:35 -07001813 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001814 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001815 continue;
1816 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1817 continue;
1818 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1819 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001820 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001821 break;
1822 }
1823out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001824 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001825 return rt;
1826}
1827
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001828static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001829 const struct in6_addr *prefix, int prefixlen,
1830 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001831 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001832{
Thomas Graf86872cb2006-08-22 00:01:08 -07001833 struct fib6_config cfg = {
1834 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001835 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001836 .fc_ifindex = ifindex,
1837 .fc_dst_len = prefixlen,
1838 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1839 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001840 .fc_nlinfo.pid = 0,
1841 .fc_nlinfo.nlh = NULL,
1842 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001843 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001844
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001845 cfg.fc_dst = *prefix;
1846 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001847
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001848 /* We should treat it as a default route if prefix length is 0. */
1849 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001850 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001851
Thomas Graf86872cb2006-08-22 00:01:08 -07001852 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001853
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001854 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001855}
1856#endif
1857
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001858struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001859{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001861 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001863 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001864 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001865 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866
Thomas Grafc71099a2006-08-04 23:20:06 -07001867 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001868 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001869 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001870 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1872 break;
1873 }
1874 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001875 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001876 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 return rt;
1878}
1879
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001880struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001881 struct net_device *dev,
1882 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883{
Thomas Graf86872cb2006-08-22 00:01:08 -07001884 struct fib6_config cfg = {
1885 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001886 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001887 .fc_ifindex = dev->ifindex,
1888 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1889 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001890 .fc_nlinfo.pid = 0,
1891 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001892 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001893 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001895 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
Thomas Graf86872cb2006-08-22 00:01:08 -07001897 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 return rt6_get_dflt_router(gwaddr, dev);
1900}
1901
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001902void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903{
1904 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001905 struct fib6_table *table;
1906
1907 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001908 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001909 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001910 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911
1912restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001913 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001914 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001916 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001917 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001918 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 goto restart;
1920 }
1921 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001922 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923}
1924
Daniel Lezcano55786892008-03-04 13:47:47 -08001925static void rtmsg_to_fib6_config(struct net *net,
1926 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001927 struct fib6_config *cfg)
1928{
1929 memset(cfg, 0, sizeof(*cfg));
1930
1931 cfg->fc_table = RT6_TABLE_MAIN;
1932 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1933 cfg->fc_metric = rtmsg->rtmsg_metric;
1934 cfg->fc_expires = rtmsg->rtmsg_info;
1935 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1936 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1937 cfg->fc_flags = rtmsg->rtmsg_flags;
1938
Daniel Lezcano55786892008-03-04 13:47:47 -08001939 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001940
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001941 cfg->fc_dst = rtmsg->rtmsg_dst;
1942 cfg->fc_src = rtmsg->rtmsg_src;
1943 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001944}
1945
Daniel Lezcano55786892008-03-04 13:47:47 -08001946int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947{
Thomas Graf86872cb2006-08-22 00:01:08 -07001948 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 struct in6_rtmsg rtmsg;
1950 int err;
1951
1952 switch(cmd) {
1953 case SIOCADDRT: /* Add a route */
1954 case SIOCDELRT: /* Delete a route */
1955 if (!capable(CAP_NET_ADMIN))
1956 return -EPERM;
1957 err = copy_from_user(&rtmsg, arg,
1958 sizeof(struct in6_rtmsg));
1959 if (err)
1960 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001961
Daniel Lezcano55786892008-03-04 13:47:47 -08001962 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001963
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 rtnl_lock();
1965 switch (cmd) {
1966 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001967 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968 break;
1969 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001970 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 break;
1972 default:
1973 err = -EINVAL;
1974 }
1975 rtnl_unlock();
1976
1977 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979
1980 return -EINVAL;
1981}
1982
1983/*
1984 * Drop the packet on the floor
1985 */
1986
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07001987static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001989 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00001990 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001991 switch (ipstats_mib_noroutes) {
1992 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001993 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00001994 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07001995 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
1996 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001997 break;
1998 }
1999 /* FALLTHROUGH */
2000 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002001 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2002 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002003 break;
2004 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002005 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 kfree_skb(skb);
2007 return 0;
2008}
2009
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002010static int ip6_pkt_discard(struct sk_buff *skb)
2011{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002012 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002013}
2014
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002015static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016{
Eric Dumazetadf30902009-06-02 05:19:30 +00002017 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002018 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019}
2020
David S. Miller6723ab52006-10-18 21:20:57 -07002021#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2022
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002023static int ip6_pkt_prohibit(struct sk_buff *skb)
2024{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002025 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002026}
2027
2028static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2029{
Eric Dumazetadf30902009-06-02 05:19:30 +00002030 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002031 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002032}
2033
David S. Miller6723ab52006-10-18 21:20:57 -07002034#endif
2035
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036/*
2037 * Allocate a dst for local (unicast / anycast) address.
2038 */
2039
2040struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2041 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002042 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002044 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002045 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002046 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047
David S. Miller38308472011-12-03 18:02:47 -05002048 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002049 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002051 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 in6_dev_hold(idev);
2054
David S. Miller11d53b42011-06-24 15:23:34 -07002055 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002056 rt->dst.input = ip6_input;
2057 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002059 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060
2061 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002062 if (anycast)
2063 rt->rt6i_flags |= RTF_ANYCAST;
2064 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002066 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002067 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002068 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002069 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 }
2071
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002072 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002074 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075
Changli Gaod8d1f302010-06-10 23:31:35 -07002076 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
2078 return rt;
2079}
2080
Daniel Walterc3968a82011-04-13 21:10:57 +00002081int ip6_route_get_saddr(struct net *net,
2082 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002083 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002084 unsigned int prefs,
2085 struct in6_addr *saddr)
2086{
2087 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2088 int err = 0;
2089 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002090 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002091 else
2092 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2093 daddr, prefs, saddr);
2094 return err;
2095}
2096
2097/* remove deleted ip from prefsrc entries */
2098struct arg_dev_net_ip {
2099 struct net_device *dev;
2100 struct net *net;
2101 struct in6_addr *addr;
2102};
2103
2104static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2105{
2106 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2107 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2108 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2109
David S. Millerd1918542011-12-28 20:19:20 -05002110 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002111 rt != net->ipv6.ip6_null_entry &&
2112 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2113 /* remove prefsrc entry */
2114 rt->rt6i_prefsrc.plen = 0;
2115 }
2116 return 0;
2117}
2118
2119void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2120{
2121 struct net *net = dev_net(ifp->idev->dev);
2122 struct arg_dev_net_ip adni = {
2123 .dev = ifp->idev->dev,
2124 .net = net,
2125 .addr = &ifp->addr,
2126 };
2127 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2128}
2129
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002130struct arg_dev_net {
2131 struct net_device *dev;
2132 struct net *net;
2133};
2134
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135static int fib6_ifdown(struct rt6_info *rt, void *arg)
2136{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002137 const struct arg_dev_net *adn = arg;
2138 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002139
David S. Millerd1918542011-12-28 20:19:20 -05002140 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002141 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002143
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 return 0;
2145}
2146
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002147void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002149 struct arg_dev_net adn = {
2150 .dev = dev,
2151 .net = net,
2152 };
2153
2154 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002155 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156}
2157
Eric Dumazet95c96172012-04-15 05:58:06 +00002158struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002160 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161};
2162
2163static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2164{
2165 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2166 struct inet6_dev *idev;
2167
2168 /* In IPv6 pmtu discovery is not optional,
2169 so that RTAX_MTU lock cannot disable it.
2170 We still use this lock to block changes
2171 caused by addrconf/ndisc.
2172 */
2173
2174 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002175 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 return 0;
2177
2178 /* For administrative MTU increase, there is no way to discover
2179 IPv6 PMTU increase, so PMTU increase should be updated here.
2180 Since RFC 1981 doesn't include administrative MTU increase
2181 update PMTU increase is a MUST. (i.e. jumbo frame)
2182 */
2183 /*
2184 If new MTU is less than route PMTU, this new MTU will be the
2185 lowest MTU in the path, update the route PMTU to reflect PMTU
2186 decreases; if new MTU is greater than route PMTU, and the
2187 old MTU is the lowest MTU in the path, update the route PMTU
2188 to reflect the increase. In this case if the other nodes' MTU
2189 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2190 PMTU discouvery.
2191 */
David S. Millerd1918542011-12-28 20:19:20 -05002192 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002193 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2194 (dst_mtu(&rt->dst) >= arg->mtu ||
2195 (dst_mtu(&rt->dst) < arg->mtu &&
2196 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002197 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002198 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 return 0;
2200}
2201
Eric Dumazet95c96172012-04-15 05:58:06 +00002202void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203{
Thomas Grafc71099a2006-08-04 23:20:06 -07002204 struct rt6_mtu_change_arg arg = {
2205 .dev = dev,
2206 .mtu = mtu,
2207 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002209 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210}
2211
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002212static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002213 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002214 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002215 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002216 [RTA_PRIORITY] = { .type = NLA_U32 },
2217 [RTA_METRICS] = { .type = NLA_NESTED },
2218};
2219
2220static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2221 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222{
Thomas Graf86872cb2006-08-22 00:01:08 -07002223 struct rtmsg *rtm;
2224 struct nlattr *tb[RTA_MAX+1];
2225 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226
Thomas Graf86872cb2006-08-22 00:01:08 -07002227 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2228 if (err < 0)
2229 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230
Thomas Graf86872cb2006-08-22 00:01:08 -07002231 err = -EINVAL;
2232 rtm = nlmsg_data(nlh);
2233 memset(cfg, 0, sizeof(*cfg));
2234
2235 cfg->fc_table = rtm->rtm_table;
2236 cfg->fc_dst_len = rtm->rtm_dst_len;
2237 cfg->fc_src_len = rtm->rtm_src_len;
2238 cfg->fc_flags = RTF_UP;
2239 cfg->fc_protocol = rtm->rtm_protocol;
2240
2241 if (rtm->rtm_type == RTN_UNREACHABLE)
2242 cfg->fc_flags |= RTF_REJECT;
2243
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002244 if (rtm->rtm_type == RTN_LOCAL)
2245 cfg->fc_flags |= RTF_LOCAL;
2246
Thomas Graf86872cb2006-08-22 00:01:08 -07002247 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2248 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002249 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002250
2251 if (tb[RTA_GATEWAY]) {
2252 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2253 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002255
2256 if (tb[RTA_DST]) {
2257 int plen = (rtm->rtm_dst_len + 7) >> 3;
2258
2259 if (nla_len(tb[RTA_DST]) < plen)
2260 goto errout;
2261
2262 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002264
2265 if (tb[RTA_SRC]) {
2266 int plen = (rtm->rtm_src_len + 7) >> 3;
2267
2268 if (nla_len(tb[RTA_SRC]) < plen)
2269 goto errout;
2270
2271 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002273
Daniel Walterc3968a82011-04-13 21:10:57 +00002274 if (tb[RTA_PREFSRC])
2275 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2276
Thomas Graf86872cb2006-08-22 00:01:08 -07002277 if (tb[RTA_OIF])
2278 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2279
2280 if (tb[RTA_PRIORITY])
2281 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2282
2283 if (tb[RTA_METRICS]) {
2284 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2285 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002287
2288 if (tb[RTA_TABLE])
2289 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2290
2291 err = 0;
2292errout:
2293 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294}
2295
Thomas Grafc127ea22007-03-22 11:58:32 -07002296static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297{
Thomas Graf86872cb2006-08-22 00:01:08 -07002298 struct fib6_config cfg;
2299 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300
Thomas Graf86872cb2006-08-22 00:01:08 -07002301 err = rtm_to_fib6_config(skb, nlh, &cfg);
2302 if (err < 0)
2303 return err;
2304
2305 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306}
2307
Thomas Grafc127ea22007-03-22 11:58:32 -07002308static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309{
Thomas Graf86872cb2006-08-22 00:01:08 -07002310 struct fib6_config cfg;
2311 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312
Thomas Graf86872cb2006-08-22 00:01:08 -07002313 err = rtm_to_fib6_config(skb, nlh, &cfg);
2314 if (err < 0)
2315 return err;
2316
2317 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318}
2319
Thomas Graf339bf982006-11-10 14:10:15 -08002320static inline size_t rt6_nlmsg_size(void)
2321{
2322 return NLMSG_ALIGN(sizeof(struct rtmsg))
2323 + nla_total_size(16) /* RTA_SRC */
2324 + nla_total_size(16) /* RTA_DST */
2325 + nla_total_size(16) /* RTA_GATEWAY */
2326 + nla_total_size(16) /* RTA_PREFSRC */
2327 + nla_total_size(4) /* RTA_TABLE */
2328 + nla_total_size(4) /* RTA_IIF */
2329 + nla_total_size(4) /* RTA_OIF */
2330 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002331 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002332 + nla_total_size(sizeof(struct rta_cacheinfo));
2333}
2334
Brian Haley191cd582008-08-14 15:33:21 -07002335static int rt6_fill_node(struct net *net,
2336 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002337 struct in6_addr *dst, struct in6_addr *src,
2338 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002339 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340{
David S. Miller346f8702011-12-29 15:22:33 -05002341 const struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002343 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002344 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002345 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002346 struct neighbour *n;
David S. Miller346f8702011-12-29 15:22:33 -05002347 u32 ts, tsage;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348
2349 if (prefix) { /* user wants prefix routes only */
2350 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2351 /* success since this is not a prefix route */
2352 return 1;
2353 }
2354 }
2355
Thomas Graf2d7202b2006-08-22 00:01:27 -07002356 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002357 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002358 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002359
2360 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361 rtm->rtm_family = AF_INET6;
2362 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2363 rtm->rtm_src_len = rt->rt6i_src.plen;
2364 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002365 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002366 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002367 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002368 table = RT6_TABLE_UNSPEC;
2369 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002370 if (nla_put_u32(skb, RTA_TABLE, table))
2371 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002372 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002374 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002375 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002376 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 rtm->rtm_type = RTN_LOCAL;
2378 else
2379 rtm->rtm_type = RTN_UNICAST;
2380 rtm->rtm_flags = 0;
2381 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2382 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002383 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384 rtm->rtm_protocol = RTPROT_REDIRECT;
2385 else if (rt->rt6i_flags & RTF_ADDRCONF)
2386 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002387 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 rtm->rtm_protocol = RTPROT_RA;
2389
David S. Miller38308472011-12-03 18:02:47 -05002390 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002391 rtm->rtm_flags |= RTM_F_CLONED;
2392
2393 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002394 if (nla_put(skb, RTA_DST, 16, dst))
2395 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002396 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002398 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2399 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400#ifdef CONFIG_IPV6_SUBTREES
2401 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002402 if (nla_put(skb, RTA_SRC, 16, src))
2403 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002404 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002405 } else if (rtm->rtm_src_len &&
2406 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2407 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002409 if (iif) {
2410#ifdef CONFIG_IPV6_MROUTE
2411 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002412 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002413 if (err <= 0) {
2414 if (!nowait) {
2415 if (err == 0)
2416 return 0;
2417 goto nla_put_failure;
2418 } else {
2419 if (err == -EMSGSIZE)
2420 goto nla_put_failure;
2421 }
2422 }
2423 } else
2424#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002425 if (nla_put_u32(skb, RTA_IIF, iif))
2426 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002427 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002429 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2430 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2431 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002433
Daniel Walterc3968a82011-04-13 21:10:57 +00002434 if (rt->rt6i_prefsrc.plen) {
2435 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002436 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002437 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2438 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002439 }
2440
David S. Millerdefb3512010-12-08 21:16:57 -08002441 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002442 goto nla_put_failure;
2443
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002444 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +00002445 n = dst_get_neighbour_noref(&rt->dst);
Eric Dumazet94f826b2012-03-27 09:53:52 +00002446 if (n) {
2447 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2448 rcu_read_unlock();
2449 goto nla_put_failure;
2450 }
2451 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002452 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002453
David S. Millerc78679e2012-04-01 20:27:33 -04002454 if (rt->dst.dev &&
2455 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2456 goto nla_put_failure;
2457 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2458 goto nla_put_failure;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002459 if (!(rt->rt6i_flags & RTF_EXPIRES))
2460 expires = 0;
David S. Millerd1918542011-12-28 20:19:20 -05002461 else if (rt->dst.expires - jiffies < INT_MAX)
2462 expires = rt->dst.expires - jiffies;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002463 else
2464 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002465
David S. Miller97bab732012-06-09 22:36:36 -07002466 peer = NULL;
2467 if (rt6_has_peer(rt))
2468 peer = rt6_peer_ptr(rt);
David S. Miller346f8702011-12-29 15:22:33 -05002469 ts = tsage = 0;
2470 if (peer && peer->tcp_ts_stamp) {
2471 ts = peer->tcp_ts;
2472 tsage = get_seconds() - peer->tcp_ts_stamp;
2473 }
2474
2475 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, ts, tsage,
Changli Gaod8d1f302010-06-10 23:31:35 -07002476 expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002477 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478
Thomas Graf2d7202b2006-08-22 00:01:27 -07002479 return nlmsg_end(skb, nlh);
2480
2481nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002482 nlmsg_cancel(skb, nlh);
2483 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484}
2485
Patrick McHardy1b43af52006-08-10 23:11:17 -07002486int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487{
2488 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2489 int prefix;
2490
Thomas Graf2d7202b2006-08-22 00:01:27 -07002491 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2492 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2494 } else
2495 prefix = 0;
2496
Brian Haley191cd582008-08-14 15:33:21 -07002497 return rt6_fill_node(arg->net,
2498 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002500 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501}
2502
Thomas Grafc127ea22007-03-22 11:58:32 -07002503static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002505 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002506 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002508 struct sk_buff *skb;
2509 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002510 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002511 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002512
2513 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2514 if (err < 0)
2515 goto errout;
2516
2517 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002518 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002519
2520 if (tb[RTA_SRC]) {
2521 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2522 goto errout;
2523
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002524 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002525 }
2526
2527 if (tb[RTA_DST]) {
2528 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2529 goto errout;
2530
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002531 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002532 }
2533
2534 if (tb[RTA_IIF])
2535 iif = nla_get_u32(tb[RTA_IIF]);
2536
2537 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002538 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002539
2540 if (iif) {
2541 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002542 int flags = 0;
2543
Daniel Lezcano55786892008-03-04 13:47:47 -08002544 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002545 if (!dev) {
2546 err = -ENODEV;
2547 goto errout;
2548 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002549
2550 fl6.flowi6_iif = iif;
2551
2552 if (!ipv6_addr_any(&fl6.saddr))
2553 flags |= RT6_LOOKUP_F_HAS_SADDR;
2554
2555 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2556 flags);
2557 } else {
2558 fl6.flowi6_oif = oif;
2559
2560 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002561 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562
2563 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002564 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002565 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002566 err = -ENOBUFS;
2567 goto errout;
2568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569
2570 /* Reserve room for dummy headers, this skb can pass
2571 through good chunk of routing engine.
2572 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002573 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2575
Changli Gaod8d1f302010-06-10 23:31:35 -07002576 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577
David S. Miller4c9483b2011-03-12 16:22:43 -05002578 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002580 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002582 kfree_skb(skb);
2583 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584 }
2585
Daniel Lezcano55786892008-03-04 13:47:47 -08002586 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002587errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589}
2590
Thomas Graf86872cb2006-08-22 00:01:08 -07002591void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592{
2593 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002594 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002595 u32 seq;
2596 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002598 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002599 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002600
Thomas Graf339bf982006-11-10 14:10:15 -08002601 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002602 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002603 goto errout;
2604
Brian Haley191cd582008-08-14 15:33:21 -07002605 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002606 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002607 if (err < 0) {
2608 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2609 WARN_ON(err == -EMSGSIZE);
2610 kfree_skb(skb);
2611 goto errout;
2612 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002613 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2614 info->nlh, gfp_any());
2615 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002616errout:
2617 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002618 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619}
2620
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002621static int ip6_route_dev_notify(struct notifier_block *this,
2622 unsigned long event, void *data)
2623{
2624 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002625 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002626
2627 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002628 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002629 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2630#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002631 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002632 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002633 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002634 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2635#endif
2636 }
2637
2638 return NOTIFY_OK;
2639}
2640
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641/*
2642 * /proc
2643 */
2644
2645#ifdef CONFIG_PROC_FS
2646
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647struct rt6_proc_arg
2648{
2649 char *buffer;
2650 int offset;
2651 int length;
2652 int skip;
2653 int len;
2654};
2655
2656static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2657{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002658 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002659 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002661 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662
2663#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002664 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002666 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002668 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +00002669 n = dst_get_neighbour_noref(&rt->dst);
David S. Miller69cce1d2011-07-17 23:09:49 -07002670 if (n) {
2671 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002673 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002674 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002675 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002676 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002677 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2678 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002679 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 return 0;
2681}
2682
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002683static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002685 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002686 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002687 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688}
2689
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002690static int ipv6_route_open(struct inode *inode, struct file *file)
2691{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002692 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002693}
2694
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002695static const struct file_operations ipv6_route_proc_fops = {
2696 .owner = THIS_MODULE,
2697 .open = ipv6_route_open,
2698 .read = seq_read,
2699 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002700 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002701};
2702
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2704{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002705 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002707 net->ipv6.rt6_stats->fib_nodes,
2708 net->ipv6.rt6_stats->fib_route_nodes,
2709 net->ipv6.rt6_stats->fib_rt_alloc,
2710 net->ipv6.rt6_stats->fib_rt_entries,
2711 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002712 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002713 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714
2715 return 0;
2716}
2717
2718static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2719{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002720 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002721}
2722
Arjan van de Ven9a321442007-02-12 00:55:35 -08002723static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724 .owner = THIS_MODULE,
2725 .open = rt6_stats_seq_open,
2726 .read = seq_read,
2727 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002728 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729};
2730#endif /* CONFIG_PROC_FS */
2731
2732#ifdef CONFIG_SYSCTL
2733
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002735int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 void __user *buffer, size_t *lenp, loff_t *ppos)
2737{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002738 struct net *net;
2739 int delay;
2740 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002742
2743 net = (struct net *)ctl->extra1;
2744 delay = net->ipv6.sysctl.flush_delay;
2745 proc_dointvec(ctl, write, buffer, lenp, ppos);
2746 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2747 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748}
2749
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002750ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002751 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002753 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002755 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002756 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 },
2758 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002760 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 .maxlen = sizeof(int),
2762 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002763 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 },
2765 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002767 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 .maxlen = sizeof(int),
2769 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002770 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 },
2772 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002774 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 .maxlen = sizeof(int),
2776 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002777 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 },
2779 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002781 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 .maxlen = sizeof(int),
2783 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002784 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 },
2786 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002788 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 .maxlen = sizeof(int),
2790 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002791 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 },
2793 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002795 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 .maxlen = sizeof(int),
2797 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002798 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 },
2800 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002802 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 .maxlen = sizeof(int),
2804 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002805 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 },
2807 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002809 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 .maxlen = sizeof(int),
2811 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002812 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 },
2814 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002816 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817 .maxlen = sizeof(int),
2818 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002819 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002821 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822};
2823
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002824struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002825{
2826 struct ctl_table *table;
2827
2828 table = kmemdup(ipv6_route_table_template,
2829 sizeof(ipv6_route_table_template),
2830 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002831
2832 if (table) {
2833 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002834 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002835 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002836 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2837 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2838 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2839 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2840 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2841 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2842 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002843 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002844 }
2845
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002846 return table;
2847}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848#endif
2849
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002850static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002851{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002852 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002853
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002854 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2855 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002856
Eric Dumazetfc66f952010-10-08 06:37:34 +00002857 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2858 goto out_ip6_dst_ops;
2859
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002860 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2861 sizeof(*net->ipv6.ip6_null_entry),
2862 GFP_KERNEL);
2863 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002864 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002865 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002866 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002867 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002868 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2869 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002870
2871#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2872 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2873 sizeof(*net->ipv6.ip6_prohibit_entry),
2874 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002875 if (!net->ipv6.ip6_prohibit_entry)
2876 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002877 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002878 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002879 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002880 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2881 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002882
2883 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2884 sizeof(*net->ipv6.ip6_blk_hole_entry),
2885 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002886 if (!net->ipv6.ip6_blk_hole_entry)
2887 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002888 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002889 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002890 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002891 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2892 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002893#endif
2894
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002895 net->ipv6.sysctl.flush_delay = 0;
2896 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2897 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2898 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2899 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2900 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2901 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2902 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2903
Benjamin Thery6891a342008-03-04 13:49:47 -08002904 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2905
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002906 ret = 0;
2907out:
2908 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002909
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002910#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2911out_ip6_prohibit_entry:
2912 kfree(net->ipv6.ip6_prohibit_entry);
2913out_ip6_null_entry:
2914 kfree(net->ipv6.ip6_null_entry);
2915#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002916out_ip6_dst_entries:
2917 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002918out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002919 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002920}
2921
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002922static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002923{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002924 kfree(net->ipv6.ip6_null_entry);
2925#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2926 kfree(net->ipv6.ip6_prohibit_entry);
2927 kfree(net->ipv6.ip6_blk_hole_entry);
2928#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002929 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002930}
2931
Thomas Grafd1896342012-06-18 12:08:33 +00002932static int __net_init ip6_route_net_init_late(struct net *net)
2933{
2934#ifdef CONFIG_PROC_FS
2935 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2936 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2937#endif
2938 return 0;
2939}
2940
2941static void __net_exit ip6_route_net_exit_late(struct net *net)
2942{
2943#ifdef CONFIG_PROC_FS
2944 proc_net_remove(net, "ipv6_route");
2945 proc_net_remove(net, "rt6_stats");
2946#endif
2947}
2948
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002949static struct pernet_operations ip6_route_net_ops = {
2950 .init = ip6_route_net_init,
2951 .exit = ip6_route_net_exit,
2952};
2953
David S. Millerc3426b42012-06-09 16:27:05 -07002954static int __net_init ipv6_inetpeer_init(struct net *net)
2955{
2956 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2957
2958 if (!bp)
2959 return -ENOMEM;
2960 inet_peer_base_init(bp);
2961 net->ipv6.peers = bp;
2962 return 0;
2963}
2964
2965static void __net_exit ipv6_inetpeer_exit(struct net *net)
2966{
2967 struct inet_peer_base *bp = net->ipv6.peers;
2968
2969 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07002970 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07002971 kfree(bp);
2972}
2973
David S. Miller2b823f72012-06-09 19:00:16 -07002974static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07002975 .init = ipv6_inetpeer_init,
2976 .exit = ipv6_inetpeer_exit,
2977};
2978
Thomas Grafd1896342012-06-18 12:08:33 +00002979static struct pernet_operations ip6_route_net_late_ops = {
2980 .init = ip6_route_net_init_late,
2981 .exit = ip6_route_net_exit_late,
2982};
2983
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002984static struct notifier_block ip6_route_dev_notifier = {
2985 .notifier_call = ip6_route_dev_notify,
2986 .priority = 0,
2987};
2988
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002989int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002991 int ret;
2992
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002993 ret = -ENOMEM;
2994 ip6_dst_ops_template.kmem_cachep =
2995 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
2996 SLAB_HWCACHE_ALIGN, NULL);
2997 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08002998 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07002999
Eric Dumazetfc66f952010-10-08 06:37:34 +00003000 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003001 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003002 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003003
David S. Millerc3426b42012-06-09 16:27:05 -07003004 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3005 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003006 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003007
David S. Miller7e52b332012-06-15 15:51:55 -07003008 ret = register_pernet_subsys(&ip6_route_net_ops);
3009 if (ret)
3010 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003011
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003012 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3013
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003014 /* Registering of the loopback is done before this portion of code,
3015 * the loopback reference in rt6_info will not be taken, do it
3016 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003017 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003018 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3019 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003020 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003021 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003022 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003023 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3024 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003025 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003026 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003027 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003028
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003029 ret = xfrm6_init();
3030 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003031 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003032
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003033 ret = fib6_rules_init();
3034 if (ret)
3035 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003036
Thomas Grafd1896342012-06-18 12:08:33 +00003037 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3038 if (ret)
3039 goto fib6_rules_init;
3040
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003041 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003042 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3043 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3044 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003045 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003046
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003047 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003048 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003049 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003050
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003051out:
3052 return ret;
3053
Thomas Grafd1896342012-06-18 12:08:33 +00003054out_register_late_subsys:
3055 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003056fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003057 fib6_rules_cleanup();
3058xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003059 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003060out_fib6_init:
3061 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003062out_register_subsys:
3063 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003064out_register_inetpeer:
3065 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003066out_dst_entries:
3067 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003068out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003069 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003070 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003071}
3072
3073void ip6_route_cleanup(void)
3074{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003075 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003076 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003077 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003079 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003080 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003081 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003082 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003083 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084}