blob: 35b07cc33aad97bc71127c131f2fbd616c7fa54f [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
Randy Dunlap4fc268d2006-01-11 12:17:47 -080027#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040029#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/types.h>
31#include <linux/times.h>
32#include <linux/socket.h>
33#include <linux/sockios.h>
34#include <linux/net.h>
35#include <linux/route.h>
36#include <linux/netdevice.h>
37#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090038#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/proc_fs.h>
42#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080043#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090044#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020045#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <net/snmp.h>
47#include <net/ipv6.h>
48#include <net/ip6_fib.h>
49#include <net/ip6_route.h>
50#include <net/ndisc.h>
51#include <net/addrconf.h>
52#include <net/tcp.h>
53#include <linux/rtnetlink.h>
54#include <net/dst.h>
55#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070056#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070057#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59#include <asm/uaccess.h>
60
61#ifdef CONFIG_SYSCTL
62#include <linux/sysctl.h>
63#endif
64
Eric Dumazet21efcfa2011-07-19 20:18:36 +000065static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort,
66 const struct in6_addr *dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080068static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000069static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static struct dst_entry *ip6_negative_advice(struct dst_entry *);
71static void ip6_dst_destroy(struct dst_entry *);
72static void ip6_dst_ifdown(struct dst_entry *,
73 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080074static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76static int ip6_pkt_discard(struct sk_buff *skb);
77static int ip6_pkt_discard_out(struct sk_buff *skb);
78static void ip6_link_failure(struct sk_buff *skb);
79static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
80
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080081#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080082static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000083 const struct in6_addr *prefix, int prefixlen,
84 const struct in6_addr *gwaddr, int ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080085 unsigned pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080086static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000087 const struct in6_addr *prefix, int prefixlen,
88 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080089#endif
90
David S. Miller06582542011-01-27 14:58:42 -080091static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
92{
93 struct rt6_info *rt = (struct rt6_info *) dst;
94 struct inet_peer *peer;
95 u32 *p = NULL;
96
Yan, Zheng8e2ec632011-09-05 21:34:30 +000097 if (!(rt->dst.flags & DST_HOST))
98 return NULL;
99
David S. Miller06582542011-01-27 14:58:42 -0800100 if (!rt->rt6i_peer)
101 rt6_bind_peer(rt, 1);
102
103 peer = rt->rt6i_peer;
104 if (peer) {
105 u32 *old_p = __DST_METRICS_PTR(old);
106 unsigned long prev, new;
107
108 p = peer->metrics;
109 if (inet_metrics_new(peer))
110 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
111
112 new = (unsigned long) p;
113 prev = cmpxchg(&dst->_metrics, old, new);
114
115 if (prev != old) {
116 p = __DST_METRICS_PTR(prev);
117 if (prev & DST_METRICS_READ_ONLY)
118 p = NULL;
119 }
120 }
121 return p;
122}
123
David S. Millerd3aaeb32011-07-18 00:40:17 -0700124static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, const void *daddr)
125{
126 return __neigh_lookup_errno(&nd_tbl, daddr, dst->dev);
127}
128
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800129static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800131 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 .gc = ip6_dst_gc,
133 .gc_thresh = 1024,
134 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800135 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000136 .mtu = ip6_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800137 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 .destroy = ip6_dst_destroy,
139 .ifdown = ip6_dst_ifdown,
140 .negative_advice = ip6_negative_advice,
141 .link_failure = ip6_link_failure,
142 .update_pmtu = ip6_rt_update_pmtu,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700143 .local_out = __ip6_local_out,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700144 .neigh_lookup = ip6_neigh_lookup,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145};
146
Steffen Klassertebb762f2011-11-23 02:12:51 +0000147static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800148{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000149 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
150
151 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800152}
153
David S. Miller14e50e52007-05-24 18:17:54 -0700154static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
155{
156}
157
Held Bernhard0972ddb2011-04-24 22:07:32 +0000158static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
159 unsigned long old)
160{
161 return NULL;
162}
163
David S. Miller14e50e52007-05-24 18:17:54 -0700164static struct dst_ops ip6_dst_blackhole_ops = {
165 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800166 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700167 .destroy = ip6_dst_destroy,
168 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000169 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800170 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700171 .update_pmtu = ip6_rt_blackhole_update_pmtu,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000172 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Millerd3aaeb32011-07-18 00:40:17 -0700173 .neigh_lookup = ip6_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700174};
175
David S. Miller62fa8a82011-01-26 20:51:05 -0800176static const u32 ip6_template_metrics[RTAX_MAX] = {
177 [RTAX_HOPLIMIT - 1] = 255,
178};
179
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800180static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700181 .dst = {
182 .__refcnt = ATOMIC_INIT(1),
183 .__use = 1,
184 .obsolete = -1,
185 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700186 .input = ip6_pkt_discard,
187 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 },
189 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700190 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 .rt6i_metric = ~(u32) 0,
192 .rt6i_ref = ATOMIC_INIT(1),
193};
194
Thomas Graf101367c2006-08-04 03:39:02 -0700195#ifdef CONFIG_IPV6_MULTIPLE_TABLES
196
David S. Miller6723ab52006-10-18 21:20:57 -0700197static int ip6_pkt_prohibit(struct sk_buff *skb);
198static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700199
Adrian Bunk280a34c2008-04-21 02:29:32 -0700200static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700201 .dst = {
202 .__refcnt = ATOMIC_INIT(1),
203 .__use = 1,
204 .obsolete = -1,
205 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700206 .input = ip6_pkt_prohibit,
207 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700208 },
209 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700210 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700211 .rt6i_metric = ~(u32) 0,
212 .rt6i_ref = ATOMIC_INIT(1),
213};
214
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800215static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700216 .dst = {
217 .__refcnt = ATOMIC_INIT(1),
218 .__use = 1,
219 .obsolete = -1,
220 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700221 .input = dst_discard,
222 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700223 },
224 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700225 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700226 .rt6i_metric = ~(u32) 0,
227 .rt6i_ref = ATOMIC_INIT(1),
228};
229
230#endif
231
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232/* allocate dst with ip6_dst_ops */
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700233static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops,
David S. Miller957c6652011-06-24 15:25:00 -0700234 struct net_device *dev,
235 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236{
David S. Miller957c6652011-06-24 15:25:00 -0700237 struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700238
David S. Miller38308472011-12-03 18:02:47 -0500239 if (rt)
Madalin Bucurfbe58182011-09-26 07:04:56 +0000240 memset(&rt->rt6i_table, 0,
David S. Miller38308472011-12-03 18:02:47 -0500241 sizeof(*rt) - sizeof(struct dst_entry));
David S. Millercf911662011-04-28 14:31:47 -0700242
243 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244}
245
246static void ip6_dst_destroy(struct dst_entry *dst)
247{
248 struct rt6_info *rt = (struct rt6_info *)dst;
249 struct inet6_dev *idev = rt->rt6i_idev;
David S. Millerb3419362010-11-30 12:27:11 -0800250 struct inet_peer *peer = rt->rt6i_peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Yan, Zheng8e2ec632011-09-05 21:34:30 +0000252 if (!(rt->dst.flags & DST_HOST))
253 dst_destroy_metrics_generic(dst);
254
David S. Miller38308472011-12-03 18:02:47 -0500255 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 rt->rt6i_idev = NULL;
257 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900258 }
David S. Millerb3419362010-11-30 12:27:11 -0800259 if (peer) {
David S. Millerb3419362010-11-30 12:27:11 -0800260 rt->rt6i_peer = NULL;
261 inet_putpeer(peer);
262 }
263}
264
David S. Miller6431cbc2011-02-07 20:38:06 -0800265static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
266
267static u32 rt6_peer_genid(void)
268{
269 return atomic_read(&__rt6_peer_genid);
270}
271
David S. Millerb3419362010-11-30 12:27:11 -0800272void rt6_bind_peer(struct rt6_info *rt, int create)
273{
274 struct inet_peer *peer;
275
David S. Millerb3419362010-11-30 12:27:11 -0800276 peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create);
277 if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL)
278 inet_putpeer(peer);
David S. Miller6431cbc2011-02-07 20:38:06 -0800279 else
280 rt->rt6i_peer_genid = rt6_peer_genid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281}
282
283static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
284 int how)
285{
286 struct rt6_info *rt = (struct rt6_info *)dst;
287 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800288 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900289 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
David S. Miller38308472011-12-03 18:02:47 -0500291 if (dev != loopback_dev && idev && idev->dev == dev) {
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800292 struct inet6_dev *loopback_idev =
293 in6_dev_get(loopback_dev);
David S. Miller38308472011-12-03 18:02:47 -0500294 if (loopback_idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 rt->rt6i_idev = loopback_idev;
296 in6_dev_put(idev);
297 }
298 }
299}
300
301static __inline__ int rt6_check_expired(const struct rt6_info *rt)
302{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000303 return (rt->rt6i_flags & RTF_EXPIRES) &&
304 time_after(jiffies, rt->rt6i_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305}
306
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000307static inline int rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700308{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000309 return ipv6_addr_type(daddr) &
310 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700311}
312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700314 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 */
316
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800317static inline struct rt6_info *rt6_device_match(struct net *net,
318 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000319 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700321 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
323 struct rt6_info *local = NULL;
324 struct rt6_info *sprt;
325
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900326 if (!oif && ipv6_addr_any(saddr))
327 goto out;
328
Changli Gaod8d1f302010-06-10 23:31:35 -0700329 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900330 struct net_device *dev = sprt->rt6i_dev;
331
332 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 if (dev->ifindex == oif)
334 return sprt;
335 if (dev->flags & IFF_LOOPBACK) {
David S. Miller38308472011-12-03 18:02:47 -0500336 if (!sprt->rt6i_idev ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700338 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900340 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 local->rt6i_idev->dev->ifindex == oif))
342 continue;
343 }
344 local = sprt;
345 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900346 } else {
347 if (ipv6_chk_addr(net, saddr, dev,
348 flags & RT6_LOOKUP_F_IFACE))
349 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900351 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900353 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 if (local)
355 return local;
356
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700357 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800358 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900360out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 return rt;
362}
363
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800364#ifdef CONFIG_IPV6_ROUTER_PREF
365static void rt6_probe(struct rt6_info *rt)
366{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000367 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800368 /*
369 * Okay, this does not seem to be appropriate
370 * for now, however, we need to check if it
371 * is really so; aka Router Reachability Probing.
372 *
373 * Router Reachability Probe MUST be rate-limited
374 * to no more than one per minute.
375 */
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000376 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +0000377 neigh = rt ? dst_get_neighbour_noref(&rt->dst) : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800378 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000379 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800380 read_lock_bh(&neigh->lock);
381 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800382 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800383 struct in6_addr mcaddr;
384 struct in6_addr *target;
385
386 neigh->updated = jiffies;
387 read_unlock_bh(&neigh->lock);
388
389 target = (struct in6_addr *)&neigh->primary_key;
390 addrconf_addr_solict_mult(target, &mcaddr);
391 ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000392 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800393 read_unlock_bh(&neigh->lock);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000394 }
395out:
396 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800397}
398#else
399static inline void rt6_probe(struct rt6_info *rt)
400{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800401}
402#endif
403
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800405 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700407static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800409 struct net_device *dev = rt->rt6i_dev;
David S. Miller161980f2007-04-06 11:42:27 -0700410 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800411 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700412 if ((dev->flags & IFF_LOOPBACK) &&
413 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
414 return 1;
415 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
417
Dave Jonesb6f99a22007-03-22 12:27:49 -0700418static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000420 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800421 int m;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000422
423 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +0000424 neigh = dst_get_neighbour_noref(&rt->dst);
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700425 if (rt->rt6i_flags & RTF_NONEXTHOP ||
426 !(rt->rt6i_flags & RTF_GATEWAY))
427 m = 1;
428 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800429 read_lock_bh(&neigh->lock);
430 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700431 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800432#ifdef CONFIG_IPV6_ROUTER_PREF
433 else if (neigh->nud_state & NUD_FAILED)
434 m = 0;
435#endif
436 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800437 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800438 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800439 } else
440 m = 0;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000441 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800442 return m;
443}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800445static int rt6_score_route(struct rt6_info *rt, int oif,
446 int strict)
447{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700448 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900449
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700450 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700451 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800452 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800453#ifdef CONFIG_IPV6_ROUTER_PREF
454 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
455#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700456 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800457 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800458 return -1;
459 return m;
460}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
David S. Millerf11e6652007-03-24 20:36:25 -0700462static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
463 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800464{
David S. Millerf11e6652007-03-24 20:36:25 -0700465 int m;
466
467 if (rt6_check_expired(rt))
468 goto out;
469
470 m = rt6_score_route(rt, oif, strict);
471 if (m < 0)
472 goto out;
473
474 if (m > *mpri) {
475 if (strict & RT6_LOOKUP_F_REACHABLE)
476 rt6_probe(match);
477 *mpri = m;
478 match = rt;
479 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
480 rt6_probe(rt);
481 }
482
483out:
484 return match;
485}
486
487static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
488 struct rt6_info *rr_head,
489 u32 metric, int oif, int strict)
490{
491 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800492 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
David S. Millerf11e6652007-03-24 20:36:25 -0700494 match = NULL;
495 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700496 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700497 match = find_match(rt, oif, strict, &mpri, match);
498 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700499 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700500 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800501
David S. Millerf11e6652007-03-24 20:36:25 -0700502 return match;
503}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800504
David S. Millerf11e6652007-03-24 20:36:25 -0700505static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
506{
507 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800508 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
David S. Millerf11e6652007-03-24 20:36:25 -0700510 rt0 = fn->rr_ptr;
511 if (!rt0)
512 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
David S. Millerf11e6652007-03-24 20:36:25 -0700514 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800516 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700517 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700518 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700519
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800520 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700521 if (!next || next->rt6i_metric != rt0->rt6i_metric)
522 next = fn->leaf;
523
524 if (next != rt0)
525 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 }
527
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900528 net = dev_net(rt0->rt6i_dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000529 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800532#ifdef CONFIG_IPV6_ROUTE_INFO
533int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000534 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800535{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900536 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800537 struct route_info *rinfo = (struct route_info *) opt;
538 struct in6_addr prefix_buf, *prefix;
539 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900540 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800541 struct rt6_info *rt;
542
543 if (len < sizeof(struct route_info)) {
544 return -EINVAL;
545 }
546
547 /* Sanity check for prefix_len and length */
548 if (rinfo->length > 3) {
549 return -EINVAL;
550 } else if (rinfo->prefix_len > 128) {
551 return -EINVAL;
552 } else if (rinfo->prefix_len > 64) {
553 if (rinfo->length < 2) {
554 return -EINVAL;
555 }
556 } else if (rinfo->prefix_len > 0) {
557 if (rinfo->length < 1) {
558 return -EINVAL;
559 }
560 }
561
562 pref = rinfo->route_pref;
563 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000564 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800565
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900566 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800567
568 if (rinfo->length == 3)
569 prefix = (struct in6_addr *)rinfo->prefix;
570 else {
571 /* this function is safe */
572 ipv6_addr_prefix(&prefix_buf,
573 (struct in6_addr *)rinfo->prefix,
574 rinfo->prefix_len);
575 prefix = &prefix_buf;
576 }
577
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800578 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
579 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800580
581 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700582 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800583 rt = NULL;
584 }
585
586 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800587 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800588 pref);
589 else if (rt)
590 rt->rt6i_flags = RTF_ROUTEINFO |
591 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
592
593 if (rt) {
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900594 if (!addrconf_finite_timeout(lifetime)) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800595 rt->rt6i_flags &= ~RTF_EXPIRES;
596 } else {
597 rt->rt6i_expires = jiffies + HZ * lifetime;
598 rt->rt6i_flags |= RTF_EXPIRES;
599 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700600 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800601 }
602 return 0;
603}
604#endif
605
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800606#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700607do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800608 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700609 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700610 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700611 if (fn->fn_flags & RTN_TL_ROOT) \
612 goto out; \
613 pn = fn->parent; \
614 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800615 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700616 else \
617 fn = pn; \
618 if (fn->fn_flags & RTN_RTINFO) \
619 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700620 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700621 } \
David S. Miller38308472011-12-03 18:02:47 -0500622} while (0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700623
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800624static struct rt6_info *ip6_pol_route_lookup(struct net *net,
625 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500626 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627{
628 struct fib6_node *fn;
629 struct rt6_info *rt;
630
Thomas Grafc71099a2006-08-04 23:20:06 -0700631 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500632 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700633restart:
634 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500635 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
636 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700637out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700638 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700639 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700640 return rt;
641
642}
643
Florian Westphalea6e5742011-09-05 16:05:44 +0200644struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6,
645 int flags)
646{
647 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup);
648}
649EXPORT_SYMBOL_GPL(ip6_route_lookup);
650
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900651struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
652 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700653{
David S. Miller4c9483b2011-03-12 16:22:43 -0500654 struct flowi6 fl6 = {
655 .flowi6_oif = oif,
656 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700657 };
658 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700659 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700660
Thomas Grafadaa70b2006-10-13 15:01:03 -0700661 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500662 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700663 flags |= RT6_LOOKUP_F_HAS_SADDR;
664 }
665
David S. Miller4c9483b2011-03-12 16:22:43 -0500666 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700667 if (dst->error == 0)
668 return (struct rt6_info *) dst;
669
670 dst_release(dst);
671
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 return NULL;
673}
674
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900675EXPORT_SYMBOL(rt6_lookup);
676
Thomas Grafc71099a2006-08-04 23:20:06 -0700677/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 It takes new route entry, the addition fails by any reason the
679 route is freed. In any case, if caller does not hold it, it may
680 be destroyed.
681 */
682
Thomas Graf86872cb2006-08-22 00:01:08 -0700683static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684{
685 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700686 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Thomas Grafc71099a2006-08-04 23:20:06 -0700688 table = rt->rt6i_table;
689 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700690 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700691 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
693 return err;
694}
695
Thomas Graf40e22e82006-08-22 00:00:45 -0700696int ip6_ins_rt(struct rt6_info *rt)
697{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800698 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900699 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800700 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800701 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700702}
703
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000704static struct rt6_info *rt6_alloc_cow(const struct rt6_info *ort,
705 const struct in6_addr *daddr,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000706 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 struct rt6_info *rt;
709
710 /*
711 * Clone the route.
712 */
713
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000714 rt = ip6_rt_copy(ort, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715
716 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800717 struct neighbour *neigh;
718 int attempts = !in_softirq();
719
David S. Miller38308472011-12-03 18:02:47 -0500720 if (!(rt->rt6i_flags & RTF_GATEWAY)) {
David S. Millerbb3c3682011-12-13 17:35:06 -0500721 if (ort->rt6i_dst.plen != 128 &&
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000722 ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900723 rt->rt6i_flags |= RTF_ANYCAST;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000724 rt->rt6i_gateway = *daddr;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900725 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 rt->rt6i_flags |= RTF_CACHE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729#ifdef CONFIG_IPV6_SUBTREES
730 if (rt->rt6i_src.plen && saddr) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000731 rt->rt6i_src.addr = *saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 rt->rt6i_src.plen = 128;
733 }
734#endif
735
David S. Miller14deae42009-01-04 16:04:39 -0800736 retry:
David S. Miller04a6f442011-12-03 18:29:30 -0500737 neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway,
738 rt->rt6i_dev);
David S. Miller14deae42009-01-04 16:04:39 -0800739 if (IS_ERR(neigh)) {
740 struct net *net = dev_net(rt->rt6i_dev);
741 int saved_rt_min_interval =
742 net->ipv6.sysctl.ip6_rt_gc_min_interval;
743 int saved_rt_elasticity =
744 net->ipv6.sysctl.ip6_rt_gc_elasticity;
745
746 if (attempts-- > 0) {
747 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
748 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
749
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000750 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800751
752 net->ipv6.sysctl.ip6_rt_gc_elasticity =
753 saved_rt_elasticity;
754 net->ipv6.sysctl.ip6_rt_gc_min_interval =
755 saved_rt_min_interval;
756 goto retry;
757 }
758
759 if (net_ratelimit())
760 printk(KERN_WARNING
Ulrich Weber7e1b33e2010-09-27 15:02:18 -0700761 "ipv6: Neighbour table overflow.\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700762 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800763 return NULL;
764 }
David S. Miller69cce1d2011-07-17 23:09:49 -0700765 dst_set_neighbour(&rt->dst, neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800767 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800769 return rt;
770}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000772static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
773 const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800774{
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000775 struct rt6_info *rt = ip6_rt_copy(ort, daddr);
776
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800777 if (rt) {
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800778 rt->rt6i_flags |= RTF_CACHE;
David Miller27217452011-12-02 16:52:08 +0000779 dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_noref_raw(&ort->dst)));
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800780 }
781 return rt;
782}
783
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800784static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500785 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786{
787 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800788 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700789 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800791 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700792 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700794 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700797 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800799restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500800 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
802restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700803 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800804
David S. Miller4c9483b2011-03-12 16:22:43 -0500805 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800806 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800807 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef042006-03-20 17:01:24 -0800808 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Changli Gaod8d1f302010-06-10 23:31:35 -0700810 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700811 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800812
David Miller27217452011-12-02 16:52:08 +0000813 if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500814 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800815 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500816 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800817 else
818 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800819
Changli Gaod8d1f302010-06-10 23:31:35 -0700820 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800821 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800822
Changli Gaod8d1f302010-06-10 23:31:35 -0700823 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800824 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700825 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800826 if (!err)
827 goto out2;
828 }
829
830 if (--attempts <= 0)
831 goto out2;
832
833 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700834 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800835 * released someone could insert this route. Relookup.
836 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700837 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800838 goto relookup;
839
840out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800841 if (reachable) {
842 reachable = 0;
843 goto restart_2;
844 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700845 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700846 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700848 rt->dst.lastuse = jiffies;
849 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700850
851 return rt;
852}
853
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800854static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500855 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700856{
David S. Miller4c9483b2011-03-12 16:22:43 -0500857 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700858}
859
Thomas Grafc71099a2006-08-04 23:20:06 -0700860void ip6_route_input(struct sk_buff *skb)
861{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000862 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900863 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700864 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500865 struct flowi6 fl6 = {
866 .flowi6_iif = skb->dev->ifindex,
867 .daddr = iph->daddr,
868 .saddr = iph->saddr,
David S. Miller38308472011-12-03 18:02:47 -0500869 .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK,
David S. Miller4c9483b2011-03-12 16:22:43 -0500870 .flowi6_mark = skb->mark,
871 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700872 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700873
Thomas Goff1d6e55f2009-01-27 22:39:59 -0800874 if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG)
Thomas Grafadaa70b2006-10-13 15:01:03 -0700875 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700876
David S. Miller4c9483b2011-03-12 16:22:43 -0500877 skb_dst_set(skb, fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input));
Thomas Grafc71099a2006-08-04 23:20:06 -0700878}
879
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800880static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500881 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700882{
David S. Miller4c9483b2011-03-12 16:22:43 -0500883 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700884}
885
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700886struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500887 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700888{
889 int flags = 0;
890
David S. Miller4c9483b2011-03-12 16:22:43 -0500891 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700892 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700893
David S. Miller4c9483b2011-03-12 16:22:43 -0500894 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700895 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000896 else if (sk)
897 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700898
David S. Miller4c9483b2011-03-12 16:22:43 -0500899 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900}
901
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900902EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
David S. Miller2774c132011-03-01 14:59:04 -0800904struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700905{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700906 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700907 struct dst_entry *new = NULL;
908
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700909 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700910 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700911 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
912
Changli Gaod8d1f302010-06-10 23:31:35 -0700913 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700914
David S. Miller14e50e52007-05-24 18:17:54 -0700915 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800916 new->input = dst_discard;
917 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700918
Eric Dumazet21efcfa2011-07-19 20:18:36 +0000919 if (dst_metrics_read_only(&ort->dst))
920 new->_metrics = ort->dst._metrics;
921 else
922 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700923 rt->rt6i_idev = ort->rt6i_idev;
924 if (rt->rt6i_idev)
925 in6_dev_hold(rt->rt6i_idev);
926 rt->rt6i_expires = 0;
927
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000928 rt->rt6i_gateway = ort->rt6i_gateway;
David S. Miller14e50e52007-05-24 18:17:54 -0700929 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
930 rt->rt6i_metric = 0;
931
932 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
933#ifdef CONFIG_IPV6_SUBTREES
934 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
935#endif
936
937 dst_free(new);
938 }
939
David S. Miller69ead7a2011-03-01 14:45:33 -0800940 dst_release(dst_orig);
941 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -0700942}
David S. Miller14e50e52007-05-24 18:17:54 -0700943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944/*
945 * Destination cache support functions
946 */
947
948static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
949{
950 struct rt6_info *rt;
951
952 rt = (struct rt6_info *) dst;
953
David S. Miller6431cbc2011-02-07 20:38:06 -0800954 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
955 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
956 if (!rt->rt6i_peer)
957 rt6_bind_peer(rt, 0);
958 rt->rt6i_peer_genid = rt6_peer_genid();
959 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -0800961 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 return NULL;
963}
964
965static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
966{
967 struct rt6_info *rt = (struct rt6_info *) dst;
968
969 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000970 if (rt->rt6i_flags & RTF_CACHE) {
971 if (rt6_check_expired(rt)) {
972 ip6_del_rt(rt);
973 dst = NULL;
974 }
975 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000977 dst = NULL;
978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000980 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981}
982
983static void ip6_link_failure(struct sk_buff *skb)
984{
985 struct rt6_info *rt;
986
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +0000987 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988
Eric Dumazetadf30902009-06-02 05:19:30 +0000989 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 if (rt) {
David S. Miller38308472011-12-03 18:02:47 -0500991 if (rt->rt6i_flags & RTF_CACHE) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700992 dst_set_expires(&rt->dst, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 rt->rt6i_flags |= RTF_EXPIRES;
994 } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
995 rt->rt6i_node->fn_sernum = -1;
996 }
997}
998
999static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1000{
1001 struct rt6_info *rt6 = (struct rt6_info*)dst;
1002
1003 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
1004 rt6->rt6i_flags |= RTF_MODIFIED;
1005 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -08001006 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -08001008 features |= RTAX_FEATURE_ALLFRAG;
1009 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 }
David S. Millerdefb3512010-12-08 21:16:57 -08001011 dst_metric_set(dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 }
1013}
1014
David S. Miller0dbaee32010-12-13 12:52:14 -08001015static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016{
David S. Miller0dbaee32010-12-13 12:52:14 -08001017 struct net_device *dev = dst->dev;
1018 unsigned int mtu = dst_mtu(dst);
1019 struct net *net = dev_net(dev);
1020
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1022
Daniel Lezcano55786892008-03-04 13:47:47 -08001023 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1024 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
1026 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001027 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1028 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1029 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 * rely only on pmtu discovery"
1031 */
1032 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1033 mtu = IPV6_MAXPLEN;
1034 return mtu;
1035}
1036
Steffen Klassertebb762f2011-11-23 02:12:51 +00001037static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08001038{
David S. Millerd33e4552010-12-14 13:01:14 -08001039 struct inet6_dev *idev;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00001040 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
1041
1042 if (mtu)
1043 return mtu;
1044
1045 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08001046
1047 rcu_read_lock();
1048 idev = __in6_dev_get(dst->dev);
1049 if (idev)
1050 mtu = idev->cnf.mtu6;
1051 rcu_read_unlock();
1052
1053 return mtu;
1054}
1055
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001056static struct dst_entry *icmp6_dst_gc_list;
1057static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001058
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001059struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 struct neighbour *neigh,
David S. Miller87a11572011-12-06 17:04:13 -05001061 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062{
David S. Miller87a11572011-12-06 17:04:13 -05001063 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 struct rt6_info *rt;
1065 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001066 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
David S. Miller38308472011-12-03 18:02:47 -05001068 if (unlikely(!idev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return NULL;
1070
David S. Miller957c6652011-06-24 15:25:00 -07001071 rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05001072 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05001074 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 goto out;
1076 }
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 if (neigh)
1079 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001080 else {
David S. Miller87a11572011-12-06 17:04:13 -05001081 neigh = __neigh_lookup_errno(&nd_tbl, &fl6->daddr, dev);
David S. Millerb43faac2011-12-13 16:48:21 -05001082 if (IS_ERR(neigh)) {
1083 dst_free(&rt->dst);
1084 return ERR_CAST(neigh);
1085 }
David S. Miller14deae42009-01-04 16:04:39 -08001086 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001088 rt->dst.flags |= DST_HOST;
1089 rt->dst.output = ip6_output;
David S. Miller69cce1d2011-07-17 23:09:49 -07001090 dst_set_neighbour(&rt->dst, neigh);
Changli Gaod8d1f302010-06-10 23:31:35 -07001091 atomic_set(&rt->dst.__refcnt, 1);
David S. Miller87a11572011-12-06 17:04:13 -05001092 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001093 rt->rt6i_dst.plen = 128;
1094 rt->rt6i_idev = idev;
Gao feng70116872011-10-28 02:46:57 +00001095 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001097 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001098 rt->dst.next = icmp6_dst_gc_list;
1099 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001100 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101
Daniel Lezcano55786892008-03-04 13:47:47 -08001102 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103
David S. Miller87a11572011-12-06 17:04:13 -05001104 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
1105
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106out:
David S. Miller87a11572011-12-06 17:04:13 -05001107 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108}
1109
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001110int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001112 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001113 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001115 spin_lock_bh(&icmp6_dst_lock);
1116 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001117
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 while ((dst = *pprev) != NULL) {
1119 if (!atomic_read(&dst->__refcnt)) {
1120 *pprev = dst->next;
1121 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 } else {
1123 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001124 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 }
1126 }
1127
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001128 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001129
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001130 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131}
1132
David S. Miller1e493d12008-09-10 17:27:15 -07001133static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1134 void *arg)
1135{
1136 struct dst_entry *dst, **pprev;
1137
1138 spin_lock_bh(&icmp6_dst_lock);
1139 pprev = &icmp6_dst_gc_list;
1140 while ((dst = *pprev) != NULL) {
1141 struct rt6_info *rt = (struct rt6_info *) dst;
1142 if (func(rt, arg)) {
1143 *pprev = dst->next;
1144 dst_free(dst);
1145 } else {
1146 pprev = &dst->next;
1147 }
1148 }
1149 spin_unlock_bh(&icmp6_dst_lock);
1150}
1151
Daniel Lezcano569d3642008-01-18 03:56:57 -08001152static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001155 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001156 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1157 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1158 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1159 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1160 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001161 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
Eric Dumazetfc66f952010-10-08 06:37:34 +00001163 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001164 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001165 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 goto out;
1167
Benjamin Thery6891a342008-03-04 13:49:47 -08001168 net->ipv6.ip6_rt_gc_expire++;
1169 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1170 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001171 entries = dst_entries_get_slow(ops);
1172 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001173 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001175 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001176 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177}
1178
1179/* Clean host part of a prefix. Not necessary in radix tree,
1180 but results in cleaner routing tables.
1181
1182 Remove it only when all the things will work!
1183 */
1184
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001185int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186{
David S. Miller5170ae82010-12-12 21:35:57 -08001187 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001188 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001189 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001190 struct inet6_dev *idev;
1191
1192 rcu_read_lock();
1193 idev = __in6_dev_get(dev);
1194 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001195 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001196 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001197 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001198 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 }
1200 return hoplimit;
1201}
David S. Millerabbf46a2010-12-12 21:14:46 -08001202EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203
1204/*
1205 *
1206 */
1207
Thomas Graf86872cb2006-08-22 00:01:08 -07001208int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209{
1210 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001211 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 struct rt6_info *rt = NULL;
1213 struct net_device *dev = NULL;
1214 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001215 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 int addr_type;
1217
Thomas Graf86872cb2006-08-22 00:01:08 -07001218 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 return -EINVAL;
1220#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001221 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 return -EINVAL;
1223#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001224 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001226 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 if (!dev)
1228 goto out;
1229 idev = in6_dev_get(dev);
1230 if (!idev)
1231 goto out;
1232 }
1233
Thomas Graf86872cb2006-08-22 00:01:08 -07001234 if (cfg->fc_metric == 0)
1235 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
Matti Vaittinend71314b2011-11-14 00:14:49 +00001237 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05001238 if (cfg->fc_nlinfo.nlh &&
1239 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001240 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001241 if (!table) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00001242 printk(KERN_WARNING "IPv6: NLM_F_CREATE should be specified when creating new route\n");
1243 table = fib6_new_table(net, cfg->fc_table);
1244 }
1245 } else {
1246 table = fib6_new_table(net, cfg->fc_table);
1247 }
David S. Miller38308472011-12-03 18:02:47 -05001248
1249 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001250 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07001251
David S. Miller957c6652011-06-24 15:25:00 -07001252 rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL, DST_NOCOUNT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
David S. Miller38308472011-12-03 18:02:47 -05001254 if (!rt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 err = -ENOMEM;
1256 goto out;
1257 }
1258
Changli Gaod8d1f302010-06-10 23:31:35 -07001259 rt->dst.obsolete = -1;
YOSHIFUJI Hideaki6f704992008-05-19 16:56:11 -07001260 rt->rt6i_expires = (cfg->fc_flags & RTF_EXPIRES) ?
1261 jiffies + clock_t_to_jiffies(cfg->fc_expires) :
1262 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
Thomas Graf86872cb2006-08-22 00:01:08 -07001264 if (cfg->fc_protocol == RTPROT_UNSPEC)
1265 cfg->fc_protocol = RTPROT_BOOT;
1266 rt->rt6i_protocol = cfg->fc_protocol;
1267
1268 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269
1270 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001271 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001272 else if (cfg->fc_flags & RTF_LOCAL)
1273 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001275 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276
Changli Gaod8d1f302010-06-10 23:31:35 -07001277 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278
Thomas Graf86872cb2006-08-22 00:01:08 -07001279 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1280 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001282 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001284 if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
1285 u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
1286 if (!metrics) {
1287 err = -ENOMEM;
1288 goto out;
1289 }
1290 dst_init_metrics(&rt->dst, metrics, 0);
1291 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001293 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1294 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295#endif
1296
Thomas Graf86872cb2006-08-22 00:01:08 -07001297 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
1299 /* We cannot add true routes via loopback here,
1300 they would result in kernel looping; promote them to reject routes
1301 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001302 if ((cfg->fc_flags & RTF_REJECT) ||
David S. Miller38308472011-12-03 18:02:47 -05001303 (dev && (dev->flags & IFF_LOOPBACK) &&
1304 !(addr_type & IPV6_ADDR_LOOPBACK) &&
1305 !(cfg->fc_flags & RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001307 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 if (dev) {
1309 dev_put(dev);
1310 in6_dev_put(idev);
1311 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001312 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 dev_hold(dev);
1314 idev = in6_dev_get(dev);
1315 if (!idev) {
1316 err = -ENODEV;
1317 goto out;
1318 }
1319 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001320 rt->dst.output = ip6_pkt_discard_out;
1321 rt->dst.input = ip6_pkt_discard;
1322 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1324 goto install_route;
1325 }
1326
Thomas Graf86872cb2006-08-22 00:01:08 -07001327 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001328 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 int gwa_type;
1330
Thomas Graf86872cb2006-08-22 00:01:08 -07001331 gw_addr = &cfg->fc_gateway;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001332 rt->rt6i_gateway = *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 gwa_type = ipv6_addr_type(gw_addr);
1334
1335 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1336 struct rt6_info *grt;
1337
1338 /* IPv6 strictly inhibits using not link-local
1339 addresses as nexthop address.
1340 Otherwise, router will not able to send redirects.
1341 It is very good, but in some (rare!) circumstances
1342 (SIT, PtP, NBMA NOARP links) it is handy to allow
1343 some exceptions. --ANK
1344 */
1345 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001346 if (!(gwa_type & IPV6_ADDR_UNICAST))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 goto out;
1348
Daniel Lezcano55786892008-03-04 13:47:47 -08001349 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
1351 err = -EHOSTUNREACH;
David S. Miller38308472011-12-03 18:02:47 -05001352 if (!grt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 goto out;
1354 if (dev) {
1355 if (dev != grt->rt6i_dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001356 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 goto out;
1358 }
1359 } else {
1360 dev = grt->rt6i_dev;
1361 idev = grt->rt6i_idev;
1362 dev_hold(dev);
1363 in6_dev_hold(grt->rt6i_idev);
1364 }
David S. Miller38308472011-12-03 18:02:47 -05001365 if (!(grt->rt6i_flags & RTF_GATEWAY))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001367 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
1369 if (err)
1370 goto out;
1371 }
1372 err = -EINVAL;
David S. Miller38308472011-12-03 18:02:47 -05001373 if (!dev || (dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 goto out;
1375 }
1376
1377 err = -ENODEV;
David S. Miller38308472011-12-03 18:02:47 -05001378 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 goto out;
1380
Daniel Walterc3968a82011-04-13 21:10:57 +00001381 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1382 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1383 err = -EINVAL;
1384 goto out;
1385 }
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001386 rt->rt6i_prefsrc.addr = cfg->fc_prefsrc;
Daniel Walterc3968a82011-04-13 21:10:57 +00001387 rt->rt6i_prefsrc.plen = 128;
1388 } else
1389 rt->rt6i_prefsrc.plen = 0;
1390
Thomas Graf86872cb2006-08-22 00:01:08 -07001391 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
David S. Miller69cce1d2011-07-17 23:09:49 -07001392 struct neighbour *n = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);
1393 if (IS_ERR(n)) {
1394 err = PTR_ERR(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 goto out;
1396 }
David S. Miller69cce1d2011-07-17 23:09:49 -07001397 dst_set_neighbour(&rt->dst, n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 }
1399
Thomas Graf86872cb2006-08-22 00:01:08 -07001400 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
1402install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001403 if (cfg->fc_mx) {
1404 struct nlattr *nla;
1405 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406
Thomas Graf86872cb2006-08-22 00:01:08 -07001407 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001408 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001409
1410 if (type) {
1411 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 err = -EINVAL;
1413 goto out;
1414 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001415
David S. Millerdefb3512010-12-08 21:16:57 -08001416 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 }
1419 }
1420
Changli Gaod8d1f302010-06-10 23:31:35 -07001421 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001423 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001424
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001425 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001426
Thomas Graf86872cb2006-08-22 00:01:08 -07001427 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
1429out:
1430 if (dev)
1431 dev_put(dev);
1432 if (idev)
1433 in6_dev_put(idev);
1434 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001435 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 return err;
1437}
1438
Thomas Graf86872cb2006-08-22 00:01:08 -07001439static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440{
1441 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001442 struct fib6_table *table;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001443 struct net *net = dev_net(rt->rt6i_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001445 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001446 return -ENOENT;
1447
Thomas Grafc71099a2006-08-04 23:20:06 -07001448 table = rt->rt6i_table;
1449 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450
Thomas Graf86872cb2006-08-22 00:01:08 -07001451 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001452 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
Thomas Grafc71099a2006-08-04 23:20:06 -07001454 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
1456 return err;
1457}
1458
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001459int ip6_del_rt(struct rt6_info *rt)
1460{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001461 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001462 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001463 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001464 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001465}
1466
Thomas Graf86872cb2006-08-22 00:01:08 -07001467static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468{
Thomas Grafc71099a2006-08-04 23:20:06 -07001469 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 struct fib6_node *fn;
1471 struct rt6_info *rt;
1472 int err = -ESRCH;
1473
Daniel Lezcano55786892008-03-04 13:47:47 -08001474 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05001475 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001476 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477
Thomas Grafc71099a2006-08-04 23:20:06 -07001478 read_lock_bh(&table->tb6_lock);
1479
1480 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001481 &cfg->fc_dst, cfg->fc_dst_len,
1482 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001483
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001485 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001486 if (cfg->fc_ifindex &&
David S. Miller38308472011-12-03 18:02:47 -05001487 (!rt->rt6i_dev ||
Thomas Graf86872cb2006-08-22 00:01:08 -07001488 rt->rt6i_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001490 if (cfg->fc_flags & RTF_GATEWAY &&
1491 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001493 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001495 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001496 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
Thomas Graf86872cb2006-08-22 00:01:08 -07001498 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 }
1500 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001501 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503 return err;
1504}
1505
1506/*
1507 * Handle redirects
1508 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001509struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001510 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001511 struct in6_addr gateway;
1512};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001514static struct rt6_info *__ip6_route_redirect(struct net *net,
1515 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001516 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001517 int flags)
1518{
David S. Miller4c9483b2011-03-12 16:22:43 -05001519 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001520 struct rt6_info *rt;
1521 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001522
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001524 * Get the "current" route for this destination and
1525 * check if the redirect has come from approriate router.
1526 *
1527 * RFC 2461 specifies that redirects should only be
1528 * accepted if they come from the nexthop to the target.
1529 * Due to the way the routes are chosen, this notion
1530 * is a bit fuzzy and one might need to check all possible
1531 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
Thomas Grafc71099a2006-08-04 23:20:06 -07001534 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001535 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001536restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001537 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001538 /*
1539 * Current route is on-link; redirect is always invalid.
1540 *
1541 * Seems, previous statement is not true. It could
1542 * be node, which looks for us as on-link (f.e. proxy ndisc)
1543 * But then router serving it might decide, that we should
1544 * know truth 8)8) --ANK (980726).
1545 */
1546 if (rt6_check_expired(rt))
1547 continue;
1548 if (!(rt->rt6i_flags & RTF_GATEWAY))
1549 continue;
David S. Miller4c9483b2011-03-12 16:22:43 -05001550 if (fl6->flowi6_oif != rt->rt6i_dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001551 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001552 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001553 continue;
1554 break;
1555 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001556
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001557 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001558 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001559 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001560out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001561 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001562
1563 read_unlock_bh(&table->tb6_lock);
1564
1565 return rt;
1566};
1567
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001568static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1569 const struct in6_addr *src,
1570 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001571 struct net_device *dev)
1572{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001573 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001574 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001575 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001576 .fl6 = {
1577 .flowi6_oif = dev->ifindex,
1578 .daddr = *dest,
1579 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001580 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001581 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001582
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001583 rdfl.gateway = *gateway;
Brian Haley86c36ce2009-10-07 13:58:01 -07001584
Thomas Grafadaa70b2006-10-13 15:01:03 -07001585 if (rt6_need_strict(dest))
1586 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001587
David S. Miller4c9483b2011-03-12 16:22:43 -05001588 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001589 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001590}
1591
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001592void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
1593 const struct in6_addr *saddr,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001594 struct neighbour *neigh, u8 *lladdr, int on_link)
1595{
1596 struct rt6_info *rt, *nrt = NULL;
1597 struct netevent_redirect netevent;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001598 struct net *net = dev_net(neigh->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001599
1600 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1601
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001602 if (rt == net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 if (net_ratelimit())
1604 printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
1605 "for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001606 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 }
1608
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 /*
1610 * We have finally decided to accept it.
1611 */
1612
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001613 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1615 NEIGH_UPDATE_F_OVERRIDE|
1616 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1617 NEIGH_UPDATE_F_ISROUTER))
1618 );
1619
1620 /*
1621 * Redirect received -> path was valid.
1622 * Look, redirects are sent only in response to data packets,
1623 * so that this nexthop apparently is reachable. --ANK
1624 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001625 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
1627 /* Duplicate redirect: silently ignore. */
David Miller27217452011-12-02 16:52:08 +00001628 if (neigh == dst_get_neighbour_noref_raw(&rt->dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 goto out;
1630
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001631 nrt = ip6_rt_copy(rt, dest);
David S. Miller38308472011-12-03 18:02:47 -05001632 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 goto out;
1634
1635 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1636 if (on_link)
1637 nrt->rt6i_flags &= ~RTF_GATEWAY;
1638
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001639 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
David S. Miller69cce1d2011-07-17 23:09:49 -07001640 dst_set_neighbour(&nrt->dst, neigh_clone(neigh));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Thomas Graf40e22e82006-08-22 00:00:45 -07001642 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 goto out;
1644
Changli Gaod8d1f302010-06-10 23:31:35 -07001645 netevent.old = &rt->dst;
1646 netevent.new = &nrt->dst;
Tom Tucker8d717402006-07-30 20:43:36 -07001647 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1648
David S. Miller38308472011-12-03 18:02:47 -05001649 if (rt->rt6i_flags & RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001650 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 return;
1652 }
1653
1654out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001655 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656}
1657
1658/*
1659 * Handle ICMP "packet too big" messages
1660 * i.e. Path MTU discovery
1661 */
1662
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001663static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr,
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001664 struct net *net, u32 pmtu, int ifindex)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665{
1666 struct rt6_info *rt, *nrt;
1667 int allfrag = 0;
Andrey Vagind3052b52010-12-11 15:20:11 +00001668again:
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001669 rt = rt6_lookup(net, daddr, saddr, ifindex, 0);
David S. Miller38308472011-12-03 18:02:47 -05001670 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 return;
1672
Andrey Vagind3052b52010-12-11 15:20:11 +00001673 if (rt6_check_expired(rt)) {
1674 ip6_del_rt(rt);
1675 goto again;
1676 }
1677
Changli Gaod8d1f302010-06-10 23:31:35 -07001678 if (pmtu >= dst_mtu(&rt->dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 goto out;
1680
1681 if (pmtu < IPV6_MIN_MTU) {
1682 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001683 * According to RFC2460, PMTU is set to the IPv6 Minimum Link
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 * MTU (1280) and a fragment header should always be included
1685 * after a node receiving Too Big message reporting PMTU is
1686 * less than the IPv6 Minimum Link MTU.
1687 */
1688 pmtu = IPV6_MIN_MTU;
1689 allfrag = 1;
1690 }
1691
1692 /* New mtu received -> path was valid.
1693 They are sent only in response to data packets,
1694 so that this nexthop apparently is reachable. --ANK
1695 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001696 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697
1698 /* Host route. If it is static, it would be better
1699 not to override it, but add new one, so that
1700 when cache entry will expire old pmtu
1701 would return automatically.
1702 */
1703 if (rt->rt6i_flags & RTF_CACHE) {
David S. Millerdefb3512010-12-08 21:16:57 -08001704 dst_metric_set(&rt->dst, RTAX_MTU, pmtu);
1705 if (allfrag) {
1706 u32 features = dst_metric(&rt->dst, RTAX_FEATURES);
1707 features |= RTAX_FEATURE_ALLFRAG;
1708 dst_metric_set(&rt->dst, RTAX_FEATURES, features);
1709 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001710 dst_set_expires(&rt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
1712 goto out;
1713 }
1714
1715 /* Network route.
1716 Two cases are possible:
1717 1. It is connected route. Action: COW
1718 2. It is gatewayed route or NONEXTHOP route. Action: clone it.
1719 */
David Miller27217452011-12-02 16:52:08 +00001720 if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001721 nrt = rt6_alloc_cow(rt, daddr, saddr);
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001722 else
1723 nrt = rt6_alloc_clone(rt, daddr);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001724
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001725 if (nrt) {
David S. Millerdefb3512010-12-08 21:16:57 -08001726 dst_metric_set(&nrt->dst, RTAX_MTU, pmtu);
1727 if (allfrag) {
1728 u32 features = dst_metric(&nrt->dst, RTAX_FEATURES);
1729 features |= RTAX_FEATURE_ALLFRAG;
1730 dst_metric_set(&nrt->dst, RTAX_FEATURES, features);
1731 }
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001732
1733 /* According to RFC 1981, detecting PMTU increase shouldn't be
1734 * happened within 5 mins, the recommended timer is 10 mins.
1735 * Here this route expiration time is set to ip6_rt_mtu_expires
1736 * which is 10 mins. After 10 mins the decreased pmtu is expired
1737 * and detecting PMTU increase will be automatically happened.
1738 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001739 dst_set_expires(&nrt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001740 nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
1741
Thomas Graf40e22e82006-08-22 00:00:45 -07001742 ip6_ins_rt(nrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001745 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746}
1747
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001748void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr,
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001749 struct net_device *dev, u32 pmtu)
1750{
1751 struct net *net = dev_net(dev);
1752
1753 /*
1754 * RFC 1981 states that a node "MUST reduce the size of the packets it
1755 * is sending along the path" that caused the Packet Too Big message.
1756 * Since it's not possible in the general case to determine which
1757 * interface was used to send the original packet, we update the MTU
1758 * on the interface that will be used to send future packets. We also
1759 * update the MTU on the interface that received the Packet Too Big in
1760 * case the original packet was forced out that interface with
1761 * SO_BINDTODEVICE or similar. This is the next best thing to the
1762 * correct behaviour, which would be to update the MTU on all
1763 * interfaces.
1764 */
1765 rt6_do_pmtu_disc(daddr, saddr, net, pmtu, 0);
1766 rt6_do_pmtu_disc(daddr, saddr, net, pmtu, dev->ifindex);
1767}
1768
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769/*
1770 * Misc support functions
1771 */
1772
Eric Dumazet21efcfa2011-07-19 20:18:36 +00001773static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort,
1774 const struct in6_addr *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001776 struct net *net = dev_net(ort->rt6i_dev);
David S. Miller5c1e6aa2011-04-28 14:13:38 -07001777 struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
David S. Miller957c6652011-06-24 15:25:00 -07001778 ort->dst.dev, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779
1780 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001781 rt->dst.input = ort->dst.input;
1782 rt->dst.output = ort->dst.output;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001783 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001785 rt->rt6i_dst.addr = *dest;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00001786 rt->rt6i_dst.plen = 128;
David S. Millerdefb3512010-12-08 21:16:57 -08001787 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001788 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 rt->rt6i_idev = ort->rt6i_idev;
1790 if (rt->rt6i_idev)
1791 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001792 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 rt->rt6i_expires = 0;
1794
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001795 rt->rt6i_gateway = ort->rt6i_gateway;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
1797 rt->rt6i_metric = 0;
1798
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799#ifdef CONFIG_IPV6_SUBTREES
1800 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1801#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001802 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001803 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 }
1805 return rt;
1806}
1807
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001808#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001809static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001810 const struct in6_addr *prefix, int prefixlen,
1811 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001812{
1813 struct fib6_node *fn;
1814 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001815 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001816
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001817 table = fib6_get_table(net, RT6_TABLE_INFO);
David S. Miller38308472011-12-03 18:02:47 -05001818 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001819 return NULL;
1820
1821 write_lock_bh(&table->tb6_lock);
1822 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001823 if (!fn)
1824 goto out;
1825
Changli Gaod8d1f302010-06-10 23:31:35 -07001826 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001827 if (rt->rt6i_dev->ifindex != ifindex)
1828 continue;
1829 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1830 continue;
1831 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1832 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001833 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001834 break;
1835 }
1836out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001837 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001838 return rt;
1839}
1840
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001841static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001842 const struct in6_addr *prefix, int prefixlen,
1843 const struct in6_addr *gwaddr, int ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001844 unsigned pref)
1845{
Thomas Graf86872cb2006-08-22 00:01:08 -07001846 struct fib6_config cfg = {
1847 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001848 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001849 .fc_ifindex = ifindex,
1850 .fc_dst_len = prefixlen,
1851 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1852 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001853 .fc_nlinfo.pid = 0,
1854 .fc_nlinfo.nlh = NULL,
1855 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001856 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001857
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001858 cfg.fc_dst = *prefix;
1859 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07001860
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001861 /* We should treat it as a default route if prefix length is 0. */
1862 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001863 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001864
Thomas Graf86872cb2006-08-22 00:01:08 -07001865 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001866
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001867 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001868}
1869#endif
1870
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001871struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001872{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001874 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001876 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001877 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001878 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879
Thomas Grafc71099a2006-08-04 23:20:06 -07001880 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001881 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 if (dev == rt->rt6i_dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001883 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1885 break;
1886 }
1887 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001888 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001889 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 return rt;
1891}
1892
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001893struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001894 struct net_device *dev,
1895 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896{
Thomas Graf86872cb2006-08-22 00:01:08 -07001897 struct fib6_config cfg = {
1898 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001899 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001900 .fc_ifindex = dev->ifindex,
1901 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1902 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001903 .fc_nlinfo.pid = 0,
1904 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001905 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001906 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001908 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909
Thomas Graf86872cb2006-08-22 00:01:08 -07001910 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 return rt6_get_dflt_router(gwaddr, dev);
1913}
1914
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001915void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916{
1917 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001918 struct fib6_table *table;
1919
1920 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001921 table = fib6_get_table(net, RT6_TABLE_DFLT);
David S. Miller38308472011-12-03 18:02:47 -05001922 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07001923 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924
1925restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001926 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001927 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001929 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001930 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001931 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 goto restart;
1933 }
1934 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001935 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936}
1937
Daniel Lezcano55786892008-03-04 13:47:47 -08001938static void rtmsg_to_fib6_config(struct net *net,
1939 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001940 struct fib6_config *cfg)
1941{
1942 memset(cfg, 0, sizeof(*cfg));
1943
1944 cfg->fc_table = RT6_TABLE_MAIN;
1945 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1946 cfg->fc_metric = rtmsg->rtmsg_metric;
1947 cfg->fc_expires = rtmsg->rtmsg_info;
1948 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1949 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1950 cfg->fc_flags = rtmsg->rtmsg_flags;
1951
Daniel Lezcano55786892008-03-04 13:47:47 -08001952 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001953
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001954 cfg->fc_dst = rtmsg->rtmsg_dst;
1955 cfg->fc_src = rtmsg->rtmsg_src;
1956 cfg->fc_gateway = rtmsg->rtmsg_gateway;
Thomas Graf86872cb2006-08-22 00:01:08 -07001957}
1958
Daniel Lezcano55786892008-03-04 13:47:47 -08001959int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960{
Thomas Graf86872cb2006-08-22 00:01:08 -07001961 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 struct in6_rtmsg rtmsg;
1963 int err;
1964
1965 switch(cmd) {
1966 case SIOCADDRT: /* Add a route */
1967 case SIOCDELRT: /* Delete a route */
1968 if (!capable(CAP_NET_ADMIN))
1969 return -EPERM;
1970 err = copy_from_user(&rtmsg, arg,
1971 sizeof(struct in6_rtmsg));
1972 if (err)
1973 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001974
Daniel Lezcano55786892008-03-04 13:47:47 -08001975 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001976
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 rtnl_lock();
1978 switch (cmd) {
1979 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001980 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 break;
1982 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001983 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 break;
1985 default:
1986 err = -EINVAL;
1987 }
1988 rtnl_unlock();
1989
1990 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001991 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992
1993 return -EINVAL;
1994}
1995
1996/*
1997 * Drop the packet on the floor
1998 */
1999
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07002000static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002002 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00002003 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002004 switch (ipstats_mib_noroutes) {
2005 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07002006 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00002007 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002008 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2009 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002010 break;
2011 }
2012 /* FALLTHROUGH */
2013 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07002014 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
2015 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002016 break;
2017 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002018 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 kfree_skb(skb);
2020 return 0;
2021}
2022
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002023static int ip6_pkt_discard(struct sk_buff *skb)
2024{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002025 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002026}
2027
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03002028static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029{
Eric Dumazetadf30902009-06-02 05:19:30 +00002030 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002031 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032}
2033
David S. Miller6723ab52006-10-18 21:20:57 -07002034#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2035
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002036static int ip6_pkt_prohibit(struct sk_buff *skb)
2037{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002038 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002039}
2040
2041static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2042{
Eric Dumazetadf30902009-06-02 05:19:30 +00002043 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002044 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002045}
2046
David S. Miller6723ab52006-10-18 21:20:57 -07002047#endif
2048
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049/*
2050 * Allocate a dst for local (unicast / anycast) address.
2051 */
2052
2053struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2054 const struct in6_addr *addr,
David S. Miller8f031512011-12-06 16:48:14 -05002055 bool anycast)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002057 struct net *net = dev_net(idev->dev);
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002058 struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
David S. Miller957c6652011-06-24 15:25:00 -07002059 net->loopback_dev, 0);
David S. Miller14deae42009-01-04 16:04:39 -08002060 struct neighbour *neigh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061
David S. Miller38308472011-12-03 18:02:47 -05002062 if (!rt) {
Ben Greear40385652010-11-08 12:33:48 +00002063 if (net_ratelimit())
2064 pr_warning("IPv6: Maximum number of routes reached,"
2065 " consider increasing route/max_size.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002067 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 in6_dev_hold(idev);
2070
David S. Miller11d53b42011-06-24 15:23:34 -07002071 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002072 rt->dst.input = ip6_input;
2073 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002075 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076
2077 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002078 if (anycast)
2079 rt->rt6i_flags |= RTF_ANYCAST;
2080 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller04a6f442011-12-03 18:29:30 -05002082 neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, rt->rt6i_dev);
David S. Miller14deae42009-01-04 16:04:39 -08002083 if (IS_ERR(neigh)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002084 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -08002085
David S. Miller29546a62011-03-03 12:10:37 -08002086 return ERR_CAST(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 }
David S. Miller69cce1d2011-07-17 23:09:49 -07002088 dst_set_neighbour(&rt->dst, neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002090 rt->rt6i_dst.addr = *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002092 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093
Changli Gaod8d1f302010-06-10 23:31:35 -07002094 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095
2096 return rt;
2097}
2098
Daniel Walterc3968a82011-04-13 21:10:57 +00002099int ip6_route_get_saddr(struct net *net,
2100 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002101 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002102 unsigned int prefs,
2103 struct in6_addr *saddr)
2104{
2105 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2106 int err = 0;
2107 if (rt->rt6i_prefsrc.plen)
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002108 *saddr = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002109 else
2110 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2111 daddr, prefs, saddr);
2112 return err;
2113}
2114
2115/* remove deleted ip from prefsrc entries */
2116struct arg_dev_net_ip {
2117 struct net_device *dev;
2118 struct net *net;
2119 struct in6_addr *addr;
2120};
2121
2122static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2123{
2124 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2125 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2126 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2127
David S. Miller38308472011-12-03 18:02:47 -05002128 if (((void *)rt->rt6i_dev == dev || !dev) &&
Daniel Walterc3968a82011-04-13 21:10:57 +00002129 rt != net->ipv6.ip6_null_entry &&
2130 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2131 /* remove prefsrc entry */
2132 rt->rt6i_prefsrc.plen = 0;
2133 }
2134 return 0;
2135}
2136
2137void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2138{
2139 struct net *net = dev_net(ifp->idev->dev);
2140 struct arg_dev_net_ip adni = {
2141 .dev = ifp->idev->dev,
2142 .net = net,
2143 .addr = &ifp->addr,
2144 };
2145 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2146}
2147
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002148struct arg_dev_net {
2149 struct net_device *dev;
2150 struct net *net;
2151};
2152
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153static int fib6_ifdown(struct rt6_info *rt, void *arg)
2154{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002155 const struct arg_dev_net *adn = arg;
2156 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002157
David S. Miller38308472011-12-03 18:02:47 -05002158 if ((rt->rt6i_dev == dev || !dev) &&
David S. Millerc159d302011-12-26 15:24:36 -05002159 rt != adn->net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 return -1;
David S. Millerc159d302011-12-26 15:24:36 -05002161
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 return 0;
2163}
2164
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002165void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002167 struct arg_dev_net adn = {
2168 .dev = dev,
2169 .net = net,
2170 };
2171
2172 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002173 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174}
2175
2176struct rt6_mtu_change_arg
2177{
2178 struct net_device *dev;
2179 unsigned mtu;
2180};
2181
2182static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2183{
2184 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2185 struct inet6_dev *idev;
2186
2187 /* In IPv6 pmtu discovery is not optional,
2188 so that RTAX_MTU lock cannot disable it.
2189 We still use this lock to block changes
2190 caused by addrconf/ndisc.
2191 */
2192
2193 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05002194 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 return 0;
2196
2197 /* For administrative MTU increase, there is no way to discover
2198 IPv6 PMTU increase, so PMTU increase should be updated here.
2199 Since RFC 1981 doesn't include administrative MTU increase
2200 update PMTU increase is a MUST. (i.e. jumbo frame)
2201 */
2202 /*
2203 If new MTU is less than route PMTU, this new MTU will be the
2204 lowest MTU in the path, update the route PMTU to reflect PMTU
2205 decreases; if new MTU is greater than route PMTU, and the
2206 old MTU is the lowest MTU in the path, update the route PMTU
2207 to reflect the increase. In this case if the other nodes' MTU
2208 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2209 PMTU discouvery.
2210 */
2211 if (rt->rt6i_dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002212 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2213 (dst_mtu(&rt->dst) >= arg->mtu ||
2214 (dst_mtu(&rt->dst) < arg->mtu &&
2215 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002216 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002217 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 return 0;
2219}
2220
2221void rt6_mtu_change(struct net_device *dev, unsigned mtu)
2222{
Thomas Grafc71099a2006-08-04 23:20:06 -07002223 struct rt6_mtu_change_arg arg = {
2224 .dev = dev,
2225 .mtu = mtu,
2226 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002228 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229}
2230
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002231static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002232 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002233 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002234 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002235 [RTA_PRIORITY] = { .type = NLA_U32 },
2236 [RTA_METRICS] = { .type = NLA_NESTED },
2237};
2238
2239static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2240 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241{
Thomas Graf86872cb2006-08-22 00:01:08 -07002242 struct rtmsg *rtm;
2243 struct nlattr *tb[RTA_MAX+1];
2244 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245
Thomas Graf86872cb2006-08-22 00:01:08 -07002246 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2247 if (err < 0)
2248 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
Thomas Graf86872cb2006-08-22 00:01:08 -07002250 err = -EINVAL;
2251 rtm = nlmsg_data(nlh);
2252 memset(cfg, 0, sizeof(*cfg));
2253
2254 cfg->fc_table = rtm->rtm_table;
2255 cfg->fc_dst_len = rtm->rtm_dst_len;
2256 cfg->fc_src_len = rtm->rtm_src_len;
2257 cfg->fc_flags = RTF_UP;
2258 cfg->fc_protocol = rtm->rtm_protocol;
2259
2260 if (rtm->rtm_type == RTN_UNREACHABLE)
2261 cfg->fc_flags |= RTF_REJECT;
2262
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002263 if (rtm->rtm_type == RTN_LOCAL)
2264 cfg->fc_flags |= RTF_LOCAL;
2265
Thomas Graf86872cb2006-08-22 00:01:08 -07002266 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2267 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002268 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002269
2270 if (tb[RTA_GATEWAY]) {
2271 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2272 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002274
2275 if (tb[RTA_DST]) {
2276 int plen = (rtm->rtm_dst_len + 7) >> 3;
2277
2278 if (nla_len(tb[RTA_DST]) < plen)
2279 goto errout;
2280
2281 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002283
2284 if (tb[RTA_SRC]) {
2285 int plen = (rtm->rtm_src_len + 7) >> 3;
2286
2287 if (nla_len(tb[RTA_SRC]) < plen)
2288 goto errout;
2289
2290 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002292
Daniel Walterc3968a82011-04-13 21:10:57 +00002293 if (tb[RTA_PREFSRC])
2294 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2295
Thomas Graf86872cb2006-08-22 00:01:08 -07002296 if (tb[RTA_OIF])
2297 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2298
2299 if (tb[RTA_PRIORITY])
2300 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2301
2302 if (tb[RTA_METRICS]) {
2303 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2304 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002306
2307 if (tb[RTA_TABLE])
2308 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2309
2310 err = 0;
2311errout:
2312 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313}
2314
Thomas Grafc127ea22007-03-22 11:58:32 -07002315static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316{
Thomas Graf86872cb2006-08-22 00:01:08 -07002317 struct fib6_config cfg;
2318 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
Thomas Graf86872cb2006-08-22 00:01:08 -07002320 err = rtm_to_fib6_config(skb, nlh, &cfg);
2321 if (err < 0)
2322 return err;
2323
2324 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325}
2326
Thomas Grafc127ea22007-03-22 11:58:32 -07002327static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328{
Thomas Graf86872cb2006-08-22 00:01:08 -07002329 struct fib6_config cfg;
2330 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331
Thomas Graf86872cb2006-08-22 00:01:08 -07002332 err = rtm_to_fib6_config(skb, nlh, &cfg);
2333 if (err < 0)
2334 return err;
2335
2336 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337}
2338
Thomas Graf339bf982006-11-10 14:10:15 -08002339static inline size_t rt6_nlmsg_size(void)
2340{
2341 return NLMSG_ALIGN(sizeof(struct rtmsg))
2342 + nla_total_size(16) /* RTA_SRC */
2343 + nla_total_size(16) /* RTA_DST */
2344 + nla_total_size(16) /* RTA_GATEWAY */
2345 + nla_total_size(16) /* RTA_PREFSRC */
2346 + nla_total_size(4) /* RTA_TABLE */
2347 + nla_total_size(4) /* RTA_IIF */
2348 + nla_total_size(4) /* RTA_OIF */
2349 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002350 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002351 + nla_total_size(sizeof(struct rta_cacheinfo));
2352}
2353
Brian Haley191cd582008-08-14 15:33:21 -07002354static int rt6_fill_node(struct net *net,
2355 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002356 struct in6_addr *dst, struct in6_addr *src,
2357 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002358 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359{
2360 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002361 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002362 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002363 u32 table;
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002364 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365
2366 if (prefix) { /* user wants prefix routes only */
2367 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2368 /* success since this is not a prefix route */
2369 return 1;
2370 }
2371 }
2372
Thomas Graf2d7202b2006-08-22 00:01:27 -07002373 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05002374 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08002375 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002376
2377 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 rtm->rtm_family = AF_INET6;
2379 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2380 rtm->rtm_src_len = rt->rt6i_src.plen;
2381 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002382 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002383 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002384 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002385 table = RT6_TABLE_UNSPEC;
2386 rtm->rtm_table = table;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002387 NLA_PUT_U32(skb, RTA_TABLE, table);
David S. Miller38308472011-12-03 18:02:47 -05002388 if (rt->rt6i_flags & RTF_REJECT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 rtm->rtm_type = RTN_UNREACHABLE;
David S. Miller38308472011-12-03 18:02:47 -05002390 else if (rt->rt6i_flags & RTF_LOCAL)
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002391 rtm->rtm_type = RTN_LOCAL;
David S. Miller38308472011-12-03 18:02:47 -05002392 else if (rt->rt6i_dev && (rt->rt6i_dev->flags & IFF_LOOPBACK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 rtm->rtm_type = RTN_LOCAL;
2394 else
2395 rtm->rtm_type = RTN_UNICAST;
2396 rtm->rtm_flags = 0;
2397 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2398 rtm->rtm_protocol = rt->rt6i_protocol;
David S. Miller38308472011-12-03 18:02:47 -05002399 if (rt->rt6i_flags & RTF_DYNAMIC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 rtm->rtm_protocol = RTPROT_REDIRECT;
2401 else if (rt->rt6i_flags & RTF_ADDRCONF)
2402 rtm->rtm_protocol = RTPROT_KERNEL;
David S. Miller38308472011-12-03 18:02:47 -05002403 else if (rt->rt6i_flags & RTF_DEFAULT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 rtm->rtm_protocol = RTPROT_RA;
2405
David S. Miller38308472011-12-03 18:02:47 -05002406 if (rt->rt6i_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 rtm->rtm_flags |= RTM_F_CLONED;
2408
2409 if (dst) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002410 NLA_PUT(skb, RTA_DST, 16, dst);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002411 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412 } else if (rtm->rtm_dst_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002413 NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414#ifdef CONFIG_IPV6_SUBTREES
2415 if (src) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002416 NLA_PUT(skb, RTA_SRC, 16, src);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002417 rtm->rtm_src_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418 } else if (rtm->rtm_src_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002419 NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002421 if (iif) {
2422#ifdef CONFIG_IPV6_MROUTE
2423 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002424 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002425 if (err <= 0) {
2426 if (!nowait) {
2427 if (err == 0)
2428 return 0;
2429 goto nla_put_failure;
2430 } else {
2431 if (err == -EMSGSIZE)
2432 goto nla_put_failure;
2433 }
2434 }
2435 } else
2436#endif
2437 NLA_PUT_U32(skb, RTA_IIF, iif);
2438 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439 struct in6_addr saddr_buf;
Daniel Walterc3968a82011-04-13 21:10:57 +00002440 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002441 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002443
Daniel Walterc3968a82011-04-13 21:10:57 +00002444 if (rt->rt6i_prefsrc.plen) {
2445 struct in6_addr saddr_buf;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002446 saddr_buf = rt->rt6i_prefsrc.addr;
Daniel Walterc3968a82011-04-13 21:10:57 +00002447 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
2448 }
2449
David S. Millerdefb3512010-12-08 21:16:57 -08002450 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002451 goto nla_put_failure;
2452
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002453 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +00002454 n = dst_get_neighbour_noref(&rt->dst);
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002455 if (n)
2456 NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
2457 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002458
Changli Gaod8d1f302010-06-10 23:31:35 -07002459 if (rt->dst.dev)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002460 NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
2461
2462 NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
Thomas Grafe3703b32006-11-27 09:27:07 -08002463
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002464 if (!(rt->rt6i_flags & RTF_EXPIRES))
2465 expires = 0;
2466 else if (rt->rt6i_expires - jiffies < INT_MAX)
2467 expires = rt->rt6i_expires - jiffies;
2468 else
2469 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002470
Changli Gaod8d1f302010-06-10 23:31:35 -07002471 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0,
2472 expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002473 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474
Thomas Graf2d7202b2006-08-22 00:01:27 -07002475 return nlmsg_end(skb, nlh);
2476
2477nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002478 nlmsg_cancel(skb, nlh);
2479 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480}
2481
Patrick McHardy1b43af52006-08-10 23:11:17 -07002482int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483{
2484 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2485 int prefix;
2486
Thomas Graf2d7202b2006-08-22 00:01:27 -07002487 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2488 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2490 } else
2491 prefix = 0;
2492
Brian Haley191cd582008-08-14 15:33:21 -07002493 return rt6_fill_node(arg->net,
2494 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002496 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497}
2498
Thomas Grafc127ea22007-03-22 11:58:32 -07002499static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002501 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002502 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002504 struct sk_buff *skb;
2505 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002506 struct flowi6 fl6;
Thomas Grafab364a62006-08-22 00:01:47 -07002507 int err, iif = 0;
2508
2509 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2510 if (err < 0)
2511 goto errout;
2512
2513 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002514 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002515
2516 if (tb[RTA_SRC]) {
2517 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2518 goto errout;
2519
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002520 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07002521 }
2522
2523 if (tb[RTA_DST]) {
2524 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2525 goto errout;
2526
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002527 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07002528 }
2529
2530 if (tb[RTA_IIF])
2531 iif = nla_get_u32(tb[RTA_IIF]);
2532
2533 if (tb[RTA_OIF])
David S. Miller4c9483b2011-03-12 16:22:43 -05002534 fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002535
2536 if (iif) {
2537 struct net_device *dev;
Daniel Lezcano55786892008-03-04 13:47:47 -08002538 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002539 if (!dev) {
2540 err = -ENODEV;
2541 goto errout;
2542 }
2543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544
2545 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05002546 if (!skb) {
Thomas Grafab364a62006-08-22 00:01:47 -07002547 err = -ENOBUFS;
2548 goto errout;
2549 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550
2551 /* Reserve room for dummy headers, this skb can pass
2552 through good chunk of routing engine.
2553 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002554 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2556
David S. Miller4c9483b2011-03-12 16:22:43 -05002557 rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6);
Changli Gaod8d1f302010-06-10 23:31:35 -07002558 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559
David S. Miller4c9483b2011-03-12 16:22:43 -05002560 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002562 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002564 kfree_skb(skb);
2565 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 }
2567
Daniel Lezcano55786892008-03-04 13:47:47 -08002568 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002569errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571}
2572
Thomas Graf86872cb2006-08-22 00:01:08 -07002573void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574{
2575 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002576 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002577 u32 seq;
2578 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002580 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05002581 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002582
Thomas Graf339bf982006-11-10 14:10:15 -08002583 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05002584 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07002585 goto errout;
2586
Brian Haley191cd582008-08-14 15:33:21 -07002587 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002588 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002589 if (err < 0) {
2590 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2591 WARN_ON(err == -EMSGSIZE);
2592 kfree_skb(skb);
2593 goto errout;
2594 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002595 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2596 info->nlh, gfp_any());
2597 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002598errout:
2599 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002600 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601}
2602
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002603static int ip6_route_dev_notify(struct notifier_block *this,
2604 unsigned long event, void *data)
2605{
2606 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002607 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002608
2609 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002610 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002611 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2612#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002613 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002614 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002615 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002616 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2617#endif
2618 }
2619
2620 return NOTIFY_OK;
2621}
2622
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623/*
2624 * /proc
2625 */
2626
2627#ifdef CONFIG_PROC_FS
2628
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629struct rt6_proc_arg
2630{
2631 char *buffer;
2632 int offset;
2633 int length;
2634 int skip;
2635 int len;
2636};
2637
2638static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2639{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002640 struct seq_file *m = p_arg;
David S. Miller69cce1d2011-07-17 23:09:49 -07002641 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002643 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644
2645#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002646 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002648 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649#endif
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002650 rcu_read_lock();
David Miller27217452011-12-02 16:52:08 +00002651 n = dst_get_neighbour_noref(&rt->dst);
David S. Miller69cce1d2011-07-17 23:09:49 -07002652 if (n) {
2653 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002655 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 }
Eric Dumazetf2c31e32011-07-29 19:00:53 +00002657 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002658 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002659 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2660 rt->dst.__use, rt->rt6i_flags,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002661 rt->rt6i_dev ? rt->rt6i_dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 return 0;
2663}
2664
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002665static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002667 struct net *net = (struct net *)m->private;
2668 fib6_clean_all(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002669 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670}
2671
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002672static int ipv6_route_open(struct inode *inode, struct file *file)
2673{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002674 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002675}
2676
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002677static const struct file_operations ipv6_route_proc_fops = {
2678 .owner = THIS_MODULE,
2679 .open = ipv6_route_open,
2680 .read = seq_read,
2681 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002682 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002683};
2684
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2686{
Daniel Lezcano69ddb8052008-03-04 13:46:23 -08002687 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb8052008-03-04 13:46:23 -08002689 net->ipv6.rt6_stats->fib_nodes,
2690 net->ipv6.rt6_stats->fib_route_nodes,
2691 net->ipv6.rt6_stats->fib_rt_alloc,
2692 net->ipv6.rt6_stats->fib_rt_entries,
2693 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002694 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb8052008-03-04 13:46:23 -08002695 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696
2697 return 0;
2698}
2699
2700static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2701{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002702 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb8052008-03-04 13:46:23 -08002703}
2704
Arjan van de Ven9a321442007-02-12 00:55:35 -08002705static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 .owner = THIS_MODULE,
2707 .open = rt6_stats_seq_open,
2708 .read = seq_read,
2709 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002710 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711};
2712#endif /* CONFIG_PROC_FS */
2713
2714#ifdef CONFIG_SYSCTL
2715
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002717int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718 void __user *buffer, size_t *lenp, loff_t *ppos)
2719{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002720 struct net *net;
2721 int delay;
2722 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002724
2725 net = (struct net *)ctl->extra1;
2726 delay = net->ipv6.sysctl.flush_delay;
2727 proc_dointvec(ctl, write, buffer, lenp, ppos);
2728 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2729 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730}
2731
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002732ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002733 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002735 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002737 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002738 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739 },
2740 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002742 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743 .maxlen = sizeof(int),
2744 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002745 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746 },
2747 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002749 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 .maxlen = sizeof(int),
2751 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002752 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 },
2754 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002756 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 .maxlen = sizeof(int),
2758 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002759 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760 },
2761 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002763 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 .maxlen = sizeof(int),
2765 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002766 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 },
2768 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002770 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 .maxlen = sizeof(int),
2772 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002773 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 },
2775 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002777 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 .maxlen = sizeof(int),
2779 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002780 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 },
2782 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002784 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 .maxlen = sizeof(int),
2786 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002787 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 },
2789 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002791 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 .maxlen = sizeof(int),
2793 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002794 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 },
2796 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002798 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 .maxlen = sizeof(int),
2800 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002801 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002803 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804};
2805
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002806struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002807{
2808 struct ctl_table *table;
2809
2810 table = kmemdup(ipv6_route_table_template,
2811 sizeof(ipv6_route_table_template),
2812 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002813
2814 if (table) {
2815 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002816 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002817 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002818 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2819 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2820 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2821 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2822 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2823 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2824 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002825 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002826 }
2827
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002828 return table;
2829}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830#endif
2831
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002832static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002833{
Pavel Emelyanov633d4242008-04-21 14:25:23 -07002834 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002835
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002836 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2837 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002838
Eric Dumazetfc66f952010-10-08 06:37:34 +00002839 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2840 goto out_ip6_dst_ops;
2841
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002842 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2843 sizeof(*net->ipv6.ip6_null_entry),
2844 GFP_KERNEL);
2845 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002846 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002847 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002848 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002849 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002850 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2851 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002852
2853#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2854 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2855 sizeof(*net->ipv6.ip6_prohibit_entry),
2856 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002857 if (!net->ipv6.ip6_prohibit_entry)
2858 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002859 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002860 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002861 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002862 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2863 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002864
2865 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2866 sizeof(*net->ipv6.ip6_blk_hole_entry),
2867 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002868 if (!net->ipv6.ip6_blk_hole_entry)
2869 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002870 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002871 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002872 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002873 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2874 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002875#endif
2876
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07002877 net->ipv6.sysctl.flush_delay = 0;
2878 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2879 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2880 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2881 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2882 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2883 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2884 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2885
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002886#ifdef CONFIG_PROC_FS
2887 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2888 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2889#endif
Benjamin Thery6891a342008-03-04 13:49:47 -08002890 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2891
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002892 ret = 0;
2893out:
2894 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002895
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002896#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2897out_ip6_prohibit_entry:
2898 kfree(net->ipv6.ip6_prohibit_entry);
2899out_ip6_null_entry:
2900 kfree(net->ipv6.ip6_null_entry);
2901#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002902out_ip6_dst_entries:
2903 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002904out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002905 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002906}
2907
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002908static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002909{
2910#ifdef CONFIG_PROC_FS
2911 proc_net_remove(net, "ipv6_route");
2912 proc_net_remove(net, "rt6_stats");
2913#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002914 kfree(net->ipv6.ip6_null_entry);
2915#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2916 kfree(net->ipv6.ip6_prohibit_entry);
2917 kfree(net->ipv6.ip6_blk_hole_entry);
2918#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002919 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002920}
2921
2922static struct pernet_operations ip6_route_net_ops = {
2923 .init = ip6_route_net_init,
2924 .exit = ip6_route_net_exit,
2925};
2926
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002927static struct notifier_block ip6_route_dev_notifier = {
2928 .notifier_call = ip6_route_dev_notify,
2929 .priority = 0,
2930};
2931
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002932int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002934 int ret;
2935
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002936 ret = -ENOMEM;
2937 ip6_dst_ops_template.kmem_cachep =
2938 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
2939 SLAB_HWCACHE_ALIGN, NULL);
2940 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08002941 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07002942
Eric Dumazetfc66f952010-10-08 06:37:34 +00002943 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002944 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002945 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002946
Eric Dumazetfc66f952010-10-08 06:37:34 +00002947 ret = register_pernet_subsys(&ip6_route_net_ops);
2948 if (ret)
2949 goto out_dst_entries;
2950
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07002951 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
2952
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002953 /* Registering of the loopback is done before this portion of code,
2954 * the loopback reference in rt6_info will not be taken, do it
2955 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07002956 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002957 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2958 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002959 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002960 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002961 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002962 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2963 #endif
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002964 ret = fib6_init();
2965 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002966 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002967
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002968 ret = xfrm6_init();
2969 if (ret)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002970 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08002971
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002972 ret = fib6_rules_init();
2973 if (ret)
2974 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08002975
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002976 ret = -ENOBUFS;
Greg Rosec7ac8672011-06-10 01:27:09 +00002977 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) ||
2978 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) ||
2979 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL))
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002980 goto fib6_rules_init;
2981
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002982 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002983 if (ret)
2984 goto fib6_rules_init;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002985
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002986out:
2987 return ret;
2988
2989fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002990 fib6_rules_cleanup();
2991xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002992 xfrm6_fini();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002993out_fib6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002994 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002995out_register_subsys:
2996 unregister_pernet_subsys(&ip6_route_net_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002997out_dst_entries:
2998 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002999out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003000 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08003001 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002}
3003
3004void ip6_route_cleanup(void)
3005{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003006 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Graf101367c2006-08-04 03:39:02 -07003007 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003009 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08003010 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00003011 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08003012 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003013}