blob: b7eb51e1a0e1c88ea447354056c1a5de4f3aed47 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020047#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <net/snmp.h>
49#include <net/ipv6.h>
50#include <net/ip6_fib.h>
51#include <net/ip6_route.h>
52#include <net/ndisc.h>
53#include <net/addrconf.h>
54#include <net/tcp.h>
55#include <linux/rtnetlink.h>
56#include <net/dst.h>
57#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070058#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070059#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61#include <asm/uaccess.h>
62
63#ifdef CONFIG_SYSCTL
64#include <linux/sysctl.h>
65#endif
66
Gao feng1716a962012-04-06 00:13:10 +000067static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +000068 const struct in6_addr *dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080070static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000071static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072static struct dst_entry *ip6_negative_advice(struct dst_entry *);
73static void ip6_dst_destroy(struct dst_entry *);
74static void ip6_dst_ifdown(struct dst_entry *,
75 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080076static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78static int ip6_pkt_discard(struct sk_buff *skb);
79static int ip6_pkt_discard_out(struct sk_buff *skb);
80static void ip6_link_failure(struct sk_buff *skb);
81static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
82
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080083#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080084static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000085 const struct in6_addr *prefix, int prefixlen,
86 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +000087 unsigned int pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080088static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000089 const struct in6_addr *prefix, int prefixlen,
90 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080091#endif
92
David S. Miller06582542011-01-27 14:58:42 -080093static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
94{
95 struct rt6_info *rt = (struct rt6_info *) dst;
96 struct inet_peer *peer;
97 u32 *p = NULL;
98
Yan, Zheng8e2ec632011-09-05 21:34:30 +000099 if (!(rt->dst.flags & DST_HOST))
100 return NULL;
101
David S. Millerfbfe95a2012-06-08 23:24:18 -0700102 peer = rt6_get_peer_create(rt);
David S. Miller06582542011-01-27 14:58:42 -0800103 if (peer) {
104 u32 *old_p = __DST_METRICS_PTR(old);
105 unsigned long prev, new;
106
107 p = peer->metrics;
108 if (inet_metrics_new(peer))
109 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
110
111 new = (unsigned long) p;
112 prev = cmpxchg(&dst->_metrics, old, new);
113
114 if (prev != old) {
115 p = __DST_METRICS_PTR(prev);
116 if (prev & DST_METRICS_READ_ONLY)
117 p = NULL;
118 }
119 }
120 return p;
121}
122
David S. Millerf894cbf2012-07-02 21:52:24 -0700123static inline const void *choose_neigh_daddr(struct rt6_info *rt,
124 struct sk_buff *skb,
125 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500126{
127 struct in6_addr *p = &rt->rt6i_gateway;
128
David S. Millera7563f32012-01-26 16:29:16 -0500129 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500130 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700131 else if (skb)
132 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500133 return daddr;
134}
135
David S. Millerf894cbf2012-07-02 21:52:24 -0700136static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
137 struct sk_buff *skb,
138 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700139{
David S. Miller39232972012-01-26 15:22:32 -0500140 struct rt6_info *rt = (struct rt6_info *) dst;
141 struct neighbour *n;
142
David S. Millerf894cbf2012-07-02 21:52:24 -0700143 daddr = choose_neigh_daddr(rt, skb, daddr);
David S. Miller39232972012-01-26 15:22:32 -0500144 n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500145 if (n)
146 return n;
147 return neigh_create(&nd_tbl, daddr, dst->dev);
148}
149
David S. Miller8ade06c2011-12-29 18:51:57 -0500150static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev)
David S. Millerf83c7792011-12-28 15:41:23 -0500151{
David S. Miller8ade06c2011-12-29 18:51:57 -0500152 struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway);
153 if (!n) {
154 n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev);
155 if (IS_ERR(n))
156 return PTR_ERR(n);
157 }
David S. Miller97cac082012-07-02 22:43:47 -0700158 rt->n = n;
David S. Millerf83c7792011-12-28 15:41:23 -0500159
160 return 0;
David S. Millerd3aaeb32011-07-18 00:40:17 -0700161}
162
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800163static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800165 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 .gc = ip6_dst_gc,
167 .gc_thresh = 1024,
168 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800169 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000170 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800171 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .destroy = ip6_dst_destroy,
173 .ifdown = ip6_dst_ifdown,
174 .negative_advice = ip6_negative_advice,
175 .link_failure = ip6_link_failure,
176 .update_pmtu = ip6_rt_update_pmtu,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700177 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700178 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179};
180
Steffen Klassertebb762f2011-11-23 02:12:51 +0000181static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800182{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000183 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
184
185 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800186}
187
David S. Miller14e50e52007-05-24 18:17:54 -0700188static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
189{
190}
191
Held Bernhard0972ddb2011-04-24 22:07:32 +0000192static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
193 unsigned long old)
194{
195 return NULL;
196}
197
David S. Miller14e50e52007-05-24 18:17:54 -0700198static struct dst_ops ip6_dst_blackhole_ops = {
199 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800200 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700201 .destroy = ip6_dst_destroy,
202 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000203 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800204 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700205 .update_pmtu = ip6_rt_blackhole_update_pmtu,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000206 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700207 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700208};
209
David S. Miller62fa8a82011-01-26 20:51:05 -0800210static const u32 ip6_template_metrics[RTAX_MAX] = {
211 [RTAX_HOPLIMIT - 1] = 255,
212};
213
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800214static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700215 .dst = {
216 .__refcnt = ATOMIC_INIT(1),
217 .__use = 1,
218 .obsolete = -1,
219 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700220 .input = ip6_pkt_discard,
221 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 },
223 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700224 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 .rt6i_metric = ~(u32) 0,
226 .rt6i_ref = ATOMIC_INIT(1),
227};
228
Thomas Graf101367c2006-08-04 03:39:02 -0700229#ifdef CONFIG_IPV6_MULTIPLE_TABLES
230
David S. Miller6723ab52006-10-18 21:20:57 -0700231static int ip6_pkt_prohibit(struct sk_buff *skb);
232static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700233
Adrian Bunk280a34c2008-04-21 02:29:32 -0700234static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700235 .dst = {
236 .__refcnt = ATOMIC_INIT(1),
237 .__use = 1,
238 .obsolete = -1,
239 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700240 .input = ip6_pkt_prohibit,
241 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700242 },
243 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700244 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700245 .rt6i_metric = ~(u32) 0,
246 .rt6i_ref = ATOMIC_INIT(1),
247};
248
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800249static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700250 .dst = {
251 .__refcnt = ATOMIC_INIT(1),
252 .__use = 1,
253 .obsolete = -1,
254 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700255 .input = dst_discard,
256 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700257 },
258 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700259 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700260 .rt6i_metric = ~(u32) 0,
261 .rt6i_ref = ATOMIC_INIT(1),
262};
263
264#endif
265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266/* allocate dst with ip6_dst_ops */
David S. Miller97bab732012-06-09 22:36:36 -0700267static inline struct rt6_info *ip6_dst_alloc(struct net *net,
David S. Miller957c6652011-06-24 15:25:00 -0700268 struct net_device *dev,
David S. Miller8b96d222012-06-11 02:01:56 -0700269 int flags,
270 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
David S. Miller97bab732012-06-09 22:36:36 -0700272 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
273 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700274
David S. Miller97bab732012-06-09 22:36:36 -0700275 if (rt) {
Steffen Klasserta2de86f2012-07-05 03:18:28 +0000276 memset(&rt->n, 0,
David S. Miller38308472011-12-03 18:02:47 -0500277 sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller8b96d222012-06-11 02:01:56 -0700278 rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
David S. Miller97bab732012-06-09 22:36:36 -0700279 }
David S. Millercf911662011-04-28 14:31:47 -0700280 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281}
282
283static void ip6_dst_destroy(struct dst_entry *dst)
284{
285 struct rt6_info *rt = (struct rt6_info *)dst;
286 struct inet6_dev *idev = rt->rt6i_idev;
287
David S. Miller97cac082012-07-02 22:43:47 -0700288 if (rt->n)
289 neigh_release(rt->n);
290
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000291 if (!(rt->dst.flags & DST_HOST))
292 dst_destroy_metrics_generic(dst);
293
David S. Miller38308472011-12-03 18:02:47 -0500294 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 rt->rt6i_idev = NULL;
296 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900297 }
Gao feng1716a962012-04-06 00:13:10 +0000298
299 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from)
300 dst_release(dst->from);
301
David S. Miller97bab732012-06-09 22:36:36 -0700302 if (rt6_has_peer(rt)) {
303 struct inet_peer *peer = rt6_peer_ptr(rt);
David S. Millerb3419362010-11-30 12:27:11 -0800304 inet_putpeer(peer);
305 }
306}
307
David S. Miller6431cbc2011-02-07 20:38:06 -0800308static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
309
310static u32 rt6_peer_genid(void)
311{
312 return atomic_read(&__rt6_peer_genid);
313}
314
David S. Millerb3419362010-11-30 12:27:11 -0800315void rt6_bind_peer(struct rt6_info *rt, int create)
316{
David S. Miller97bab732012-06-09 22:36:36 -0700317 struct inet_peer_base *base;
David S. Millerb3419362010-11-30 12:27:11 -0800318 struct inet_peer *peer;
319
David S. Miller97bab732012-06-09 22:36:36 -0700320 base = inetpeer_base_ptr(rt->_rt6i_peer);
321 if (!base)
322 return;
323
324 peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create);
David S. Miller7b34ca22012-06-11 04:13:57 -0700325 if (peer) {
326 if (!rt6_set_peer(rt, peer))
327 inet_putpeer(peer);
328 else
329 rt->rt6i_peer_genid = rt6_peer_genid();
330 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331}
332
333static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
334 int how)
335{
336 struct rt6_info *rt = (struct rt6_info *)dst;
337 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800338 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900339 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
David S. Miller97cac082012-07-02 22:43:47 -0700341 if (dev != loopback_dev) {
342 if (idev && idev->dev == dev) {
343 struct inet6_dev *loopback_idev =
344 in6_dev_get(loopback_dev);
345 if (loopback_idev) {
346 rt->rt6i_idev = loopback_idev;
347 in6_dev_put(idev);
348 }
349 }
350 if (rt->n && rt->n->dev == dev) {
351 rt->n->dev = loopback_dev;
352 dev_hold(loopback_dev);
353 dev_put(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 }
355 }
356}
357
Eric Dumazeta50feda2012-05-18 18:57:34 +0000358static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359{
Gao feng1716a962012-04-06 00:13:10 +0000360 struct rt6_info *ort = NULL;
361
362 if (rt->rt6i_flags & RTF_EXPIRES) {
363 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000364 return true;
Gao feng1716a962012-04-06 00:13:10 +0000365 } else if (rt->dst.from) {
366 ort = (struct rt6_info *) rt->dst.from;
367 return (ort->rt6i_flags & RTF_EXPIRES) &&
368 time_after(jiffies, ort->dst.expires);
369 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000370 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371}
372
Eric Dumazeta50feda2012-05-18 18:57:34 +0000373static bool rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700374{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000375 return ipv6_addr_type(daddr) &
376 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700377}
378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700380 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 */
382
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800383static inline struct rt6_info *rt6_device_match(struct net *net,
384 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000385 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700387 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388{
389 struct rt6_info *local = NULL;
390 struct rt6_info *sprt;
391
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900392 if (!oif && ipv6_addr_any(saddr))
393 goto out;
394
Changli Gaod8d1f302010-06-10 23:31:35 -0700395 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -0500396 struct net_device *dev = sprt->dst.dev;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900397
398 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 if (dev->ifindex == oif)
400 return sprt;
401 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500402 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700404 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900406 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 local->rt6i_idev->dev->ifindex == oif))
408 continue;
409 }
410 local = sprt;
411 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900412 } else {
413 if (ipv6_chk_addr(net, saddr, dev,
414 flags & RT6_LOOKUP_F_IFACE))
415 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900419 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 if (local)
421 return local;
422
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700423 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800424 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900426out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 return rt;
428}
429
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800430#ifdef CONFIG_IPV6_ROUTER_PREF
431static void rt6_probe(struct rt6_info *rt)
432{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000433 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800434 /*
435 * Okay, this does not seem to be appropriate
436 * for now, however, we need to check if it
437 * is really so; aka Router Reachability Probing.
438 *
439 * Router Reachability Probe MUST be rate-limited
440 * to no more than one per minute.
441 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000442 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700443 neigh = rt ? rt->n : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800444 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000445 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800446 read_lock_bh(&neigh->lock);
447 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e163562006-03-20 17:05:47 -0800448 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800449 struct in6_addr mcaddr;
450 struct in6_addr *target;
451
452 neigh->updated = jiffies;
453 read_unlock_bh(&neigh->lock);
454
455 target = (struct in6_addr *)&neigh->primary_key;
456 addrconf_addr_solict_mult(target, &mcaddr);
David S. Millerd1918542011-12-28 20:19:20 -0500457 ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000458 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800459 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000460 }
461out:
462 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800463}
464#else
465static inline void rt6_probe(struct rt6_info *rt)
466{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800467}
468#endif
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800471 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700473static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
David S. Millerd1918542011-12-28 20:19:20 -0500475 struct net_device *dev = rt->dst.dev;
David S. Miller161980f2007-04-06 11:42:27 -0700476 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800477 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700478 if ((dev->flags & IFF_LOOPBACK) &&
479 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
480 return 1;
481 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482}
483
Dave Jonesb6f99a22007-03-22 12:27:49 -0700484static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000486 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800487 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000488
489 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -0700490 neigh = rt->n;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700491 if (rt->rt6i_flags & RTF_NONEXTHOP ||
492 !(rt->rt6i_flags & RTF_GATEWAY))
493 m = 1;
494 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800495 read_lock_bh(&neigh->lock);
496 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700497 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800498#ifdef CONFIG_IPV6_ROUTER_PREF
499 else if (neigh->nud_state & NUD_FAILED)
500 m = 0;
501#endif
502 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800503 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800504 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800505 } else
506 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000507 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800508 return m;
509}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800511static int rt6_score_route(struct rt6_info *rt, int oif,
512 int strict)
513{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700514 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900515
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700516 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700517 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800518 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800519#ifdef CONFIG_IPV6_ROUTER_PREF
520 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
521#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700522 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800523 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800524 return -1;
525 return m;
526}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
David S. Millerf11e6652007-03-24 20:36:25 -0700528static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
529 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800530{
David S. Millerf11e6652007-03-24 20:36:25 -0700531 int m;
532
533 if (rt6_check_expired(rt))
534 goto out;
535
536 m = rt6_score_route(rt, oif, strict);
537 if (m < 0)
538 goto out;
539
540 if (m > *mpri) {
541 if (strict & RT6_LOOKUP_F_REACHABLE)
542 rt6_probe(match);
543 *mpri = m;
544 match = rt;
545 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
546 rt6_probe(rt);
547 }
548
549out:
550 return match;
551}
552
553static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
554 struct rt6_info *rr_head,
555 u32 metric, int oif, int strict)
556{
557 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800558 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
David S. Millerf11e6652007-03-24 20:36:25 -0700560 match = NULL;
561 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700562 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700563 match = find_match(rt, oif, strict, &mpri, match);
564 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700565 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700566 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800567
David S. Millerf11e6652007-03-24 20:36:25 -0700568 return match;
569}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800570
David S. Millerf11e6652007-03-24 20:36:25 -0700571static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
572{
573 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800574 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
David S. Millerf11e6652007-03-24 20:36:25 -0700576 rt0 = fn->rr_ptr;
577 if (!rt0)
578 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
David S. Millerf11e6652007-03-24 20:36:25 -0700580 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800582 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700583 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700584 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700585
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800586 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700587 if (!next || next->rt6i_metric != rt0->rt6i_metric)
588 next = fn->leaf;
589
590 if (next != rt0)
591 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 }
593
David S. Millerd1918542011-12-28 20:19:20 -0500594 net = dev_net(rt0->dst.dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000595 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596}
597
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800598#ifdef CONFIG_IPV6_ROUTE_INFO
599int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000600 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800601{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900602 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800603 struct route_info *rinfo = (struct route_info *) opt;
604 struct in6_addr prefix_buf, *prefix;
605 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900606 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800607 struct rt6_info *rt;
608
609 if (len < sizeof(struct route_info)) {
610 return -EINVAL;
611 }
612
613 /* Sanity check for prefix_len and length */
614 if (rinfo->length > 3) {
615 return -EINVAL;
616 } else if (rinfo->prefix_len > 128) {
617 return -EINVAL;
618 } else if (rinfo->prefix_len > 64) {
619 if (rinfo->length < 2) {
620 return -EINVAL;
621 }
622 } else if (rinfo->prefix_len > 0) {
623 if (rinfo->length < 1) {
624 return -EINVAL;
625 }
626 }
627
628 pref = rinfo->route_pref;
629 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000630 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800631
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900632 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800633
634 if (rinfo->length == 3)
635 prefix = (struct in6_addr *)rinfo->prefix;
636 else {
637 /* this function is safe */
638 ipv6_addr_prefix(&prefix_buf,
639 (struct in6_addr *)rinfo->prefix,
640 rinfo->prefix_len);
641 prefix = &prefix_buf;
642 }
643
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800644 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
645 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800646
647 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700648 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800649 rt = NULL;
650 }
651
652 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800653 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800654 pref);
655 else if (rt)
656 rt->rt6i_flags = RTF_ROUTEINFO |
657 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
658
659 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000660 if (!addrconf_finite_timeout(lifetime))
661 rt6_clean_expires(rt);
662 else
663 rt6_set_expires(rt, jiffies + HZ * lifetime);
664
Changli Gaod8d1f302010-06-10 23:31:35 -0700665 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800666 }
667 return 0;
668}
669#endif
670
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800671#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700672do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800673 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700674 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700675 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700676 if (fn->fn_flags & RTN_TL_ROOT) \
677 goto out; \
678 pn = fn->parent; \
679 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800680 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700681 else \
682 fn = pn; \
683 if (fn->fn_flags & RTN_RTINFO) \
684 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700685 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700686 } \
David S. Miller38308472011-12-03 18:02:47 -0500687} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700688
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800689static struct rt6_info *ip6_pol_route_lookup(struct net *net,
690 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500691 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
693 struct fib6_node *fn;
694 struct rt6_info *rt;
695
Thomas Grafc71099a2006-08-04 23:20:06 -0700696 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500697 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700698restart:
699 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500700 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
701 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700702out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700703 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700704 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700705 return rt;
706
707}
708
Florian Westphalea6e5742011-09-05 16:05:44 +0200709struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
710 int flags)
711{
712 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
713}
714EXPORT_SYMBOL_GPL(ip6_route_lookup);
715
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900716struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
717 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700718{
David S. Miller4c9483b2011-03-12 16:22:43 -0500719 struct flowi6 fl6 = {
720 .flowi6_oif = oif,
721 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700722 };
723 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700724 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700725
Thomas Grafadaa70b2006-10-13 15:01:03 -0700726 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500727 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700728 flags |= RT6_LOOKUP_F_HAS_SADDR;
729 }
730
David S. Miller4c9483b2011-03-12 16:22:43 -0500731 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700732 if (dst->error == 0)
733 return (struct rt6_info *) dst;
734
735 dst_release(dst);
736
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 return NULL;
738}
739
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900740EXPORT_SYMBOL(rt6_lookup);
741
Thomas Grafc71099a2006-08-04 23:20:06 -0700742/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 It takes new route entry, the addition fails by any reason the
744 route is freed. In any case, if caller does not hold it, it may
745 be destroyed.
746 */
747
Thomas Graf86872cb2006-08-22 00:01:08 -0700748static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749{
750 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700751 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Thomas Grafc71099a2006-08-04 23:20:06 -0700753 table = rt->rt6i_table;
754 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700755 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700756 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
758 return err;
759}
760
Thomas Graf40e22e82006-08-22 00:00:45 -0700761int ip6_ins_rt(struct rt6_info *rt)
762{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800763 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -0500764 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800765 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800766 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700767}
768
Gao feng1716a962012-04-06 00:13:10 +0000769static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000770 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000771 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 struct rt6_info *rt;
774
775 /*
776 * Clone the route.
777 */
778
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000779 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
781 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800782 int attempts = !in_softirq();
783
David S. Miller38308472011-12-03 18:02:47 -0500784 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500785 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000786 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900787 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000788 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900789 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793#ifdef CONFIG_IPV6_SUBTREES
794 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000795 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 rt->rt6i_src.plen = 128;
797 }
798#endif
799
David S. Miller14deae42009-01-04 16:04:39 -0800800 retry:
David S. Miller8ade06c2011-12-29 18:51:57 -0500801 if (rt6_bind_neighbour(rt, rt->dst.dev)) {
David S. Millerd1918542011-12-28 20:19:20 -0500802 struct net *net = dev_net(rt->dst.dev);
David S. Miller14deae42009-01-04 16:04:39 -0800803 int saved_rt_min_interval =
804 net->ipv6.sysctl.ip6_rt_gc_min_interval;
805 int saved_rt_elasticity =
806 net->ipv6.sysctl.ip6_rt_gc_elasticity;
807
808 if (attempts-- > 0) {
809 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
810 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
811
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000812 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800813
814 net->ipv6.sysctl.ip6_rt_gc_elasticity =
815 saved_rt_elasticity;
816 net->ipv6.sysctl.ip6_rt_gc_min_interval =
817 saved_rt_min_interval;
818 goto retry;
819 }
820
Joe Perchesf3213832012-05-15 14:11:53 +0000821 net_warn_ratelimited("Neighbour table overflow\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700822 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800823 return NULL;
824 }
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800825 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800827 return rt;
828}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000830static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
831 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800832{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000833 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
834
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800835 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800836 rt->rt6i_flags |= RTF_CACHE;
David S. Miller97cac082012-07-02 22:43:47 -0700837 rt->n = neigh_clone(ort->n);
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800838 }
839 return rt;
840}
841
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800842static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500843 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844{
845 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800846 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700847 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800849 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700850 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700852 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
854relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700855 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800857restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500858 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
860restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700861 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800862
David S. Miller4c9483b2011-03-12 16:22:43 -0500863 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800864 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800865 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800866 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Changli Gaod8d1f302010-06-10 23:31:35 -0700868 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700869 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800870
David S. Miller97cac082012-07-02 22:43:47 -0700871 if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500872 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800873 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500874 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800875 else
876 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800877
Changli Gaod8d1f302010-06-10 23:31:35 -0700878 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800879 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800880
Changli Gaod8d1f302010-06-10 23:31:35 -0700881 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800882 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700883 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800884 if (!err)
885 goto out2;
886 }
887
888 if (--attempts <= 0)
889 goto out2;
890
891 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700892 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800893 * released someone could insert this route. Relookup.
894 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700895 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800896 goto relookup;
897
898out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800899 if (reachable) {
900 reachable = 0;
901 goto restart_2;
902 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700903 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700904 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700906 rt->dst.lastuse = jiffies;
907 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700908
909 return rt;
910}
911
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800912static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500913 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700914{
David S. Miller4c9483b2011-03-12 16:22:43 -0500915 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700916}
917
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000918static struct dst_entry *ip6_route_input_lookup(struct net *net,
919 struct net_device *dev,
920 struct flowi6 *fl6, int flags)
921{
922 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
923 flags |= RT6_LOOKUP_F_IFACE;
924
925 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input);
926}
927
Thomas Grafc71099a2006-08-04 23:20:06 -0700928void ip6_route_input(struct sk_buff *skb)
929{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000930 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900931 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700932 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500933 struct flowi6 fl6 = {
934 .flowi6_iif = skb->dev->ifindex,
935 .daddr = iph->daddr,
936 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500937 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500938 .flowi6_mark = skb->mark,
939 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700940 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700941
Shmulik Ladkani72331bc2012-04-01 04:03:45 +0000942 skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -0700943}
944
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800945static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500946 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700947{
David S. Miller4c9483b2011-03-12 16:22:43 -0500948 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700949}
950
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700951struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500952 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700953{
954 int flags = 0;
955
David McCullough4dc27d1c2012-06-25 15:42:26 +0000956 fl6->flowi6_iif = net->loopback_dev->ifindex;
957
David S. Miller4c9483b2011-03-12 16:22:43 -0500958 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700959 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700960
David S. Miller4c9483b2011-03-12 16:22:43 -0500961 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700962 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000963 else if (sk)
964 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700965
David S. Miller4c9483b2011-03-12 16:22:43 -0500966 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967}
968
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900969EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
David S. Miller2774c132011-03-01 14:59:04 -0800971struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700972{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700973 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700974 struct dst_entry *new = NULL;
975
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700976 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700977 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700978 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
David S. Miller97bab732012-06-09 22:36:36 -0700979 rt6_init_peer(rt, net->ipv6.peers);
David S. Millercf911662011-04-28 14:31:47 -0700980
Changli Gaod8d1f302010-06-10 23:31:35 -0700981 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700982
David S. Miller14e50e52007-05-24 18:17:54 -0700983 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800984 new->input = dst_discard;
985 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700986
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000987 if (dst_metrics_read_only(&ort->dst))
988 new->_metrics = ort->dst._metrics;
989 else
990 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700991 rt->rt6i_idev = ort->rt6i_idev;
992 if (rt->rt6i_idev)
993 in6_dev_hold(rt->rt6i_idev);
David S. Miller14e50e52007-05-24 18:17:54 -0700994
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000995 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +0000996 rt->rt6i_flags = ort->rt6i_flags;
997 rt6_clean_expires(rt);
David S. Miller14e50e52007-05-24 18:17:54 -0700998 rt->rt6i_metric = 0;
999
1000 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1001#ifdef CONFIG_IPV6_SUBTREES
1002 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1003#endif
1004
1005 dst_free(new);
1006 }
1007
David S. Miller69ead7a2011-03-01 14:45:33 -08001008 dst_release(dst_orig);
1009 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07001010}
David S. Miller14e50e52007-05-24 18:17:54 -07001011
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012/*
1013 * Destination cache support functions
1014 */
1015
1016static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1017{
1018 struct rt6_info *rt;
1019
1020 rt = (struct rt6_info *) dst;
1021
David S. Miller6431cbc2011-02-07 20:38:06 -08001022 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
1023 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
David S. Miller97bab732012-06-09 22:36:36 -07001024 if (!rt6_has_peer(rt))
David S. Miller6431cbc2011-02-07 20:38:06 -08001025 rt6_bind_peer(rt, 0);
1026 rt->rt6i_peer_genid = rt6_peer_genid();
1027 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -08001029 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 return NULL;
1031}
1032
1033static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
1034{
1035 struct rt6_info *rt = (struct rt6_info *) dst;
1036
1037 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001038 if (rt->rt6i_flags & RTF_CACHE) {
1039 if (rt6_check_expired(rt)) {
1040 ip6_del_rt(rt);
1041 dst = NULL;
1042 }
1043 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001045 dst = NULL;
1046 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00001048 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049}
1050
1051static void ip6_link_failure(struct sk_buff *skb)
1052{
1053 struct rt6_info *rt;
1054
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001055 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
Eric Dumazetadf30902009-06-02 05:19:30 +00001057 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +00001059 if (rt->rt6i_flags & RTF_CACHE)
1060 rt6_update_expires(rt, 0);
1061 else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 rt->rt6i_node->fn_sernum = -1;
1063 }
1064}
1065
1066static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1067{
1068 struct rt6_info *rt6 = (struct rt6_info*)dst;
1069
David S. Miller81aded22012-06-15 14:54:11 -07001070 dst_confirm(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
David S. Miller81aded22012-06-15 14:54:11 -07001072 struct net *net = dev_net(dst->dev);
1073
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 rt6->rt6i_flags |= RTF_MODIFIED;
1075 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001076 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001078 features |= RTAX_FEATURE_ALLFRAG;
1079 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 }
David S. Millerdefb3512010-12-08 21:16:57 -08001081 dst_metric_set(dst, RTAX_MTU, mtu);
David S. Miller81aded22012-06-15 14:54:11 -07001082 rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 }
1084}
1085
David S. Miller42ae66c2012-06-15 20:01:57 -07001086void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
1087 int oif, u32 mark)
David S. Miller81aded22012-06-15 14:54:11 -07001088{
1089 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
1090 struct dst_entry *dst;
1091 struct flowi6 fl6;
1092
1093 memset(&fl6, 0, sizeof(fl6));
1094 fl6.flowi6_oif = oif;
1095 fl6.flowi6_mark = mark;
David S. Miller3e129392012-07-10 04:01:57 -07001096 fl6.flowi6_flags = 0;
David S. Miller81aded22012-06-15 14:54:11 -07001097 fl6.daddr = iph->daddr;
1098 fl6.saddr = iph->saddr;
1099 fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK;
1100
1101 dst = ip6_route_output(net, NULL, &fl6);
1102 if (!dst->error)
1103 ip6_rt_update_pmtu(dst, ntohl(mtu));
1104 dst_release(dst);
1105}
1106EXPORT_SYMBOL_GPL(ip6_update_pmtu);
1107
1108void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
1109{
1110 ip6_update_pmtu(skb, sock_net(sk), mtu,
1111 sk->sk_bound_dev_if, sk->sk_mark);
1112}
1113EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
1114
David S. Miller0dbaee32010-12-13 12:52:14 -08001115static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116{
David S. Miller0dbaee32010-12-13 12:52:14 -08001117 struct net_device *dev = dst->dev;
1118 unsigned int mtu = dst_mtu(dst);
1119 struct net *net = dev_net(dev);
1120
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1122
Daniel Lezcano55786892008-03-04 13:47:47 -08001123 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1124 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
1126 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001127 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1128 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1129 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 * rely only on pmtu discovery"
1131 */
1132 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1133 mtu = IPV6_MAXPLEN;
1134 return mtu;
1135}
1136
Steffen Klassertebb762f2011-11-23 02:12:51 +00001137static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001138{
David S. Millerd33e4552010-12-14 13:01:14 -08001139 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001140 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1141
1142 if (mtu)
1143 return mtu;
1144
1145 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001146
1147 rcu_read_lock();
1148 idev = __in6_dev_get(dst->dev);
1149 if (idev)
1150 mtu = idev->cnf.mtu6;
1151 rcu_read_unlock();
1152
1153 return mtu;
1154}
1155
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001156static struct dst_entry *icmp6_dst_gc_list;
1157static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001158
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001159struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001161 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162{
David S. Miller87a11572011-12-06 17:04:13 -05001163 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 struct rt6_info *rt;
1165 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001166 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167
David S. Miller38308472011-12-03 18:02:47 -05001168 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00001169 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170
David S. Miller8b96d222012-06-11 02:01:56 -07001171 rt = ip6_dst_alloc(net, dev, 0, NULL);
David S. Miller38308472011-12-03 18:02:47 -05001172 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001174 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 goto out;
1176 }
1177
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 if (neigh)
1179 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001180 else {
David S. Millerf894cbf2012-07-02 21:52:24 -07001181 neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr);
David S. Millerb43faac2011-12-13 16:48:21 -05001182 if (IS_ERR(neigh)) {
RongQing.Li252c3d82012-01-12 22:33:46 +00001183 in6_dev_put(idev);
David S. Millerb43faac2011-12-13 16:48:21 -05001184 dst_free(&rt->dst);
1185 return ERR_CAST(neigh);
1186 }
David S. Miller14deae42009-01-04 16:04:39 -08001187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001189 rt->dst.flags |= DST_HOST;
1190 rt->dst.output = ip6_output;
David S. Miller97cac082012-07-02 22:43:47 -07001191 rt->n = neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001192 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001193 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001194 rt->rt6i_dst.plen = 128;
1195 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001196 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001198 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001199 rt->dst.next = icmp6_dst_gc_list;
1200 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001201 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
Daniel Lezcano55786892008-03-04 13:47:47 -08001203 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
David S. Miller87a11572011-12-06 17:04:13 -05001205 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1206
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207out:
David S. Miller87a11572011-12-06 17:04:13 -05001208 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209}
1210
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001211int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212{
Hagen Paul Pfeifere9476e952011-02-25 05:45:19 +00001213 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001214 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001216 spin_lock_bh(&icmp6_dst_lock);
1217 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001218
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 while ((dst = *pprev) != NULL) {
1220 if (!atomic_read(&dst->__refcnt)) {
1221 *pprev = dst->next;
1222 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 } else {
1224 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001225 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 }
1227 }
1228
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001229 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001230
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001231 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232}
1233
David S. Miller1e493d12008-09-10 17:27:15 -07001234static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1235 void *arg)
1236{
1237 struct dst_entry *dst, **pprev;
1238
1239 spin_lock_bh(&icmp6_dst_lock);
1240 pprev = &icmp6_dst_gc_list;
1241 while ((dst = *pprev) != NULL) {
1242 struct rt6_info *rt = (struct rt6_info *) dst;
1243 if (func(rt, arg)) {
1244 *pprev = dst->next;
1245 dst_free(dst);
1246 } else {
1247 pprev = &dst->next;
1248 }
1249 }
1250 spin_unlock_bh(&icmp6_dst_lock);
1251}
1252
Daniel Lezcano569d3642008-01-18 03:56:57 -08001253static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001256 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001257 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1258 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1259 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1260 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1261 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001262 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
Eric Dumazetfc66f952010-10-08 06:37:34 +00001264 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001265 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001266 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 goto out;
1268
Benjamin Thery6891a342008-03-04 13:49:47 -08001269 net->ipv6.ip6_rt_gc_expire++;
1270 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1271 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001272 entries = dst_entries_get_slow(ops);
1273 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001274 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001276 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001277 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278}
1279
1280/* Clean host part of a prefix. Not necessary in radix tree,
1281 but results in cleaner routing tables.
1282
1283 Remove it only when all the things will work!
1284 */
1285
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001286int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287{
David S. Miller5170ae82010-12-12 21:35:57 -08001288 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001289 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001290 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001291 struct inet6_dev *idev;
1292
1293 rcu_read_lock();
1294 idev = __in6_dev_get(dev);
1295 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001296 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001297 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001298 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001299 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 }
1301 return hoplimit;
1302}
David S. Millerabbf46a2010-12-12 21:14:46 -08001303EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
1305/*
1306 *
1307 */
1308
Thomas Graf86872cb2006-08-22 00:01:08 -07001309int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310{
1311 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001312 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 struct rt6_info *rt = NULL;
1314 struct net_device *dev = NULL;
1315 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001316 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 int addr_type;
1318
Thomas Graf86872cb2006-08-22 00:01:08 -07001319 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 return -EINVAL;
1321#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001322 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 return -EINVAL;
1324#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001325 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001327 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 if (!dev)
1329 goto out;
1330 idev = in6_dev_get(dev);
1331 if (!idev)
1332 goto out;
1333 }
1334
Thomas Graf86872cb2006-08-22 00:01:08 -07001335 if (cfg->fc_metric == 0)
1336 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
Matti Vaittinend71314b2011-11-14 00:14:49 +00001338 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001339 if (cfg->fc_nlinfo.nlh &&
1340 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001341 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001342 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00001343 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00001344 table = fib6_new_table(net, cfg->fc_table);
1345 }
1346 } else {
1347 table = fib6_new_table(net, cfg->fc_table);
1348 }
David S. Miller38308472011-12-03 18:02:47 -05001349
1350 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001351 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001352
David S. Miller8b96d222012-06-11 02:01:56 -07001353 rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
David S. Miller38308472011-12-03 18:02:47 -05001355 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 err = -ENOMEM;
1357 goto out;
1358 }
1359
Changli Gaod8d1f302010-06-10 23:31:35 -07001360 rt->dst.obsolete = -1;
Gao feng1716a962012-04-06 00:13:10 +00001361
1362 if (cfg->fc_flags & RTF_EXPIRES)
1363 rt6_set_expires(rt, jiffies +
1364 clock_t_to_jiffies(cfg->fc_expires));
1365 else
1366 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Thomas Graf86872cb2006-08-22 00:01:08 -07001368 if (cfg->fc_protocol == RTPROT_UNSPEC)
1369 cfg->fc_protocol = RTPROT_BOOT;
1370 rt->rt6i_protocol = cfg->fc_protocol;
1371
1372 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001375 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001376 else if (cfg->fc_flags & RTF_LOCAL)
1377 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001379 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Changli Gaod8d1f302010-06-10 23:31:35 -07001381 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Thomas Graf86872cb2006-08-22 00:01:08 -07001383 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1384 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001386 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001388 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1389 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1390 if (!metrics) {
1391 err = -ENOMEM;
1392 goto out;
1393 }
1394 dst_init_metrics(&rt->dst, metrics, 0);
1395 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001397 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1398 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399#endif
1400
Thomas Graf86872cb2006-08-22 00:01:08 -07001401 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 /* We cannot add true routes via loopback here,
1404 they would result in kernel looping; promote them to reject routes
1405 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001406 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001407 (dev && (dev->flags & IFF_LOOPBACK) &&
1408 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1409 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001411 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 if (dev) {
1413 dev_put(dev);
1414 in6_dev_put(idev);
1415 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001416 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 dev_hold(dev);
1418 idev = in6_dev_get(dev);
1419 if (!idev) {
1420 err = -ENODEV;
1421 goto out;
1422 }
1423 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001424 rt->dst.output = ip6_pkt_discard_out;
1425 rt->dst.input = ip6_pkt_discard;
1426 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1428 goto install_route;
1429 }
1430
Thomas Graf86872cb2006-08-22 00:01:08 -07001431 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001432 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 int gwa_type;
1434
Thomas Graf86872cb2006-08-22 00:01:08 -07001435 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001436 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 gwa_type = ipv6_addr_type(gw_addr);
1438
1439 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1440 struct rt6_info *grt;
1441
1442 /* IPv6 strictly inhibits using not link-local
1443 addresses as nexthop address.
1444 Otherwise, router will not able to send redirects.
1445 It is very good, but in some (rare!) circumstances
1446 (SIT, PtP, NBMA NOARP links) it is handy to allow
1447 some exceptions. --ANK
1448 */
1449 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001450 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 goto out;
1452
Daniel Lezcano55786892008-03-04 13:47:47 -08001453 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
1455 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001456 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 goto out;
1458 if (dev) {
David S. Millerd1918542011-12-28 20:19:20 -05001459 if (dev != grt->dst.dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001460 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 goto out;
1462 }
1463 } else {
David S. Millerd1918542011-12-28 20:19:20 -05001464 dev = grt->dst.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 idev = grt->rt6i_idev;
1466 dev_hold(dev);
1467 in6_dev_hold(grt->rt6i_idev);
1468 }
David S. Miller38308472011-12-03 18:02:47 -05001469 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001471 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472
1473 if (err)
1474 goto out;
1475 }
1476 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001477 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 goto out;
1479 }
1480
1481 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001482 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 goto out;
1484
Daniel Walterc3968a82011-04-13 21:10:57 +00001485 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1486 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1487 err = -EINVAL;
1488 goto out;
1489 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001490 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001491 rt->rt6i_prefsrc.plen = 128;
1492 } else
1493 rt->rt6i_prefsrc.plen = 0;
1494
Thomas Graf86872cb2006-08-22 00:01:08 -07001495 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller8ade06c2011-12-29 18:51:57 -05001496 err = rt6_bind_neighbour(rt, dev);
David S. Millerf83c7792011-12-28 15:41:23 -05001497 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 }
1500
Thomas Graf86872cb2006-08-22 00:01:08 -07001501 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001504 if (cfg->fc_mx) {
1505 struct nlattr *nla;
1506 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507
Thomas Graf86872cb2006-08-22 00:01:08 -07001508 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001509 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001510
1511 if (type) {
1512 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 err = -EINVAL;
1514 goto out;
1515 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001516
David S. Millerdefb3512010-12-08 21:16:57 -08001517 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 }
1520 }
1521
Changli Gaod8d1f302010-06-10 23:31:35 -07001522 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001524 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001525
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001526 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001527
Thomas Graf86872cb2006-08-22 00:01:08 -07001528 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
1530out:
1531 if (dev)
1532 dev_put(dev);
1533 if (idev)
1534 in6_dev_put(idev);
1535 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001536 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 return err;
1538}
1539
Thomas Graf86872cb2006-08-22 00:01:08 -07001540static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541{
1542 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001543 struct fib6_table *table;
David S. Millerd1918542011-12-28 20:19:20 -05001544 struct net *net = dev_net(rt->dst.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001546 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001547 return -ENOENT;
1548
Thomas Grafc71099a2006-08-04 23:20:06 -07001549 table = rt->rt6i_table;
1550 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
Thomas Graf86872cb2006-08-22 00:01:08 -07001552 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001553 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Thomas Grafc71099a2006-08-04 23:20:06 -07001555 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
1557 return err;
1558}
1559
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001560int ip6_del_rt(struct rt6_info *rt)
1561{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001562 struct nl_info info = {
David S. Millerd1918542011-12-28 20:19:20 -05001563 .nl_net = dev_net(rt->dst.dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001564 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001565 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001566}
1567
Thomas Graf86872cb2006-08-22 00:01:08 -07001568static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569{
Thomas Grafc71099a2006-08-04 23:20:06 -07001570 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 struct fib6_node *fn;
1572 struct rt6_info *rt;
1573 int err = -ESRCH;
1574
Daniel Lezcano55786892008-03-04 13:47:47 -08001575 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001576 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001577 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578
Thomas Grafc71099a2006-08-04 23:20:06 -07001579 read_lock_bh(&table->tb6_lock);
1580
1581 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001582 &cfg->fc_dst, cfg->fc_dst_len,
1583 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001584
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001586 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001587 if (cfg->fc_ifindex &&
David S. Millerd1918542011-12-28 20:19:20 -05001588 (!rt->dst.dev ||
1589 rt->dst.dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001591 if (cfg->fc_flags & RTF_GATEWAY &&
1592 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001594 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001596 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001597 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
Thomas Graf86872cb2006-08-22 00:01:08 -07001599 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 }
1601 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001602 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603
1604 return err;
1605}
1606
1607/*
1608 * Handle redirects
1609 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001610struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001611 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001612 struct in6_addr gateway;
1613};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001615static struct rt6_info *__ip6_route_redirect(struct net *net,
1616 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001617 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001618 int flags)
1619{
David S. Miller4c9483b2011-03-12 16:22:43 -05001620 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001621 struct rt6_info *rt;
1622 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001623
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001625 * Get the "current" route for this destination and
1626 * check if the redirect has come from approriate router.
1627 *
1628 * RFC 2461 specifies that redirects should only be
1629 * accepted if they come from the nexthop to the target.
1630 * Due to the way the routes are chosen, this notion
1631 * is a bit fuzzy and one might need to check all possible
1632 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634
Thomas Grafc71099a2006-08-04 23:20:06 -07001635 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001636 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001637restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001638 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001639 /*
1640 * Current route is on-link; redirect is always invalid.
1641 *
1642 * Seems, previous statement is not true. It could
1643 * be node, which looks for us as on-link (f.e. proxy ndisc)
1644 * But then router serving it might decide, that we should
1645 * know truth 8)8) --ANK (980726).
1646 */
1647 if (rt6_check_expired(rt))
1648 continue;
1649 if (!(rt->rt6i_flags & RTF_GATEWAY))
1650 continue;
David S. Millerd1918542011-12-28 20:19:20 -05001651 if (fl6->flowi6_oif != rt->dst.dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001652 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001653 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001654 continue;
1655 break;
1656 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001657
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001658 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001659 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001660 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001661out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001662 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001663
1664 read_unlock_bh(&table->tb6_lock);
1665
1666 return rt;
1667};
1668
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001669static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1670 const struct in6_addr *src,
1671 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001672 struct net_device *dev)
1673{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001674 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001675 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001676 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001677 .fl6 = {
1678 .flowi6_oif = dev->ifindex,
1679 .daddr = *dest,
1680 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001681 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001682 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001683
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001684 rdfl.gateway = *gateway;
Brian Haley86c36ce2009-10-07 13:58:01 -07001685
Thomas Grafadaa70b2006-10-13 15:01:03 -07001686 if (rt6_need_strict(dest))
1687 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001688
David S. Miller4c9483b2011-03-12 16:22:43 -05001689 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001690 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001691}
1692
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001693void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
1694 const struct in6_addr *saddr,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001695 struct neighbour *neigh, u8 *lladdr, int on_link)
1696{
1697 struct rt6_info *rt, *nrt = NULL;
1698 struct netevent_redirect netevent;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001699 struct net *net = dev_net(neigh->dev);
David S. Miller1d248b12012-07-03 01:01:51 -07001700 struct neighbour *old_neigh;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001701
1702 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1703
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001704 if (rt == net->ipv6.ip6_null_entry) {
Joe Perchese87cc472012-05-13 21:56:26 +00001705 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001706 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 }
1708
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 /*
1710 * We have finally decided to accept it.
1711 */
1712
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001713 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1715 NEIGH_UPDATE_F_OVERRIDE|
1716 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1717 NEIGH_UPDATE_F_ISROUTER))
1718 );
1719
1720 /*
1721 * Redirect received -> path was valid.
1722 * Look, redirects are sent only in response to data packets,
1723 * so that this nexthop apparently is reachable. --ANK
1724 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001725 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
1727 /* Duplicate redirect: silently ignore. */
David S. Miller97cac082012-07-02 22:43:47 -07001728 old_neigh = rt->n;
David S. Miller1d248b12012-07-03 01:01:51 -07001729 if (neigh == old_neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 goto out;
1731
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001732 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001733 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 goto out;
1735
1736 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1737 if (on_link)
1738 nrt->rt6i_flags &= ~RTF_GATEWAY;
1739
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001740 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller97cac082012-07-02 22:43:47 -07001741 nrt->n = neigh_clone(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742
Thomas Graf40e22e82006-08-22 00:00:45 -07001743 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 goto out;
1745
Changli Gaod8d1f302010-06-10 23:31:35 -07001746 netevent.old = &rt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001747 netevent.old_neigh = old_neigh;
Changli Gaod8d1f302010-06-10 23:31:35 -07001748 netevent.new = &nrt->dst;
David S. Miller1d248b12012-07-03 01:01:51 -07001749 netevent.new_neigh = neigh;
1750 netevent.daddr = dest;
Tom Tucker8d717402006-07-30 20:43:36 -07001751 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1752
David S. Miller38308472011-12-03 18:02:47 -05001753 if (rt->rt6i_flags & RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001754 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 return;
1756 }
1757
1758out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001759 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760}
1761
1762/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 * Misc support functions
1764 */
1765
Gao feng1716a962012-04-06 00:13:10 +00001766static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001767 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768{
David S. Millerd1918542011-12-28 20:19:20 -05001769 struct net *net = dev_net(ort->dst.dev);
David S. Miller8b96d222012-06-11 02:01:56 -07001770 struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0,
1771 ort->rt6i_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
1773 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001774 rt->dst.input = ort->dst.input;
1775 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001776 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001778 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001779 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001780 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001781 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 rt->rt6i_idev = ort->rt6i_idev;
1783 if (rt->rt6i_idev)
1784 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001785 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001787 rt->rt6i_gateway = ort->rt6i_gateway;
Gao feng1716a962012-04-06 00:13:10 +00001788 rt->rt6i_flags = ort->rt6i_flags;
1789 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1790 (RTF_DEFAULT | RTF_ADDRCONF))
1791 rt6_set_from(rt, ort);
1792 else
1793 rt6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 rt->rt6i_metric = 0;
1795
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796#ifdef CONFIG_IPV6_SUBTREES
1797 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1798#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001799 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001800 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 }
1802 return rt;
1803}
1804
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001805#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001806static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001807 const struct in6_addr *prefix, int prefixlen,
1808 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001809{
1810 struct fib6_node *fn;
1811 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001812 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001813
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001814 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001815 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001816 return NULL;
1817
1818 write_lock_bh(&table->tb6_lock);
1819 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001820 if (!fn)
1821 goto out;
1822
Changli Gaod8d1f302010-06-10 23:31:35 -07001823 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001824 if (rt->dst.dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001825 continue;
1826 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1827 continue;
1828 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1829 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001830 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001831 break;
1832 }
1833out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001834 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001835 return rt;
1836}
1837
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001838static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001839 const struct in6_addr *prefix, int prefixlen,
1840 const struct in6_addr *gwaddr, int ifindex,
Eric Dumazet95c96172012-04-15 05:58:06 +00001841 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001842{
Thomas Graf86872cb2006-08-22 00:01:08 -07001843 struct fib6_config cfg = {
1844 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001845 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001846 .fc_ifindex = ifindex,
1847 .fc_dst_len = prefixlen,
1848 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1849 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001850 .fc_nlinfo.pid = 0,
1851 .fc_nlinfo.nlh = NULL,
1852 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001853 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001854
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001855 cfg.fc_dst = *prefix;
1856 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001857
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001858 /* We should treat it as a default route if prefix length is 0. */
1859 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001860 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001861
Thomas Graf86872cb2006-08-22 00:01:08 -07001862 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001863
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001864 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001865}
1866#endif
1867
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001868struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001869{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001871 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001873 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001874 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001875 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876
Thomas Grafc71099a2006-08-04 23:20:06 -07001877 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001878 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
David S. Millerd1918542011-12-28 20:19:20 -05001879 if (dev == rt->dst.dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001880 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1882 break;
1883 }
1884 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001885 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001886 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 return rt;
1888}
1889
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001890struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001891 struct net_device *dev,
1892 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893{
Thomas Graf86872cb2006-08-22 00:01:08 -07001894 struct fib6_config cfg = {
1895 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001896 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001897 .fc_ifindex = dev->ifindex,
1898 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1899 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001900 .fc_nlinfo.pid = 0,
1901 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001902 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001903 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001905 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906
Thomas Graf86872cb2006-08-22 00:01:08 -07001907 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 return rt6_get_dflt_router(gwaddr, dev);
1910}
1911
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001912void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913{
1914 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001915 struct fib6_table *table;
1916
1917 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001918 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001919 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001920 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921
1922restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001923 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001924 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001926 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001927 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001928 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 goto restart;
1930 }
1931 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001932 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933}
1934
Daniel Lezcano55786892008-03-04 13:47:47 -08001935static void rtmsg_to_fib6_config(struct net *net,
1936 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001937 struct fib6_config *cfg)
1938{
1939 memset(cfg, 0, sizeof(*cfg));
1940
1941 cfg->fc_table = RT6_TABLE_MAIN;
1942 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1943 cfg->fc_metric = rtmsg->rtmsg_metric;
1944 cfg->fc_expires = rtmsg->rtmsg_info;
1945 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1946 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1947 cfg->fc_flags = rtmsg->rtmsg_flags;
1948
Daniel Lezcano55786892008-03-04 13:47:47 -08001949 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001950
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001951 cfg->fc_dst = rtmsg->rtmsg_dst;
1952 cfg->fc_src = rtmsg->rtmsg_src;
1953 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001954}
1955
Daniel Lezcano55786892008-03-04 13:47:47 -08001956int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957{
Thomas Graf86872cb2006-08-22 00:01:08 -07001958 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959 struct in6_rtmsg rtmsg;
1960 int err;
1961
1962 switch(cmd) {
1963 case SIOCADDRT: /* Add a route */
1964 case SIOCDELRT: /* Delete a route */
1965 if (!capable(CAP_NET_ADMIN))
1966 return -EPERM;
1967 err = copy_from_user(&rtmsg, arg,
1968 sizeof(struct in6_rtmsg));
1969 if (err)
1970 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001971
Daniel Lezcano55786892008-03-04 13:47:47 -08001972 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001973
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 rtnl_lock();
1975 switch (cmd) {
1976 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001977 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 break;
1979 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001980 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 break;
1982 default:
1983 err = -EINVAL;
1984 }
1985 rtnl_unlock();
1986
1987 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001988 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989
1990 return -EINVAL;
1991}
1992
1993/*
1994 * Drop the packet on the floor
1995 */
1996
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07001997static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001999 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002000 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002001 switch (ipstats_mib_noroutes) {
2002 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002003 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002004 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002005 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2006 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002007 break;
2008 }
2009 /* FALLTHROUGH */
2010 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002011 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2012 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002013 break;
2014 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002015 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 kfree_skb(skb);
2017 return 0;
2018}
2019
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002020static int ip6_pkt_discard(struct sk_buff *skb)
2021{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002022 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002023}
2024
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002025static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026{
Eric Dumazetadf30902009-06-02 05:19:30 +00002027 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002028 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029}
2030
David S. Miller6723ab52006-10-18 21:20:57 -07002031#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2032
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002033static int ip6_pkt_prohibit(struct sk_buff *skb)
2034{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002035 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002036}
2037
2038static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2039{
Eric Dumazetadf30902009-06-02 05:19:30 +00002040 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002041 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002042}
2043
David S. Miller6723ab52006-10-18 21:20:57 -07002044#endif
2045
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046/*
2047 * Allocate a dst for local (unicast / anycast) address.
2048 */
2049
2050struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2051 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002052 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002054 struct net *net = dev_net(idev->dev);
David S. Miller8b96d222012-06-11 02:01:56 -07002055 struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL);
David S. Millerf83c7792011-12-28 15:41:23 -05002056 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057
David S. Miller38308472011-12-03 18:02:47 -05002058 if (!rt) {
Joe Perchesf3213832012-05-15 14:11:53 +00002059 net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002061 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 in6_dev_hold(idev);
2064
David S. Miller11d53b42011-06-24 15:23:34 -07002065 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002066 rt->dst.input = ip6_input;
2067 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002069 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070
2071 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002072 if (anycast)
2073 rt->rt6i_flags |= RTF_ANYCAST;
2074 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller8ade06c2011-12-29 18:51:57 -05002076 err = rt6_bind_neighbour(rt, rt->dst.dev);
David S. Millerf83c7792011-12-28 15:41:23 -05002077 if (err) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002078 dst_free(&rt->dst);
David S. Millerf83c7792011-12-28 15:41:23 -05002079 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 }
2081
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002082 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002084 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085
Changli Gaod8d1f302010-06-10 23:31:35 -07002086 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
2088 return rt;
2089}
2090
Daniel Walterc3968a82011-04-13 21:10:57 +00002091int ip6_route_get_saddr(struct net *net,
2092 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002093 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002094 unsigned int prefs,
2095 struct in6_addr *saddr)
2096{
2097 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2098 int err = 0;
2099 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002100 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002101 else
2102 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2103 daddr, prefs, saddr);
2104 return err;
2105}
2106
2107/* remove deleted ip from prefsrc entries */
2108struct arg_dev_net_ip {
2109 struct net_device *dev;
2110 struct net *net;
2111 struct in6_addr *addr;
2112};
2113
2114static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2115{
2116 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2117 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2118 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2119
David S. Millerd1918542011-12-28 20:19:20 -05002120 if (((void *)rt->dst.dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002121 rt != net->ipv6.ip6_null_entry &&
2122 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2123 /* remove prefsrc entry */
2124 rt->rt6i_prefsrc.plen = 0;
2125 }
2126 return 0;
2127}
2128
2129void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2130{
2131 struct net *net = dev_net(ifp->idev->dev);
2132 struct arg_dev_net_ip adni = {
2133 .dev = ifp->idev->dev,
2134 .net = net,
2135 .addr = &ifp->addr,
2136 };
2137 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2138}
2139
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002140struct arg_dev_net {
2141 struct net_device *dev;
2142 struct net *net;
2143};
2144
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145static int fib6_ifdown(struct rt6_info *rt, void *arg)
2146{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002147 const struct arg_dev_net *adn = arg;
2148 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002149
David S. Millerd1918542011-12-28 20:19:20 -05002150 if ((rt->dst.dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002151 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002153
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 return 0;
2155}
2156
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002157void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002159 struct arg_dev_net adn = {
2160 .dev = dev,
2161 .net = net,
2162 };
2163
2164 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002165 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166}
2167
Eric Dumazet95c96172012-04-15 05:58:06 +00002168struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00002170 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171};
2172
2173static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2174{
2175 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2176 struct inet6_dev *idev;
2177
2178 /* In IPv6 pmtu discovery is not optional,
2179 so that RTAX_MTU lock cannot disable it.
2180 We still use this lock to block changes
2181 caused by addrconf/ndisc.
2182 */
2183
2184 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002185 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186 return 0;
2187
2188 /* For administrative MTU increase, there is no way to discover
2189 IPv6 PMTU increase, so PMTU increase should be updated here.
2190 Since RFC 1981 doesn't include administrative MTU increase
2191 update PMTU increase is a MUST. (i.e. jumbo frame)
2192 */
2193 /*
2194 If new MTU is less than route PMTU, this new MTU will be the
2195 lowest MTU in the path, update the route PMTU to reflect PMTU
2196 decreases; if new MTU is greater than route PMTU, and the
2197 old MTU is the lowest MTU in the path, update the route PMTU
2198 to reflect the increase. In this case if the other nodes' MTU
2199 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2200 PMTU discouvery.
2201 */
David S. Millerd1918542011-12-28 20:19:20 -05002202 if (rt->dst.dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002203 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2204 (dst_mtu(&rt->dst) >= arg->mtu ||
2205 (dst_mtu(&rt->dst) < arg->mtu &&
2206 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002207 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002208 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 return 0;
2210}
2211
Eric Dumazet95c96172012-04-15 05:58:06 +00002212void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213{
Thomas Grafc71099a2006-08-04 23:20:06 -07002214 struct rt6_mtu_change_arg arg = {
2215 .dev = dev,
2216 .mtu = mtu,
2217 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002219 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220}
2221
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002222static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002223 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002224 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002225 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002226 [RTA_PRIORITY] = { .type = NLA_U32 },
2227 [RTA_METRICS] = { .type = NLA_NESTED },
2228};
2229
2230static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2231 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232{
Thomas Graf86872cb2006-08-22 00:01:08 -07002233 struct rtmsg *rtm;
2234 struct nlattr *tb[RTA_MAX+1];
2235 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236
Thomas Graf86872cb2006-08-22 00:01:08 -07002237 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2238 if (err < 0)
2239 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240
Thomas Graf86872cb2006-08-22 00:01:08 -07002241 err = -EINVAL;
2242 rtm = nlmsg_data(nlh);
2243 memset(cfg, 0, sizeof(*cfg));
2244
2245 cfg->fc_table = rtm->rtm_table;
2246 cfg->fc_dst_len = rtm->rtm_dst_len;
2247 cfg->fc_src_len = rtm->rtm_src_len;
2248 cfg->fc_flags = RTF_UP;
2249 cfg->fc_protocol = rtm->rtm_protocol;
2250
2251 if (rtm->rtm_type == RTN_UNREACHABLE)
2252 cfg->fc_flags |= RTF_REJECT;
2253
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002254 if (rtm->rtm_type == RTN_LOCAL)
2255 cfg->fc_flags |= RTF_LOCAL;
2256
Thomas Graf86872cb2006-08-22 00:01:08 -07002257 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2258 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002259 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002260
2261 if (tb[RTA_GATEWAY]) {
2262 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2263 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002265
2266 if (tb[RTA_DST]) {
2267 int plen = (rtm->rtm_dst_len + 7) >> 3;
2268
2269 if (nla_len(tb[RTA_DST]) < plen)
2270 goto errout;
2271
2272 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002274
2275 if (tb[RTA_SRC]) {
2276 int plen = (rtm->rtm_src_len + 7) >> 3;
2277
2278 if (nla_len(tb[RTA_SRC]) < plen)
2279 goto errout;
2280
2281 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002283
Daniel Walterc3968a82011-04-13 21:10:57 +00002284 if (tb[RTA_PREFSRC])
2285 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2286
Thomas Graf86872cb2006-08-22 00:01:08 -07002287 if (tb[RTA_OIF])
2288 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2289
2290 if (tb[RTA_PRIORITY])
2291 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2292
2293 if (tb[RTA_METRICS]) {
2294 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2295 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002297
2298 if (tb[RTA_TABLE])
2299 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2300
2301 err = 0;
2302errout:
2303 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304}
2305
Thomas Grafc127ea22007-03-22 11:58:32 -07002306static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307{
Thomas Graf86872cb2006-08-22 00:01:08 -07002308 struct fib6_config cfg;
2309 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310
Thomas Graf86872cb2006-08-22 00:01:08 -07002311 err = rtm_to_fib6_config(skb, nlh, &cfg);
2312 if (err < 0)
2313 return err;
2314
2315 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316}
2317
Thomas Grafc127ea22007-03-22 11:58:32 -07002318static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319{
Thomas Graf86872cb2006-08-22 00:01:08 -07002320 struct fib6_config cfg;
2321 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322
Thomas Graf86872cb2006-08-22 00:01:08 -07002323 err = rtm_to_fib6_config(skb, nlh, &cfg);
2324 if (err < 0)
2325 return err;
2326
2327 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328}
2329
Thomas Graf339bf982006-11-10 14:10:15 -08002330static inline size_t rt6_nlmsg_size(void)
2331{
2332 return NLMSG_ALIGN(sizeof(struct rtmsg))
2333 + nla_total_size(16) /* RTA_SRC */
2334 + nla_total_size(16) /* RTA_DST */
2335 + nla_total_size(16) /* RTA_GATEWAY */
2336 + nla_total_size(16) /* RTA_PREFSRC */
2337 + nla_total_size(4) /* RTA_TABLE */
2338 + nla_total_size(4) /* RTA_IIF */
2339 + nla_total_size(4) /* RTA_OIF */
2340 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002341 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002342 + nla_total_size(sizeof(struct rta_cacheinfo));
2343}
2344
Brian Haley191cd582008-08-14 15:33:21 -07002345static int rt6_fill_node(struct net *net,
2346 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002347 struct in6_addr *dst, struct in6_addr *src,
2348 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002349 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350{
2351 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002352 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002353 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002354 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002355 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
2357 if (prefix) { /* user wants prefix routes only */
2358 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2359 /* success since this is not a prefix route */
2360 return 1;
2361 }
2362 }
2363
Thomas Graf2d7202b2006-08-22 00:01:27 -07002364 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002365 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002366 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002367
2368 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369 rtm->rtm_family = AF_INET6;
2370 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2371 rtm->rtm_src_len = rt->rt6i_src.plen;
2372 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002373 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002374 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002375 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002376 table = RT6_TABLE_UNSPEC;
2377 rtm->rtm_table = table;
David S. Millerc78679e2012-04-01 20:27:33 -04002378 if (nla_put_u32(skb, RTA_TABLE, table))
2379 goto nla_put_failure;
David S. Miller38308472011-12-03 18:02:47 -05002380 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002382 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002383 rtm->rtm_type = RTN_LOCAL;
David S. Millerd1918542011-12-28 20:19:20 -05002384 else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 rtm->rtm_type = RTN_LOCAL;
2386 else
2387 rtm->rtm_type = RTN_UNICAST;
2388 rtm->rtm_flags = 0;
2389 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2390 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002391 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 rtm->rtm_protocol = RTPROT_REDIRECT;
2393 else if (rt->rt6i_flags & RTF_ADDRCONF)
2394 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002395 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 rtm->rtm_protocol = RTPROT_RA;
2397
David S. Miller38308472011-12-03 18:02:47 -05002398 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 rtm->rtm_flags |= RTM_F_CLONED;
2400
2401 if (dst) {
David S. Millerc78679e2012-04-01 20:27:33 -04002402 if (nla_put(skb, RTA_DST, 16, dst))
2403 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002404 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 } else if (rtm->rtm_dst_len)
David S. Millerc78679e2012-04-01 20:27:33 -04002406 if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr))
2407 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408#ifdef CONFIG_IPV6_SUBTREES
2409 if (src) {
David S. Millerc78679e2012-04-01 20:27:33 -04002410 if (nla_put(skb, RTA_SRC, 16, src))
2411 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002412 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04002413 } else if (rtm->rtm_src_len &&
2414 nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr))
2415 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002417 if (iif) {
2418#ifdef CONFIG_IPV6_MROUTE
2419 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002420 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002421 if (err <= 0) {
2422 if (!nowait) {
2423 if (err == 0)
2424 return 0;
2425 goto nla_put_failure;
2426 } else {
2427 if (err == -EMSGSIZE)
2428 goto nla_put_failure;
2429 }
2430 }
2431 } else
2432#endif
David S. Millerc78679e2012-04-01 20:27:33 -04002433 if (nla_put_u32(skb, RTA_IIF, iif))
2434 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002435 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 struct in6_addr saddr_buf;
David S. Millerc78679e2012-04-01 20:27:33 -04002437 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 &&
2438 nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2439 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002441
Daniel Walterc3968a82011-04-13 21:10:57 +00002442 if (rt->rt6i_prefsrc.plen) {
2443 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002444 saddr_buf = rt->rt6i_prefsrc.addr;
David S. Millerc78679e2012-04-01 20:27:33 -04002445 if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf))
2446 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00002447 }
2448
David S. Millerdefb3512010-12-08 21:16:57 -08002449 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002450 goto nla_put_failure;
2451
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002452 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002453 n = rt->n;
Eric Dumazet94f826b2012-03-27 09:53:52 +00002454 if (n) {
2455 if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) {
2456 rcu_read_unlock();
2457 goto nla_put_failure;
2458 }
2459 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002460 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002461
David S. Millerc78679e2012-04-01 20:27:33 -04002462 if (rt->dst.dev &&
2463 nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
2464 goto nla_put_failure;
2465 if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
2466 goto nla_put_failure;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002467 if (!(rt->rt6i_flags & RTF_EXPIRES))
2468 expires = 0;
David S. Millerd1918542011-12-28 20:19:20 -05002469 else if (rt->dst.expires - jiffies < INT_MAX)
2470 expires = rt->dst.expires - jiffies;
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002471 else
2472 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002473
David S. Miller81166dd2012-07-10 03:14:24 -07002474 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0,
Changli Gaod8d1f302010-06-10 23:31:35 -07002475 expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002476 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477
Thomas Graf2d7202b2006-08-22 00:01:27 -07002478 return nlmsg_end(skb, nlh);
2479
2480nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002481 nlmsg_cancel(skb, nlh);
2482 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483}
2484
Patrick McHardy1b43af52006-08-10 23:11:17 -07002485int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486{
2487 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2488 int prefix;
2489
Thomas Graf2d7202b2006-08-22 00:01:27 -07002490 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2491 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2493 } else
2494 prefix = 0;
2495
Brian Haley191cd582008-08-14 15:33:21 -07002496 return rt6_fill_node(arg->net,
2497 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002499 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500}
2501
Thomas Grafc127ea22007-03-22 11:58:32 -07002502static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002504 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002505 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002507 struct sk_buff *skb;
2508 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002509 struct flowi6 fl6;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002510 int err, iif = 0, oif = 0;
Thomas Grafab364a62006-08-22 00:01:47 -07002511
2512 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2513 if (err < 0)
2514 goto errout;
2515
2516 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002517 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002518
2519 if (tb[RTA_SRC]) {
2520 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2521 goto errout;
2522
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002523 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002524 }
2525
2526 if (tb[RTA_DST]) {
2527 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2528 goto errout;
2529
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002530 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002531 }
2532
2533 if (tb[RTA_IIF])
2534 iif = nla_get_u32(tb[RTA_IIF]);
2535
2536 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002537 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002538
2539 if (iif) {
2540 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002541 int flags = 0;
2542
Daniel Lezcano55786892008-03-04 13:47:47 -08002543 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002544 if (!dev) {
2545 err = -ENODEV;
2546 goto errout;
2547 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002548
2549 fl6.flowi6_iif = iif;
2550
2551 if (!ipv6_addr_any(&fl6.saddr))
2552 flags |= RT6_LOOKUP_F_HAS_SADDR;
2553
2554 rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6,
2555 flags);
2556 } else {
2557 fl6.flowi6_oif = oif;
2558
2559 rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
Thomas Grafab364a62006-08-22 00:01:47 -07002560 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561
2562 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002563 if (!skb) {
Shmulik Ladkani2173bff2012-04-03 23:13:00 +00002564 dst_release(&rt->dst);
Thomas Grafab364a62006-08-22 00:01:47 -07002565 err = -ENOBUFS;
2566 goto errout;
2567 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568
2569 /* Reserve room for dummy headers, this skb can pass
2570 through good chunk of routing engine.
2571 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002572 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2574
Changli Gaod8d1f302010-06-10 23:31:35 -07002575 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576
David S. Miller4c9483b2011-03-12 16:22:43 -05002577 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002579 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002581 kfree_skb(skb);
2582 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 }
2584
Daniel Lezcano55786892008-03-04 13:47:47 -08002585 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002586errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588}
2589
Thomas Graf86872cb2006-08-22 00:01:08 -07002590void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591{
2592 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002593 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002594 u32 seq;
2595 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002597 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002598 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002599
Thomas Graf339bf982006-11-10 14:10:15 -08002600 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002601 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002602 goto errout;
2603
Brian Haley191cd582008-08-14 15:33:21 -07002604 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002605 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002606 if (err < 0) {
2607 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2608 WARN_ON(err == -EMSGSIZE);
2609 kfree_skb(skb);
2610 goto errout;
2611 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002612 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2613 info->nlh, gfp_any());
2614 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002615errout:
2616 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002617 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618}
2619
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002620static int ip6_route_dev_notify(struct notifier_block *this,
2621 unsigned long event, void *data)
2622{
2623 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002624 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002625
2626 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002627 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002628 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2629#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002630 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002631 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002632 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002633 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2634#endif
2635 }
2636
2637 return NOTIFY_OK;
2638}
2639
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640/*
2641 * /proc
2642 */
2643
2644#ifdef CONFIG_PROC_FS
2645
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646struct rt6_proc_arg
2647{
2648 char *buffer;
2649 int offset;
2650 int length;
2651 int skip;
2652 int len;
2653};
2654
2655static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2656{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002657 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002658 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002660 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661
2662#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002663 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002665 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002667 rcu_read_lock();
David S. Miller97cac082012-07-02 22:43:47 -07002668 n = rt->n;
David S. Miller69cce1d2011-07-17 23:09:49 -07002669 if (n) {
2670 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002672 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002674 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002675 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002676 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2677 rt->dst.__use, rt->rt6i_flags,
David S. Millerd1918542011-12-28 20:19:20 -05002678 rt->dst.dev ? rt->dst.dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 return 0;
2680}
2681
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002682static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002684 struct net *net = (struct net *)m->private;
Josh Hunt32b293a2011-12-28 13:23:07 +00002685 fib6_clean_all_ro(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002686 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687}
2688
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002689static int ipv6_route_open(struct inode *inode, struct file *file)
2690{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002691 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002692}
2693
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002694static const struct file_operations ipv6_route_proc_fops = {
2695 .owner = THIS_MODULE,
2696 .open = ipv6_route_open,
2697 .read = seq_read,
2698 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002699 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002700};
2701
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2703{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002704 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002706 net->ipv6.rt6_stats->fib_nodes,
2707 net->ipv6.rt6_stats->fib_route_nodes,
2708 net->ipv6.rt6_stats->fib_rt_alloc,
2709 net->ipv6.rt6_stats->fib_rt_entries,
2710 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002711 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002712 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713
2714 return 0;
2715}
2716
2717static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2718{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002719 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002720}
2721
Arjan van de Ven9a321442007-02-12 00:55:35 -08002722static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 .owner = THIS_MODULE,
2724 .open = rt6_stats_seq_open,
2725 .read = seq_read,
2726 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002727 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728};
2729#endif /* CONFIG_PROC_FS */
2730
2731#ifdef CONFIG_SYSCTL
2732
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002734int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 void __user *buffer, size_t *lenp, loff_t *ppos)
2736{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002737 struct net *net;
2738 int delay;
2739 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002741
2742 net = (struct net *)ctl->extra1;
2743 delay = net->ipv6.sysctl.flush_delay;
2744 proc_dointvec(ctl, write, buffer, lenp, ppos);
2745 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2746 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747}
2748
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002749ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002750 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002752 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002754 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002755 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 },
2757 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002759 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760 .maxlen = sizeof(int),
2761 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002762 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763 },
2764 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002766 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 .maxlen = sizeof(int),
2768 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002769 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770 },
2771 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002773 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 .maxlen = sizeof(int),
2775 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002776 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 },
2778 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002780 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 .maxlen = sizeof(int),
2782 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002783 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 },
2785 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002787 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 .maxlen = sizeof(int),
2789 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002790 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 },
2792 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002794 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 .maxlen = sizeof(int),
2796 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002797 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 },
2799 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002801 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 .maxlen = sizeof(int),
2803 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002804 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 },
2806 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002808 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 .maxlen = sizeof(int),
2810 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002811 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 },
2813 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002815 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 .maxlen = sizeof(int),
2817 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002818 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002820 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821};
2822
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002823struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002824{
2825 struct ctl_table *table;
2826
2827 table = kmemdup(ipv6_route_table_template,
2828 sizeof(ipv6_route_table_template),
2829 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002830
2831 if (table) {
2832 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002833 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002834 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002835 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2836 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2837 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2838 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2839 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2840 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2841 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002842 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002843 }
2844
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002845 return table;
2846}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847#endif
2848
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002849static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002850{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002851 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002852
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002853 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2854 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002855
Eric Dumazetfc66f952010-10-08 06:37:34 +00002856 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2857 goto out_ip6_dst_ops;
2858
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002859 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2860 sizeof(*net->ipv6.ip6_null_entry),
2861 GFP_KERNEL);
2862 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002863 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002864 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002865 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002866 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002867 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2868 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002869
2870#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2871 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2872 sizeof(*net->ipv6.ip6_prohibit_entry),
2873 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002874 if (!net->ipv6.ip6_prohibit_entry)
2875 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002876 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002877 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002878 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002879 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2880 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002881
2882 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2883 sizeof(*net->ipv6.ip6_blk_hole_entry),
2884 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002885 if (!net->ipv6.ip6_blk_hole_entry)
2886 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002887 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002888 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002889 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002890 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2891 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002892#endif
2893
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002894 net->ipv6.sysctl.flush_delay = 0;
2895 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2896 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2897 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2898 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2899 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2900 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2901 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2902
Benjamin Thery6891a342008-03-04 13:49:47 -08002903 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2904
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002905 ret = 0;
2906out:
2907 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002908
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002909#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2910out_ip6_prohibit_entry:
2911 kfree(net->ipv6.ip6_prohibit_entry);
2912out_ip6_null_entry:
2913 kfree(net->ipv6.ip6_null_entry);
2914#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002915out_ip6_dst_entries:
2916 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002917out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002918 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002919}
2920
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002921static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002922{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002923 kfree(net->ipv6.ip6_null_entry);
2924#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2925 kfree(net->ipv6.ip6_prohibit_entry);
2926 kfree(net->ipv6.ip6_blk_hole_entry);
2927#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002928 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002929}
2930
Thomas Grafd1896342012-06-18 12:08:33 +00002931static int __net_init ip6_route_net_init_late(struct net *net)
2932{
2933#ifdef CONFIG_PROC_FS
2934 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2935 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2936#endif
2937 return 0;
2938}
2939
2940static void __net_exit ip6_route_net_exit_late(struct net *net)
2941{
2942#ifdef CONFIG_PROC_FS
2943 proc_net_remove(net, "ipv6_route");
2944 proc_net_remove(net, "rt6_stats");
2945#endif
2946}
2947
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002948static struct pernet_operations ip6_route_net_ops = {
2949 .init = ip6_route_net_init,
2950 .exit = ip6_route_net_exit,
2951};
2952
David S. Millerc3426b42012-06-09 16:27:05 -07002953static int __net_init ipv6_inetpeer_init(struct net *net)
2954{
2955 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
2956
2957 if (!bp)
2958 return -ENOMEM;
2959 inet_peer_base_init(bp);
2960 net->ipv6.peers = bp;
2961 return 0;
2962}
2963
2964static void __net_exit ipv6_inetpeer_exit(struct net *net)
2965{
2966 struct inet_peer_base *bp = net->ipv6.peers;
2967
2968 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07002969 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07002970 kfree(bp);
2971}
2972
David S. Miller2b823f72012-06-09 19:00:16 -07002973static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07002974 .init = ipv6_inetpeer_init,
2975 .exit = ipv6_inetpeer_exit,
2976};
2977
Thomas Grafd1896342012-06-18 12:08:33 +00002978static struct pernet_operations ip6_route_net_late_ops = {
2979 .init = ip6_route_net_init_late,
2980 .exit = ip6_route_net_exit_late,
2981};
2982
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002983static struct notifier_block ip6_route_dev_notifier = {
2984 .notifier_call = ip6_route_dev_notify,
2985 .priority = 0,
2986};
2987
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002988int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002990 int ret;
2991
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002992 ret = -ENOMEM;
2993 ip6_dst_ops_template.kmem_cachep =
2994 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
2995 SLAB_HWCACHE_ALIGN, NULL);
2996 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08002997 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07002998
Eric Dumazetfc66f952010-10-08 06:37:34 +00002999 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003000 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003001 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08003002
David S. Millerc3426b42012-06-09 16:27:05 -07003003 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
3004 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003005 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00003006
David S. Miller7e52b332012-06-15 15:51:55 -07003007 ret = register_pernet_subsys(&ip6_route_net_ops);
3008 if (ret)
3009 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07003010
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07003011 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
3012
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003013 /* Registering of the loopback is done before this portion of code,
3014 * the loopback reference in rt6_info will not be taken, do it
3015 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07003016 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003017 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3018 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07003019 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003020 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07003021 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003022 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
3023 #endif
David S. Millere8803b62012-06-16 01:12:19 -07003024 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003025 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003026 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003027
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003028 ret = xfrm6_init();
3029 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07003030 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08003031
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003032 ret = fib6_rules_init();
3033 if (ret)
3034 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08003035
Thomas Grafd1896342012-06-18 12:08:33 +00003036 ret = register_pernet_subsys(&ip6_route_net_late_ops);
3037 if (ret)
3038 goto fib6_rules_init;
3039
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003040 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00003041 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
3042 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
3043 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Thomas Grafd1896342012-06-18 12:08:33 +00003044 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003045
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003046 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08003047 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00003048 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003049
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003050out:
3051 return ret;
3052
Thomas Grafd1896342012-06-18 12:08:33 +00003053out_register_late_subsys:
3054 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003055fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003056 fib6_rules_cleanup();
3057xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003058 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00003059out_fib6_init:
3060 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003061out_register_subsys:
3062 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07003063out_register_inetpeer:
3064 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003065out_dst_entries:
3066 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003067out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003068 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003069 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003070}
3071
3072void ip6_route_cleanup(void)
3073{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003074 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00003075 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07003076 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003077 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07003079 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003080 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003081 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003082 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083}