blob: e70e902a5d064d09489d6b26d7ad190fd1f9472a [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>
29#include <linux/types.h>
30#include <linux/times.h>
31#include <linux/socket.h>
32#include <linux/sockios.h>
33#include <linux/net.h>
34#include <linux/route.h>
35#include <linux/netdevice.h>
36#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090037#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/proc_fs.h>
41#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080042#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090043#include <linux/slab.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020044#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#include <net/snmp.h>
46#include <net/ipv6.h>
47#include <net/ip6_fib.h>
48#include <net/ip6_route.h>
49#include <net/ndisc.h>
50#include <net/addrconf.h>
51#include <net/tcp.h>
52#include <linux/rtnetlink.h>
53#include <net/dst.h>
54#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070055#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070056#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58#include <asm/uaccess.h>
59
60#ifdef CONFIG_SYSCTL
61#include <linux/sysctl.h>
62#endif
63
64/* Set to 3 to get tracing. */
65#define RT6_DEBUG 2
66
67#if RT6_DEBUG >= 3
68#define RDBG(x) printk x
69#define RT6_TRACE(x...) printk(KERN_DEBUG x)
70#else
71#define RDBG(x)
72#define RT6_TRACE(x...) do { ; } while (0)
73#endif
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
76static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080077static unsigned int ip6_default_advmss(const struct dst_entry *dst);
David S. Millerd33e4552010-12-14 13:01:14 -080078static unsigned int ip6_default_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070079static struct dst_entry *ip6_negative_advice(struct dst_entry *);
80static void ip6_dst_destroy(struct dst_entry *);
81static void ip6_dst_ifdown(struct dst_entry *,
82 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080083static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85static int ip6_pkt_discard(struct sk_buff *skb);
86static int ip6_pkt_discard_out(struct sk_buff *skb);
87static void ip6_link_failure(struct sk_buff *skb);
88static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
89
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080090#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080091static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000092 const struct in6_addr *prefix, int prefixlen,
93 const struct in6_addr *gwaddr, int ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080094 unsigned pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080095static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +000096 const struct in6_addr *prefix, int prefixlen,
97 const struct in6_addr *gwaddr, int ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080098#endif
99
David S. Miller06582542011-01-27 14:58:42 -0800100static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
101{
102 struct rt6_info *rt = (struct rt6_info *) dst;
103 struct inet_peer *peer;
104 u32 *p = NULL;
105
106 if (!rt->rt6i_peer)
107 rt6_bind_peer(rt, 1);
108
109 peer = rt->rt6i_peer;
110 if (peer) {
111 u32 *old_p = __DST_METRICS_PTR(old);
112 unsigned long prev, new;
113
114 p = peer->metrics;
115 if (inet_metrics_new(peer))
116 memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
117
118 new = (unsigned long) p;
119 prev = cmpxchg(&dst->_metrics, old, new);
120
121 if (prev != old) {
122 p = __DST_METRICS_PTR(prev);
123 if (prev & DST_METRICS_READ_ONLY)
124 p = NULL;
125 }
126 }
127 return p;
128}
129
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800130static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800132 .protocol = cpu_to_be16(ETH_P_IPV6),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 .gc = ip6_dst_gc,
134 .gc_thresh = 1024,
135 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800136 .default_advmss = ip6_default_advmss,
David S. Millerd33e4552010-12-14 13:01:14 -0800137 .default_mtu = ip6_default_mtu,
David S. Miller06582542011-01-27 14:58:42 -0800138 .cow_metrics = ipv6_cow_metrics,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 .destroy = ip6_dst_destroy,
140 .ifdown = ip6_dst_ifdown,
141 .negative_advice = ip6_negative_advice,
142 .link_failure = ip6_link_failure,
143 .update_pmtu = ip6_rt_update_pmtu,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700144 .local_out = __ip6_local_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145};
146
Roland Dreierec831ea2011-01-31 13:16:00 -0800147static unsigned int ip6_blackhole_default_mtu(const struct dst_entry *dst)
148{
149 return 0;
150}
151
David S. Miller14e50e52007-05-24 18:17:54 -0700152static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
153{
154}
155
Held Bernhard0972ddb2011-04-24 22:07:32 +0000156static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
157 unsigned long old)
158{
159 return NULL;
160}
161
David S. Miller14e50e52007-05-24 18:17:54 -0700162static struct dst_ops ip6_dst_blackhole_ops = {
163 .family = AF_INET6,
Harvey Harrison09640e62009-02-01 00:45:17 -0800164 .protocol = cpu_to_be16(ETH_P_IPV6),
David S. Miller14e50e52007-05-24 18:17:54 -0700165 .destroy = ip6_dst_destroy,
166 .check = ip6_dst_check,
Roland Dreierec831ea2011-01-31 13:16:00 -0800167 .default_mtu = ip6_blackhole_default_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800168 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700169 .update_pmtu = ip6_rt_blackhole_update_pmtu,
Held Bernhard0972ddb2011-04-24 22:07:32 +0000170 .cow_metrics = ip6_rt_blackhole_cow_metrics,
David S. Miller14e50e52007-05-24 18:17:54 -0700171};
172
David S. Miller62fa8a82011-01-26 20:51:05 -0800173static const u32 ip6_template_metrics[RTAX_MAX] = {
174 [RTAX_HOPLIMIT - 1] = 255,
175};
176
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800177static struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700178 .dst = {
179 .__refcnt = ATOMIC_INIT(1),
180 .__use = 1,
181 .obsolete = -1,
182 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700183 .input = ip6_pkt_discard,
184 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 },
186 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700187 .rt6i_protocol = RTPROT_KERNEL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 .rt6i_metric = ~(u32) 0,
189 .rt6i_ref = ATOMIC_INIT(1),
190};
191
Thomas Graf101367c2006-08-04 03:39:02 -0700192#ifdef CONFIG_IPV6_MULTIPLE_TABLES
193
David S. Miller6723ab52006-10-18 21:20:57 -0700194static int ip6_pkt_prohibit(struct sk_buff *skb);
195static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700196
Adrian Bunk280a34c2008-04-21 02:29:32 -0700197static struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700198 .dst = {
199 .__refcnt = ATOMIC_INIT(1),
200 .__use = 1,
201 .obsolete = -1,
202 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700203 .input = ip6_pkt_prohibit,
204 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700205 },
206 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700207 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700208 .rt6i_metric = ~(u32) 0,
209 .rt6i_ref = ATOMIC_INIT(1),
210};
211
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800212static struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700213 .dst = {
214 .__refcnt = ATOMIC_INIT(1),
215 .__use = 1,
216 .obsolete = -1,
217 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700218 .input = dst_discard,
219 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700220 },
221 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Jean-Mickael Guerin4f724272009-05-20 17:38:59 -0700222 .rt6i_protocol = RTPROT_KERNEL,
Thomas Graf101367c2006-08-04 03:39:02 -0700223 .rt6i_metric = ~(u32) 0,
224 .rt6i_ref = ATOMIC_INIT(1),
225};
226
227#endif
228
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229/* allocate dst with ip6_dst_ops */
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700230static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops,
David S. Miller957c6652011-06-24 15:25:00 -0700231 struct net_device *dev,
232 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
David S. Miller957c6652011-06-24 15:25:00 -0700234 struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, flags);
David S. Millercf911662011-04-28 14:31:47 -0700235
236 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
237
238 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239}
240
241static void ip6_dst_destroy(struct dst_entry *dst)
242{
243 struct rt6_info *rt = (struct rt6_info *)dst;
244 struct inet6_dev *idev = rt->rt6i_idev;
David S. Millerb3419362010-11-30 12:27:11 -0800245 struct inet_peer *peer = rt->rt6i_peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 if (idev != NULL) {
248 rt->rt6i_idev = NULL;
249 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900250 }
David S. Millerb3419362010-11-30 12:27:11 -0800251 if (peer) {
David S. Millerb3419362010-11-30 12:27:11 -0800252 rt->rt6i_peer = NULL;
253 inet_putpeer(peer);
254 }
255}
256
David S. Miller6431cbc2011-02-07 20:38:06 -0800257static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
258
259static u32 rt6_peer_genid(void)
260{
261 return atomic_read(&__rt6_peer_genid);
262}
263
David S. Millerb3419362010-11-30 12:27:11 -0800264void rt6_bind_peer(struct rt6_info *rt, int create)
265{
266 struct inet_peer *peer;
267
David S. Millerb3419362010-11-30 12:27:11 -0800268 peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create);
269 if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL)
270 inet_putpeer(peer);
David S. Miller6431cbc2011-02-07 20:38:06 -0800271 else
272 rt->rt6i_peer_genid = rt6_peer_genid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273}
274
275static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
276 int how)
277{
278 struct rt6_info *rt = (struct rt6_info *)dst;
279 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800280 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900281 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800283 if (dev != loopback_dev && idev != NULL && idev->dev == dev) {
284 struct inet6_dev *loopback_idev =
285 in6_dev_get(loopback_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 if (loopback_idev != NULL) {
287 rt->rt6i_idev = loopback_idev;
288 in6_dev_put(idev);
289 }
290 }
291}
292
293static __inline__ int rt6_check_expired(const struct rt6_info *rt)
294{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000295 return (rt->rt6i_flags & RTF_EXPIRES) &&
296 time_after(jiffies, rt->rt6i_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297}
298
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000299static inline int rt6_need_strict(const struct in6_addr *daddr)
Thomas Grafc71099a2006-08-04 23:20:06 -0700300{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000301 return ipv6_addr_type(daddr) &
302 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
Thomas Grafc71099a2006-08-04 23:20:06 -0700303}
304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700306 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 */
308
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800309static inline struct rt6_info *rt6_device_match(struct net *net,
310 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000311 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700313 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314{
315 struct rt6_info *local = NULL;
316 struct rt6_info *sprt;
317
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900318 if (!oif && ipv6_addr_any(saddr))
319 goto out;
320
Changli Gaod8d1f302010-06-10 23:31:35 -0700321 for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) {
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900322 struct net_device *dev = sprt->rt6i_dev;
323
324 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 if (dev->ifindex == oif)
326 return sprt;
327 if (dev->flags & IFF_LOOPBACK) {
328 if (sprt->rt6i_idev == NULL ||
329 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700330 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900332 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 local->rt6i_idev->dev->ifindex == oif))
334 continue;
335 }
336 local = sprt;
337 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900338 } else {
339 if (ipv6_chk_addr(net, saddr, dev,
340 flags & RT6_LOOKUP_F_IFACE))
341 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900343 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900345 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 if (local)
347 return local;
348
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700349 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800350 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900352out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 return rt;
354}
355
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800356#ifdef CONFIG_IPV6_ROUTER_PREF
357static void rt6_probe(struct rt6_info *rt)
358{
Eric Dumazet8a533662012-02-09 16:13:19 -0500359 struct neighbour *neigh;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800360 /*
361 * Okay, this does not seem to be appropriate
362 * for now, however, we need to check if it
363 * is really so; aka Router Reachability Probing.
364 *
365 * Router Reachability Probe MUST be rate-limited
366 * to no more than one per minute.
367 */
Eric Dumazet8a533662012-02-09 16:13:19 -0500368 rcu_read_lock();
369 neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800370 if (!neigh || (neigh->nud_state & NUD_VALID))
Eric Dumazet8a533662012-02-09 16:13:19 -0500371 goto out;
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800372 read_lock_bh(&neigh->lock);
373 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800374 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800375 struct in6_addr mcaddr;
376 struct in6_addr *target;
377
378 neigh->updated = jiffies;
379 read_unlock_bh(&neigh->lock);
380
381 target = (struct in6_addr *)&neigh->primary_key;
382 addrconf_addr_solict_mult(target, &mcaddr);
383 ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
Eric Dumazet8a533662012-02-09 16:13:19 -0500384 } else {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800385 read_unlock_bh(&neigh->lock);
Eric Dumazet8a533662012-02-09 16:13:19 -0500386 }
387out:
388 rcu_read_unlock();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800389}
390#else
391static inline void rt6_probe(struct rt6_info *rt)
392{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800393}
394#endif
395
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800397 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700399static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800401 struct net_device *dev = rt->rt6i_dev;
David S. Miller161980f2007-04-06 11:42:27 -0700402 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800403 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700404 if ((dev->flags & IFF_LOOPBACK) &&
405 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
406 return 1;
407 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408}
409
Dave Jonesb6f99a22007-03-22 12:27:49 -0700410static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
Eric Dumazet8a533662012-02-09 16:13:19 -0500412 struct neighbour *neigh;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800413 int m;
Eric Dumazet8a533662012-02-09 16:13:19 -0500414
415 rcu_read_lock();
416 neigh = dst_get_neighbour(&rt->dst);
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700417 if (rt->rt6i_flags & RTF_NONEXTHOP ||
418 !(rt->rt6i_flags & RTF_GATEWAY))
419 m = 1;
420 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800421 read_lock_bh(&neigh->lock);
422 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700423 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800424#ifdef CONFIG_IPV6_ROUTER_PREF
425 else if (neigh->nud_state & NUD_FAILED)
426 m = 0;
427#endif
428 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800429 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800430 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800431 } else
432 m = 0;
Eric Dumazet8a533662012-02-09 16:13:19 -0500433 rcu_read_unlock();
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800434 return m;
435}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800437static int rt6_score_route(struct rt6_info *rt, int oif,
438 int strict)
439{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700440 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900441
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700442 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700443 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800444 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800445#ifdef CONFIG_IPV6_ROUTER_PREF
446 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
447#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700448 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800449 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800450 return -1;
451 return m;
452}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
David S. Millerf11e6652007-03-24 20:36:25 -0700454static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
455 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800456{
David S. Millerf11e6652007-03-24 20:36:25 -0700457 int m;
458
459 if (rt6_check_expired(rt))
460 goto out;
461
462 m = rt6_score_route(rt, oif, strict);
463 if (m < 0)
464 goto out;
465
466 if (m > *mpri) {
467 if (strict & RT6_LOOKUP_F_REACHABLE)
468 rt6_probe(match);
469 *mpri = m;
470 match = rt;
471 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
472 rt6_probe(rt);
473 }
474
475out:
476 return match;
477}
478
479static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
480 struct rt6_info *rr_head,
481 u32 metric, int oif, int strict)
482{
483 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800484 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
David S. Millerf11e6652007-03-24 20:36:25 -0700486 match = NULL;
487 for (rt = rr_head; rt && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700488 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700489 match = find_match(rt, oif, strict, &mpri, match);
490 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
Changli Gaod8d1f302010-06-10 23:31:35 -0700491 rt = rt->dst.rt6_next)
David S. Millerf11e6652007-03-24 20:36:25 -0700492 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800493
David S. Millerf11e6652007-03-24 20:36:25 -0700494 return match;
495}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800496
David S. Millerf11e6652007-03-24 20:36:25 -0700497static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
498{
499 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800500 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
David S. Millerf11e6652007-03-24 20:36:25 -0700502 RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800503 __func__, fn->leaf, oif);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
David S. Millerf11e6652007-03-24 20:36:25 -0700505 rt0 = fn->rr_ptr;
506 if (!rt0)
507 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
David S. Millerf11e6652007-03-24 20:36:25 -0700509 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800511 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700512 (strict & RT6_LOOKUP_F_REACHABLE)) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700513 struct rt6_info *next = rt0->dst.rt6_next;
David S. Millerf11e6652007-03-24 20:36:25 -0700514
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800515 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700516 if (!next || next->rt6i_metric != rt0->rt6i_metric)
517 next = fn->leaf;
518
519 if (next != rt0)
520 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 }
522
David S. Millerf11e6652007-03-24 20:36:25 -0700523 RT6_TRACE("%s() => %p\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800524 __func__, match);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900526 net = dev_net(rt0->rt6i_dev);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000527 return match ? match : net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528}
529
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800530#ifdef CONFIG_IPV6_ROUTE_INFO
531int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000532 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800533{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900534 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800535 struct route_info *rinfo = (struct route_info *) opt;
536 struct in6_addr prefix_buf, *prefix;
537 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900538 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800539 struct rt6_info *rt;
540
541 if (len < sizeof(struct route_info)) {
542 return -EINVAL;
543 }
544
545 /* Sanity check for prefix_len and length */
546 if (rinfo->length > 3) {
547 return -EINVAL;
548 } else if (rinfo->prefix_len > 128) {
549 return -EINVAL;
550 } else if (rinfo->prefix_len > 64) {
551 if (rinfo->length < 2) {
552 return -EINVAL;
553 }
554 } else if (rinfo->prefix_len > 0) {
555 if (rinfo->length < 1) {
556 return -EINVAL;
557 }
558 }
559
560 pref = rinfo->route_pref;
561 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000562 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800563
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900564 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800565
566 if (rinfo->length == 3)
567 prefix = (struct in6_addr *)rinfo->prefix;
568 else {
569 /* this function is safe */
570 ipv6_addr_prefix(&prefix_buf,
571 (struct in6_addr *)rinfo->prefix,
572 rinfo->prefix_len);
573 prefix = &prefix_buf;
574 }
575
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800576 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
577 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800578
579 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700580 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800581 rt = NULL;
582 }
583
584 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800585 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800586 pref);
587 else if (rt)
588 rt->rt6i_flags = RTF_ROUTEINFO |
589 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
590
591 if (rt) {
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900592 if (!addrconf_finite_timeout(lifetime)) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800593 rt->rt6i_flags &= ~RTF_EXPIRES;
594 } else {
595 rt->rt6i_expires = jiffies + HZ * lifetime;
596 rt->rt6i_flags |= RTF_EXPIRES;
597 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700598 dst_release(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800599 }
600 return 0;
601}
602#endif
603
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800604#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700605do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800606 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700607 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700608 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700609 if (fn->fn_flags & RTN_TL_ROOT) \
610 goto out; \
611 pn = fn->parent; \
612 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800613 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700614 else \
615 fn = pn; \
616 if (fn->fn_flags & RTN_RTINFO) \
617 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700618 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700619 } \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700620} while(0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700621
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800622static struct rt6_info *ip6_pol_route_lookup(struct net *net,
623 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500624 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
626 struct fib6_node *fn;
627 struct rt6_info *rt;
628
Thomas Grafc71099a2006-08-04 23:20:06 -0700629 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -0500630 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700631restart:
632 rt = fn->leaf;
David S. Miller4c9483b2011-03-12 16:22:43 -0500633 rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
634 BACKTRACK(net, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -0700635out:
Changli Gaod8d1f302010-06-10 23:31:35 -0700636 dst_use(&rt->dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700637 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700638 return rt;
639
640}
641
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900642struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
643 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700644{
David S. Miller4c9483b2011-03-12 16:22:43 -0500645 struct flowi6 fl6 = {
646 .flowi6_oif = oif,
647 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700648 };
649 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700650 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700651
Thomas Grafadaa70b2006-10-13 15:01:03 -0700652 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -0500653 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -0700654 flags |= RT6_LOOKUP_F_HAS_SADDR;
655 }
656
David S. Miller4c9483b2011-03-12 16:22:43 -0500657 dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700658 if (dst->error == 0)
659 return (struct rt6_info *) dst;
660
661 dst_release(dst);
662
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 return NULL;
664}
665
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900666EXPORT_SYMBOL(rt6_lookup);
667
Thomas Grafc71099a2006-08-04 23:20:06 -0700668/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 It takes new route entry, the addition fails by any reason the
670 route is freed. In any case, if caller does not hold it, it may
671 be destroyed.
672 */
673
Thomas Graf86872cb2006-08-22 00:01:08 -0700674static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
676 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700677 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Thomas Grafc71099a2006-08-04 23:20:06 -0700679 table = rt->rt6i_table;
680 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700681 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700682 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
684 return err;
685}
686
Thomas Graf40e22e82006-08-22 00:00:45 -0700687int ip6_ins_rt(struct rt6_info *rt)
688{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800689 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900690 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800691 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800692 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700693}
694
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000695static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, const struct in6_addr *daddr,
696 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 struct rt6_info *rt;
699
700 /*
701 * Clone the route.
702 */
703
704 rt = ip6_rt_copy(ort);
705
706 if (rt) {
David S. Miller14deae42009-01-04 16:04:39 -0800707 struct neighbour *neigh;
708 int attempts = !in_softirq();
709
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900710 if (!(rt->rt6i_flags&RTF_GATEWAY)) {
711 if (rt->rt6i_dst.plen != 128 &&
712 ipv6_addr_equal(&rt->rt6i_dst.addr, daddr))
713 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 ipv6_addr_copy(&rt->rt6i_gateway, daddr);
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900715 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900717 ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 rt->rt6i_dst.plen = 128;
719 rt->rt6i_flags |= RTF_CACHE;
Changli Gaod8d1f302010-06-10 23:31:35 -0700720 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
722#ifdef CONFIG_IPV6_SUBTREES
723 if (rt->rt6i_src.plen && saddr) {
724 ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
725 rt->rt6i_src.plen = 128;
726 }
727#endif
728
David S. Miller14deae42009-01-04 16:04:39 -0800729 retry:
730 neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
731 if (IS_ERR(neigh)) {
732 struct net *net = dev_net(rt->rt6i_dev);
733 int saved_rt_min_interval =
734 net->ipv6.sysctl.ip6_rt_gc_min_interval;
735 int saved_rt_elasticity =
736 net->ipv6.sysctl.ip6_rt_gc_elasticity;
737
738 if (attempts-- > 0) {
739 net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
740 net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
741
Alexey Dobriyan86393e52009-08-29 01:34:49 +0000742 ip6_dst_gc(&net->ipv6.ip6_dst_ops);
David S. Miller14deae42009-01-04 16:04:39 -0800743
744 net->ipv6.sysctl.ip6_rt_gc_elasticity =
745 saved_rt_elasticity;
746 net->ipv6.sysctl.ip6_rt_gc_min_interval =
747 saved_rt_min_interval;
748 goto retry;
749 }
750
751 if (net_ratelimit())
752 printk(KERN_WARNING
Ulrich Weber7e1b33e2010-09-27 15:02:18 -0700753 "ipv6: Neighbour table overflow.\n");
Changli Gaod8d1f302010-06-10 23:31:35 -0700754 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -0800755 return NULL;
756 }
Eric Dumazet8a533662012-02-09 16:13:19 -0500757 dst_set_neighbour(&rt->dst, neigh);
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800758 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800760 return rt;
761}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000763static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, const struct in6_addr *daddr)
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800764{
765 struct rt6_info *rt = ip6_rt_copy(ort);
766 if (rt) {
767 ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
768 rt->rt6i_dst.plen = 128;
769 rt->rt6i_flags |= RTF_CACHE;
Changli Gaod8d1f302010-06-10 23:31:35 -0700770 rt->dst.flags |= DST_HOST;
Eric Dumazet8a533662012-02-09 16:13:19 -0500771 dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst)));
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800772 }
773 return rt;
774}
775
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800776static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
David S. Miller4c9483b2011-03-12 16:22:43 -0500777 struct flowi6 *fl6, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
779 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800780 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700781 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800783 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700784 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700786 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
788relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700789 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800791restart_2:
David S. Miller4c9483b2011-03-12 16:22:43 -0500792 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
794restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700795 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800796
David S. Miller4c9483b2011-03-12 16:22:43 -0500797 BACKTRACK(net, &fl6->saddr);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800798 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800799 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef042006-03-20 17:01:24 -0800800 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Changli Gaod8d1f302010-06-10 23:31:35 -0700802 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700803 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800804
Eric Dumazet8a533662012-02-09 16:13:19 -0500805 if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
David S. Miller4c9483b2011-03-12 16:22:43 -0500806 nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800807 else if (!(rt->dst.flags & DST_HOST))
David S. Miller4c9483b2011-03-12 16:22:43 -0500808 nrt = rt6_alloc_clone(rt, &fl6->daddr);
David S. Miller7343ff32011-03-09 19:55:25 -0800809 else
810 goto out2;
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800811
Changli Gaod8d1f302010-06-10 23:31:35 -0700812 dst_release(&rt->dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800813 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800814
Changli Gaod8d1f302010-06-10 23:31:35 -0700815 dst_hold(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800816 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700817 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800818 if (!err)
819 goto out2;
820 }
821
822 if (--attempts <= 0)
823 goto out2;
824
825 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700826 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800827 * released someone could insert this route. Relookup.
828 */
Changli Gaod8d1f302010-06-10 23:31:35 -0700829 dst_release(&rt->dst);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800830 goto relookup;
831
832out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800833 if (reachable) {
834 reachable = 0;
835 goto restart_2;
836 }
Changli Gaod8d1f302010-06-10 23:31:35 -0700837 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700838 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839out2:
Changli Gaod8d1f302010-06-10 23:31:35 -0700840 rt->dst.lastuse = jiffies;
841 rt->dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700842
843 return rt;
844}
845
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800846static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500847 struct flowi6 *fl6, int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700848{
David S. Miller4c9483b2011-03-12 16:22:43 -0500849 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700850}
851
Thomas Grafc71099a2006-08-04 23:20:06 -0700852void ip6_route_input(struct sk_buff *skb)
853{
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000854 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900855 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700856 int flags = RT6_LOOKUP_F_HAS_SADDR;
David S. Miller4c9483b2011-03-12 16:22:43 -0500857 struct flowi6 fl6 = {
858 .flowi6_iif = skb->dev->ifindex,
859 .daddr = iph->daddr,
860 .saddr = iph->saddr,
861 .flowlabel = (* (__be32 *) iph)&IPV6_FLOWINFO_MASK,
862 .flowi6_mark = skb->mark,
863 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700864 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700865
Thomas Goff1d6e55f2009-01-27 22:39:59 -0800866 if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG)
Thomas Grafadaa70b2006-10-13 15:01:03 -0700867 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700868
David S. Miller4c9483b2011-03-12 16:22:43 -0500869 skb_dst_set(skb, fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input));
Thomas Grafc71099a2006-08-04 23:20:06 -0700870}
871
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800872static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -0500873 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -0700874{
David S. Miller4c9483b2011-03-12 16:22:43 -0500875 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700876}
877
Florian Westphal9c7a4f92011-03-22 19:17:36 -0700878struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk,
David S. Miller4c9483b2011-03-12 16:22:43 -0500879 struct flowi6 *fl6)
Thomas Grafc71099a2006-08-04 23:20:06 -0700880{
881 int flags = 0;
882
David S. Miller4c9483b2011-03-12 16:22:43 -0500883 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700884 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700885
David S. Miller4c9483b2011-03-12 16:22:43 -0500886 if (!ipv6_addr_any(&fl6->saddr))
Thomas Grafadaa70b2006-10-13 15:01:03 -0700887 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +0000888 else if (sk)
889 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700890
David S. Miller4c9483b2011-03-12 16:22:43 -0500891 return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892}
893
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900894EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
David S. Miller2774c132011-03-01 14:59:04 -0800896struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -0700897{
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700898 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
David S. Miller14e50e52007-05-24 18:17:54 -0700899 struct dst_entry *new = NULL;
900
David S. Miller5c1e6aa2011-04-28 14:13:38 -0700901 rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);
David S. Miller14e50e52007-05-24 18:17:54 -0700902 if (rt) {
David S. Millercf911662011-04-28 14:31:47 -0700903 memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry));
904
Changli Gaod8d1f302010-06-10 23:31:35 -0700905 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -0700906
David S. Miller14e50e52007-05-24 18:17:54 -0700907 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800908 new->input = dst_discard;
909 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700910
David S. Millerdefb3512010-12-08 21:16:57 -0800911 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -0700912 rt->rt6i_idev = ort->rt6i_idev;
913 if (rt->rt6i_idev)
914 in6_dev_hold(rt->rt6i_idev);
915 rt->rt6i_expires = 0;
916
917 ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
918 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
919 rt->rt6i_metric = 0;
920
921 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
922#ifdef CONFIG_IPV6_SUBTREES
923 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
924#endif
925
926 dst_free(new);
927 }
928
David S. Miller69ead7a2011-03-01 14:45:33 -0800929 dst_release(dst_orig);
930 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -0700931}
David S. Miller14e50e52007-05-24 18:17:54 -0700932
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933/*
934 * Destination cache support functions
935 */
936
937static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
938{
939 struct rt6_info *rt;
940
941 rt = (struct rt6_info *) dst;
942
David S. Miller6431cbc2011-02-07 20:38:06 -0800943 if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
944 if (rt->rt6i_peer_genid != rt6_peer_genid()) {
945 if (!rt->rt6i_peer)
946 rt6_bind_peer(rt, 0);
947 rt->rt6i_peer_genid = rt6_peer_genid();
948 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 return dst;
David S. Miller6431cbc2011-02-07 20:38:06 -0800950 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 return NULL;
952}
953
954static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
955{
956 struct rt6_info *rt = (struct rt6_info *) dst;
957
958 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000959 if (rt->rt6i_flags & RTF_CACHE) {
960 if (rt6_check_expired(rt)) {
961 ip6_del_rt(rt);
962 dst = NULL;
963 }
964 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000966 dst = NULL;
967 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +0000969 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970}
971
972static void ip6_link_failure(struct sk_buff *skb)
973{
974 struct rt6_info *rt;
975
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +0000976 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
Eric Dumazetadf30902009-06-02 05:19:30 +0000978 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 if (rt) {
980 if (rt->rt6i_flags&RTF_CACHE) {
Changli Gaod8d1f302010-06-10 23:31:35 -0700981 dst_set_expires(&rt->dst, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 rt->rt6i_flags |= RTF_EXPIRES;
983 } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
984 rt->rt6i_node->fn_sernum = -1;
985 }
986}
987
988static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
989{
990 struct rt6_info *rt6 = (struct rt6_info*)dst;
991
992 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
993 rt6->rt6i_flags |= RTF_MODIFIED;
994 if (mtu < IPV6_MIN_MTU) {
David S. Millerdefb3512010-12-08 21:16:57 -0800995 u32 features = dst_metric(dst, RTAX_FEATURES);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 mtu = IPV6_MIN_MTU;
David S. Millerdefb3512010-12-08 21:16:57 -0800997 features |= RTAX_FEATURE_ALLFRAG;
998 dst_metric_set(dst, RTAX_FEATURES, features);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 }
David S. Millerdefb3512010-12-08 21:16:57 -08001000 dst_metric_set(dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 }
1002}
1003
David S. Miller0dbaee32010-12-13 12:52:14 -08001004static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005{
David S. Miller0dbaee32010-12-13 12:52:14 -08001006 struct net_device *dev = dst->dev;
1007 unsigned int mtu = dst_mtu(dst);
1008 struct net *net = dev_net(dev);
1009
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
1011
Daniel Lezcano55786892008-03-04 13:47:47 -08001012 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
1013 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
1015 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001016 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
1017 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
1018 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 * rely only on pmtu discovery"
1020 */
1021 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
1022 mtu = IPV6_MAXPLEN;
1023 return mtu;
1024}
1025
David S. Millerd33e4552010-12-14 13:01:14 -08001026static unsigned int ip6_default_mtu(const struct dst_entry *dst)
1027{
1028 unsigned int mtu = IPV6_MIN_MTU;
1029 struct inet6_dev *idev;
1030
1031 rcu_read_lock();
1032 idev = __in6_dev_get(dst->dev);
1033 if (idev)
1034 mtu = idev->cnf.mtu6;
1035 rcu_read_unlock();
1036
1037 return mtu;
1038}
1039
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001040static struct dst_entry *icmp6_dst_gc_list;
1041static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001042
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001043struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001045 const struct in6_addr *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046{
1047 struct rt6_info *rt;
1048 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001049 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
1051 if (unlikely(idev == NULL))
1052 return NULL;
1053
David S. Miller957c6652011-06-24 15:25:00 -07001054 rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 if (unlikely(rt == NULL)) {
1056 in6_dev_put(idev);
1057 goto out;
1058 }
1059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 if (neigh)
1061 neigh_hold(neigh);
David S. Miller14deae42009-01-04 16:04:39 -08001062 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 neigh = ndisc_get_neigh(dev, addr);
David S. Miller14deae42009-01-04 16:04:39 -08001064 if (IS_ERR(neigh))
1065 neigh = NULL;
1066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 rt->rt6i_idev = idev;
Eric Dumazet8a533662012-02-09 16:13:19 -05001069 dst_set_neighbour(&rt->dst, neigh);
Changli Gaod8d1f302010-06-10 23:31:35 -07001070 atomic_set(&rt->dst.__refcnt, 1);
David S. Millerdefb3512010-12-08 21:16:57 -08001071 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
Changli Gaod8d1f302010-06-10 23:31:35 -07001072 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001074 spin_lock_bh(&icmp6_dst_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001075 rt->dst.next = icmp6_dst_gc_list;
1076 icmp6_dst_gc_list = &rt->dst;
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001077 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
Daniel Lezcano55786892008-03-04 13:47:47 -08001079 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
1081out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001082 return &rt->dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083}
1084
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001085int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086{
Hagen Paul Pfeifere9476e92011-02-25 05:45:19 +00001087 struct dst_entry *dst, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001088 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001090 spin_lock_bh(&icmp6_dst_lock);
1091 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001092
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 while ((dst = *pprev) != NULL) {
1094 if (!atomic_read(&dst->__refcnt)) {
1095 *pprev = dst->next;
1096 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 } else {
1098 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001099 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 }
1101 }
1102
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001103 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001104
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001105 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106}
1107
David S. Miller1e493d12008-09-10 17:27:15 -07001108static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
1109 void *arg)
1110{
1111 struct dst_entry *dst, **pprev;
1112
1113 spin_lock_bh(&icmp6_dst_lock);
1114 pprev = &icmp6_dst_gc_list;
1115 while ((dst = *pprev) != NULL) {
1116 struct rt6_info *rt = (struct rt6_info *) dst;
1117 if (func(rt, arg)) {
1118 *pprev = dst->next;
1119 dst_free(dst);
1120 } else {
1121 pprev = &dst->next;
1122 }
1123 }
1124 spin_unlock_bh(&icmp6_dst_lock);
1125}
1126
Daniel Lezcano569d3642008-01-18 03:56:57 -08001127static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 unsigned long now = jiffies;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00001130 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001131 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1132 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1133 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1134 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1135 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001136 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
Eric Dumazetfc66f952010-10-08 06:37:34 +00001138 entries = dst_entries_get_fast(ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08001139 if (time_after(rt_last_gc + rt_min_interval, now) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00001140 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 goto out;
1142
Benjamin Thery6891a342008-03-04 13:49:47 -08001143 net->ipv6.ip6_rt_gc_expire++;
1144 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1145 net->ipv6.ip6_rt_last_gc = now;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001146 entries = dst_entries_get_slow(ops);
1147 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08001148 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001150 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00001151 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152}
1153
1154/* Clean host part of a prefix. Not necessary in radix tree,
1155 but results in cleaner routing tables.
1156
1157 Remove it only when all the things will work!
1158 */
1159
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001160int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161{
David S. Miller5170ae82010-12-12 21:35:57 -08001162 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
David S. Millera02e4b72010-12-12 21:39:02 -08001163 if (hoplimit == 0) {
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001164 struct net_device *dev = dst->dev;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001165 struct inet6_dev *idev;
1166
1167 rcu_read_lock();
1168 idev = __in6_dev_get(dev);
1169 if (idev)
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001170 hoplimit = idev->cnf.hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001171 else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001172 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Eric Dumazetc68f24c2010-06-14 04:46:20 +00001173 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 }
1175 return hoplimit;
1176}
David S. Millerabbf46a2010-12-12 21:14:46 -08001177EXPORT_SYMBOL(ip6_dst_hoplimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
1179/*
1180 *
1181 */
1182
Thomas Graf86872cb2006-08-22 00:01:08 -07001183int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184{
1185 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001186 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 struct rt6_info *rt = NULL;
1188 struct net_device *dev = NULL;
1189 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001190 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 int addr_type;
1192
Thomas Graf86872cb2006-08-22 00:01:08 -07001193 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 return -EINVAL;
1195#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001196 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 return -EINVAL;
1198#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001199 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001201 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 if (!dev)
1203 goto out;
1204 idev = in6_dev_get(dev);
1205 if (!idev)
1206 goto out;
1207 }
1208
Thomas Graf86872cb2006-08-22 00:01:08 -07001209 if (cfg->fc_metric == 0)
1210 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211
Daniel Lezcano55786892008-03-04 13:47:47 -08001212 table = fib6_new_table(net, cfg->fc_table);
Thomas Grafc71099a2006-08-04 23:20:06 -07001213 if (table == NULL) {
1214 err = -ENOBUFS;
1215 goto out;
1216 }
1217
David S. Miller957c6652011-06-24 15:25:00 -07001218 rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL, DST_NOCOUNT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
1220 if (rt == NULL) {
1221 err = -ENOMEM;
1222 goto out;
1223 }
1224
Changli Gaod8d1f302010-06-10 23:31:35 -07001225 rt->dst.obsolete = -1;
YOSHIFUJI Hideaki6f704992008-05-19 16:56:11 -07001226 rt->rt6i_expires = (cfg->fc_flags & RTF_EXPIRES) ?
1227 jiffies + clock_t_to_jiffies(cfg->fc_expires) :
1228 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229
Thomas Graf86872cb2006-08-22 00:01:08 -07001230 if (cfg->fc_protocol == RTPROT_UNSPEC)
1231 cfg->fc_protocol = RTPROT_BOOT;
1232 rt->rt6i_protocol = cfg->fc_protocol;
1233
1234 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
1236 if (addr_type & IPV6_ADDR_MULTICAST)
Changli Gaod8d1f302010-06-10 23:31:35 -07001237 rt->dst.input = ip6_mc_input;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001238 else if (cfg->fc_flags & RTF_LOCAL)
1239 rt->dst.input = ip6_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 else
Changli Gaod8d1f302010-06-10 23:31:35 -07001241 rt->dst.input = ip6_forward;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
Changli Gaod8d1f302010-06-10 23:31:35 -07001243 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
Thomas Graf86872cb2006-08-22 00:01:08 -07001245 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1246 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 if (rt->rt6i_dst.plen == 128)
David S. Miller11d53b42011-06-24 15:23:34 -07001248 rt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249
1250#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001251 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1252 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253#endif
1254
Thomas Graf86872cb2006-08-22 00:01:08 -07001255 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
1257 /* We cannot add true routes via loopback here,
1258 they would result in kernel looping; promote them to reject routes
1259 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001260 if ((cfg->fc_flags & RTF_REJECT) ||
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00001261 (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK)
1262 && !(cfg->fc_flags&RTF_LOCAL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001264 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 if (dev) {
1266 dev_put(dev);
1267 in6_dev_put(idev);
1268 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001269 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 dev_hold(dev);
1271 idev = in6_dev_get(dev);
1272 if (!idev) {
1273 err = -ENODEV;
1274 goto out;
1275 }
1276 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001277 rt->dst.output = ip6_pkt_discard_out;
1278 rt->dst.input = ip6_pkt_discard;
1279 rt->dst.error = -ENETUNREACH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1281 goto install_route;
1282 }
1283
Thomas Graf86872cb2006-08-22 00:01:08 -07001284 if (cfg->fc_flags & RTF_GATEWAY) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001285 const struct in6_addr *gw_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 int gwa_type;
1287
Thomas Graf86872cb2006-08-22 00:01:08 -07001288 gw_addr = &cfg->fc_gateway;
1289 ipv6_addr_copy(&rt->rt6i_gateway, gw_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 gwa_type = ipv6_addr_type(gw_addr);
1291
1292 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1293 struct rt6_info *grt;
1294
1295 /* IPv6 strictly inhibits using not link-local
1296 addresses as nexthop address.
1297 Otherwise, router will not able to send redirects.
1298 It is very good, but in some (rare!) circumstances
1299 (SIT, PtP, NBMA NOARP links) it is handy to allow
1300 some exceptions. --ANK
1301 */
1302 err = -EINVAL;
1303 if (!(gwa_type&IPV6_ADDR_UNICAST))
1304 goto out;
1305
Daniel Lezcano55786892008-03-04 13:47:47 -08001306 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307
1308 err = -EHOSTUNREACH;
1309 if (grt == NULL)
1310 goto out;
1311 if (dev) {
1312 if (dev != grt->rt6i_dev) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001313 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 goto out;
1315 }
1316 } else {
1317 dev = grt->rt6i_dev;
1318 idev = grt->rt6i_idev;
1319 dev_hold(dev);
1320 in6_dev_hold(grt->rt6i_idev);
1321 }
1322 if (!(grt->rt6i_flags&RTF_GATEWAY))
1323 err = 0;
Changli Gaod8d1f302010-06-10 23:31:35 -07001324 dst_release(&grt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325
1326 if (err)
1327 goto out;
1328 }
1329 err = -EINVAL;
1330 if (dev == NULL || (dev->flags&IFF_LOOPBACK))
1331 goto out;
1332 }
1333
1334 err = -ENODEV;
1335 if (dev == NULL)
1336 goto out;
1337
Daniel Walterc3968a82011-04-13 21:10:57 +00001338 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
1339 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
1340 err = -EINVAL;
1341 goto out;
1342 }
1343 ipv6_addr_copy(&rt->rt6i_prefsrc.addr, &cfg->fc_prefsrc);
1344 rt->rt6i_prefsrc.plen = 128;
1345 } else
1346 rt->rt6i_prefsrc.plen = 0;
1347
Thomas Graf86872cb2006-08-22 00:01:08 -07001348 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
Eric Dumazet8a533662012-02-09 16:13:19 -05001349 struct neighbour *neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);
1350 if (IS_ERR(neigh)) {
1351 err = PTR_ERR(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 goto out;
1353 }
Eric Dumazet8a533662012-02-09 16:13:19 -05001354 dst_set_neighbour(&rt->dst, neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 }
1356
Thomas Graf86872cb2006-08-22 00:01:08 -07001357 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358
1359install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001360 if (cfg->fc_mx) {
1361 struct nlattr *nla;
1362 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Thomas Graf86872cb2006-08-22 00:01:08 -07001364 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001365 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001366
1367 if (type) {
1368 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 err = -EINVAL;
1370 goto out;
1371 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001372
David S. Millerdefb3512010-12-08 21:16:57 -08001373 dst_metric_set(&rt->dst, type, nla_get_u32(nla));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 }
1376 }
1377
Changli Gaod8d1f302010-06-10 23:31:35 -07001378 rt->dst.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001380 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001381
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001382 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001383
Thomas Graf86872cb2006-08-22 00:01:08 -07001384 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
1386out:
1387 if (dev)
1388 dev_put(dev);
1389 if (idev)
1390 in6_dev_put(idev);
1391 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001392 dst_free(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 return err;
1394}
1395
Thomas Graf86872cb2006-08-22 00:01:08 -07001396static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
1398 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001399 struct fib6_table *table;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001400 struct net *net = dev_net(rt->rt6i_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001402 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001403 return -ENOENT;
1404
Thomas Grafc71099a2006-08-04 23:20:06 -07001405 table = rt->rt6i_table;
1406 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
Thomas Graf86872cb2006-08-22 00:01:08 -07001408 err = fib6_del(rt, info);
Changli Gaod8d1f302010-06-10 23:31:35 -07001409 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410
Thomas Grafc71099a2006-08-04 23:20:06 -07001411 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
1413 return err;
1414}
1415
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001416int ip6_del_rt(struct rt6_info *rt)
1417{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001418 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001419 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001420 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001421 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001422}
1423
Thomas Graf86872cb2006-08-22 00:01:08 -07001424static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425{
Thomas Grafc71099a2006-08-04 23:20:06 -07001426 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 struct fib6_node *fn;
1428 struct rt6_info *rt;
1429 int err = -ESRCH;
1430
Daniel Lezcano55786892008-03-04 13:47:47 -08001431 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
Thomas Grafc71099a2006-08-04 23:20:06 -07001432 if (table == NULL)
1433 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Thomas Grafc71099a2006-08-04 23:20:06 -07001435 read_lock_bh(&table->tb6_lock);
1436
1437 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001438 &cfg->fc_dst, cfg->fc_dst_len,
1439 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001440
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 if (fn) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001442 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001443 if (cfg->fc_ifindex &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 (rt->rt6i_dev == NULL ||
Thomas Graf86872cb2006-08-22 00:01:08 -07001445 rt->rt6i_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001447 if (cfg->fc_flags & RTF_GATEWAY &&
1448 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001450 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001452 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001453 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
Thomas Graf86872cb2006-08-22 00:01:08 -07001455 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 }
1457 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001458 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459
1460 return err;
1461}
1462
1463/*
1464 * Handle redirects
1465 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001466struct ip6rd_flowi {
David S. Miller4c9483b2011-03-12 16:22:43 -05001467 struct flowi6 fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001468 struct in6_addr gateway;
1469};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001471static struct rt6_info *__ip6_route_redirect(struct net *net,
1472 struct fib6_table *table,
David S. Miller4c9483b2011-03-12 16:22:43 -05001473 struct flowi6 *fl6,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001474 int flags)
1475{
David S. Miller4c9483b2011-03-12 16:22:43 -05001476 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001477 struct rt6_info *rt;
1478 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001479
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001481 * Get the "current" route for this destination and
1482 * check if the redirect has come from approriate router.
1483 *
1484 * RFC 2461 specifies that redirects should only be
1485 * accepted if they come from the nexthop to the target.
1486 * Due to the way the routes are chosen, this notion
1487 * is a bit fuzzy and one might need to check all possible
1488 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
Thomas Grafc71099a2006-08-04 23:20:06 -07001491 read_lock_bh(&table->tb6_lock);
David S. Miller4c9483b2011-03-12 16:22:43 -05001492 fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001493restart:
Changli Gaod8d1f302010-06-10 23:31:35 -07001494 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001495 /*
1496 * Current route is on-link; redirect is always invalid.
1497 *
1498 * Seems, previous statement is not true. It could
1499 * be node, which looks for us as on-link (f.e. proxy ndisc)
1500 * But then router serving it might decide, that we should
1501 * know truth 8)8) --ANK (980726).
1502 */
1503 if (rt6_check_expired(rt))
1504 continue;
1505 if (!(rt->rt6i_flags & RTF_GATEWAY))
1506 continue;
David S. Miller4c9483b2011-03-12 16:22:43 -05001507 if (fl6->flowi6_oif != rt->rt6i_dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001508 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001509 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001510 continue;
1511 break;
1512 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001513
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001514 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001515 rt = net->ipv6.ip6_null_entry;
David S. Miller4c9483b2011-03-12 16:22:43 -05001516 BACKTRACK(net, &fl6->saddr);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001517out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001518 dst_hold(&rt->dst);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001519
1520 read_unlock_bh(&table->tb6_lock);
1521
1522 return rt;
1523};
1524
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001525static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
1526 const struct in6_addr *src,
1527 const struct in6_addr *gateway,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001528 struct net_device *dev)
1529{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001530 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001531 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001532 struct ip6rd_flowi rdfl = {
David S. Miller4c9483b2011-03-12 16:22:43 -05001533 .fl6 = {
1534 .flowi6_oif = dev->ifindex,
1535 .daddr = *dest,
1536 .saddr = *src,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001537 },
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001538 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001539
Brian Haley86c36ce2009-10-07 13:58:01 -07001540 ipv6_addr_copy(&rdfl.gateway, gateway);
1541
Thomas Grafadaa70b2006-10-13 15:01:03 -07001542 if (rt6_need_strict(dest))
1543 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001544
David S. Miller4c9483b2011-03-12 16:22:43 -05001545 return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001546 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001547}
1548
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001549void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
1550 const struct in6_addr *saddr,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001551 struct neighbour *neigh, u8 *lladdr, int on_link)
1552{
1553 struct rt6_info *rt, *nrt = NULL;
1554 struct netevent_redirect netevent;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001555 struct net *net = dev_net(neigh->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001556
1557 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1558
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001559 if (rt == net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 if (net_ratelimit())
1561 printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
1562 "for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001563 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 }
1565
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 /*
1567 * We have finally decided to accept it.
1568 */
1569
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001570 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1572 NEIGH_UPDATE_F_OVERRIDE|
1573 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1574 NEIGH_UPDATE_F_ISROUTER))
1575 );
1576
1577 /*
1578 * Redirect received -> path was valid.
1579 * Look, redirects are sent only in response to data packets,
1580 * so that this nexthop apparently is reachable. --ANK
1581 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001582 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
1584 /* Duplicate redirect: silently ignore. */
Eric Dumazet8a533662012-02-09 16:13:19 -05001585 if (neigh == dst_get_neighbour_raw(&rt->dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 goto out;
1587
1588 nrt = ip6_rt_copy(rt);
1589 if (nrt == NULL)
1590 goto out;
1591
1592 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1593 if (on_link)
1594 nrt->rt6i_flags &= ~RTF_GATEWAY;
1595
1596 ipv6_addr_copy(&nrt->rt6i_dst.addr, dest);
1597 nrt->rt6i_dst.plen = 128;
Changli Gaod8d1f302010-06-10 23:31:35 -07001598 nrt->dst.flags |= DST_HOST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
1600 ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key);
Eric Dumazet8a533662012-02-09 16:13:19 -05001601 dst_set_neighbour(&nrt->dst, neigh_clone(neigh));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Thomas Graf40e22e82006-08-22 00:00:45 -07001603 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 goto out;
1605
Changli Gaod8d1f302010-06-10 23:31:35 -07001606 netevent.old = &rt->dst;
1607 netevent.new = &nrt->dst;
Tom Tucker8d717402006-07-30 20:43:36 -07001608 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1609
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 if (rt->rt6i_flags&RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001611 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 return;
1613 }
1614
1615out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001616 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617}
1618
1619/*
1620 * Handle ICMP "packet too big" messages
1621 * i.e. Path MTU discovery
1622 */
1623
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001624static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr,
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001625 struct net *net, u32 pmtu, int ifindex)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626{
1627 struct rt6_info *rt, *nrt;
1628 int allfrag = 0;
Andrey Vagind3052b52010-12-11 15:20:11 +00001629again:
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001630 rt = rt6_lookup(net, daddr, saddr, ifindex, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 if (rt == NULL)
1632 return;
1633
Andrey Vagind3052b52010-12-11 15:20:11 +00001634 if (rt6_check_expired(rt)) {
1635 ip6_del_rt(rt);
1636 goto again;
1637 }
1638
Changli Gaod8d1f302010-06-10 23:31:35 -07001639 if (pmtu >= dst_mtu(&rt->dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 goto out;
1641
1642 if (pmtu < IPV6_MIN_MTU) {
1643 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001644 * According to RFC2460, PMTU is set to the IPv6 Minimum Link
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 * MTU (1280) and a fragment header should always be included
1646 * after a node receiving Too Big message reporting PMTU is
1647 * less than the IPv6 Minimum Link MTU.
1648 */
1649 pmtu = IPV6_MIN_MTU;
1650 allfrag = 1;
1651 }
1652
1653 /* New mtu received -> path was valid.
1654 They are sent only in response to data packets,
1655 so that this nexthop apparently is reachable. --ANK
1656 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001657 dst_confirm(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
1659 /* Host route. If it is static, it would be better
1660 not to override it, but add new one, so that
1661 when cache entry will expire old pmtu
1662 would return automatically.
1663 */
1664 if (rt->rt6i_flags & RTF_CACHE) {
David S. Millerdefb3512010-12-08 21:16:57 -08001665 dst_metric_set(&rt->dst, RTAX_MTU, pmtu);
1666 if (allfrag) {
1667 u32 features = dst_metric(&rt->dst, RTAX_FEATURES);
1668 features |= RTAX_FEATURE_ALLFRAG;
1669 dst_metric_set(&rt->dst, RTAX_FEATURES, features);
1670 }
Changli Gaod8d1f302010-06-10 23:31:35 -07001671 dst_set_expires(&rt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
1673 goto out;
1674 }
1675
1676 /* Network route.
1677 Two cases are possible:
1678 1. It is connected route. Action: COW
1679 2. It is gatewayed route or NONEXTHOP route. Action: clone it.
1680 */
Eric Dumazet8a533662012-02-09 16:13:19 -05001681 if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001682 nrt = rt6_alloc_cow(rt, daddr, saddr);
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001683 else
1684 nrt = rt6_alloc_clone(rt, daddr);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001685
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001686 if (nrt) {
David S. Millerdefb3512010-12-08 21:16:57 -08001687 dst_metric_set(&nrt->dst, RTAX_MTU, pmtu);
1688 if (allfrag) {
1689 u32 features = dst_metric(&nrt->dst, RTAX_FEATURES);
1690 features |= RTAX_FEATURE_ALLFRAG;
1691 dst_metric_set(&nrt->dst, RTAX_FEATURES, features);
1692 }
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001693
1694 /* According to RFC 1981, detecting PMTU increase shouldn't be
1695 * happened within 5 mins, the recommended timer is 10 mins.
1696 * Here this route expiration time is set to ip6_rt_mtu_expires
1697 * which is 10 mins. After 10 mins the decreased pmtu is expired
1698 * and detecting PMTU increase will be automatically happened.
1699 */
Changli Gaod8d1f302010-06-10 23:31:35 -07001700 dst_set_expires(&nrt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001701 nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
1702
Thomas Graf40e22e82006-08-22 00:00:45 -07001703 ip6_ins_rt(nrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705out:
Changli Gaod8d1f302010-06-10 23:31:35 -07001706 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707}
1708
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001709void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr,
Maciej Żenczykowskiae878ae2010-10-03 14:49:00 -07001710 struct net_device *dev, u32 pmtu)
1711{
1712 struct net *net = dev_net(dev);
1713
1714 /*
1715 * RFC 1981 states that a node "MUST reduce the size of the packets it
1716 * is sending along the path" that caused the Packet Too Big message.
1717 * Since it's not possible in the general case to determine which
1718 * interface was used to send the original packet, we update the MTU
1719 * on the interface that will be used to send future packets. We also
1720 * update the MTU on the interface that received the Packet Too Big in
1721 * case the original packet was forced out that interface with
1722 * SO_BINDTODEVICE or similar. This is the next best thing to the
1723 * correct behaviour, which would be to update the MTU on all
1724 * interfaces.
1725 */
1726 rt6_do_pmtu_disc(daddr, saddr, net, pmtu, 0);
1727 rt6_do_pmtu_disc(daddr, saddr, net, pmtu, dev->ifindex);
1728}
1729
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730/*
1731 * Misc support functions
1732 */
1733
1734static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
1735{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001736 struct net *net = dev_net(ort->rt6i_dev);
David S. Miller5c1e6aa2011-04-28 14:13:38 -07001737 struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
David S. Miller957c6652011-06-24 15:25:00 -07001738 ort->dst.dev, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
1740 if (rt) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001741 rt->dst.input = ort->dst.input;
1742 rt->dst.output = ort->dst.output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743
David S. Millerdefb3512010-12-08 21:16:57 -08001744 dst_copy_metrics(&rt->dst, &ort->dst);
Changli Gaod8d1f302010-06-10 23:31:35 -07001745 rt->dst.error = ort->dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 rt->rt6i_idev = ort->rt6i_idev;
1747 if (rt->rt6i_idev)
1748 in6_dev_hold(rt->rt6i_idev);
Changli Gaod8d1f302010-06-10 23:31:35 -07001749 rt->dst.lastuse = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750 rt->rt6i_expires = 0;
1751
1752 ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
1753 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
1754 rt->rt6i_metric = 0;
1755
1756 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1757#ifdef CONFIG_IPV6_SUBTREES
1758 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1759#endif
Florian Westphal0f6c6392011-05-20 11:27:24 +00001760 memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));
Thomas Grafc71099a2006-08-04 23:20:06 -07001761 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 }
1763 return rt;
1764}
1765
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001766#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001767static struct rt6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001768 const struct in6_addr *prefix, int prefixlen,
1769 const struct in6_addr *gwaddr, int ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001770{
1771 struct fib6_node *fn;
1772 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001773 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001774
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001775 table = fib6_get_table(net, RT6_TABLE_INFO);
Thomas Grafc71099a2006-08-04 23:20:06 -07001776 if (table == NULL)
1777 return NULL;
1778
1779 write_lock_bh(&table->tb6_lock);
1780 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001781 if (!fn)
1782 goto out;
1783
Changli Gaod8d1f302010-06-10 23:31:35 -07001784 for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001785 if (rt->rt6i_dev->ifindex != ifindex)
1786 continue;
1787 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1788 continue;
1789 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1790 continue;
Changli Gaod8d1f302010-06-10 23:31:35 -07001791 dst_hold(&rt->dst);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001792 break;
1793 }
1794out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001795 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001796 return rt;
1797}
1798
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001799static struct rt6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001800 const struct in6_addr *prefix, int prefixlen,
1801 const struct in6_addr *gwaddr, int ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001802 unsigned pref)
1803{
Thomas Graf86872cb2006-08-22 00:01:08 -07001804 struct fib6_config cfg = {
1805 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001806 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001807 .fc_ifindex = ifindex,
1808 .fc_dst_len = prefixlen,
1809 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1810 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001811 .fc_nlinfo.pid = 0,
1812 .fc_nlinfo.nlh = NULL,
1813 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001814 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001815
Thomas Graf86872cb2006-08-22 00:01:08 -07001816 ipv6_addr_copy(&cfg.fc_dst, prefix);
1817 ipv6_addr_copy(&cfg.fc_gateway, gwaddr);
1818
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001819 /* We should treat it as a default route if prefix length is 0. */
1820 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001821 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001822
Thomas Graf86872cb2006-08-22 00:01:08 -07001823 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001824
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001825 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001826}
1827#endif
1828
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001829struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001830{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001832 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001834 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
Thomas Grafc71099a2006-08-04 23:20:06 -07001835 if (table == NULL)
1836 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
Thomas Grafc71099a2006-08-04 23:20:06 -07001838 write_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001839 for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 if (dev == rt->rt6i_dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001841 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1843 break;
1844 }
1845 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001846 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001847 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 return rt;
1849}
1850
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001851struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001852 struct net_device *dev,
1853 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854{
Thomas Graf86872cb2006-08-22 00:01:08 -07001855 struct fib6_config cfg = {
1856 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001857 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001858 .fc_ifindex = dev->ifindex,
1859 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1860 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001861 .fc_nlinfo.pid = 0,
1862 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001863 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001864 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865
Thomas Graf86872cb2006-08-22 00:01:08 -07001866 ipv6_addr_copy(&cfg.fc_gateway, gwaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867
Thomas Graf86872cb2006-08-22 00:01:08 -07001868 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 return rt6_get_dflt_router(gwaddr, dev);
1871}
1872
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001873void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874{
1875 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001876 struct fib6_table *table;
1877
1878 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001879 table = fib6_get_table(net, RT6_TABLE_DFLT);
Thomas Grafc71099a2006-08-04 23:20:06 -07001880 if (table == NULL)
1881 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882
1883restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001884 read_lock_bh(&table->tb6_lock);
Changli Gaod8d1f302010-06-10 23:31:35 -07001885 for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07001887 dst_hold(&rt->dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001888 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001889 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 goto restart;
1891 }
1892 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001893 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894}
1895
Daniel Lezcano55786892008-03-04 13:47:47 -08001896static void rtmsg_to_fib6_config(struct net *net,
1897 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001898 struct fib6_config *cfg)
1899{
1900 memset(cfg, 0, sizeof(*cfg));
1901
1902 cfg->fc_table = RT6_TABLE_MAIN;
1903 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1904 cfg->fc_metric = rtmsg->rtmsg_metric;
1905 cfg->fc_expires = rtmsg->rtmsg_info;
1906 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1907 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1908 cfg->fc_flags = rtmsg->rtmsg_flags;
1909
Daniel Lezcano55786892008-03-04 13:47:47 -08001910 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001911
Thomas Graf86872cb2006-08-22 00:01:08 -07001912 ipv6_addr_copy(&cfg->fc_dst, &rtmsg->rtmsg_dst);
1913 ipv6_addr_copy(&cfg->fc_src, &rtmsg->rtmsg_src);
1914 ipv6_addr_copy(&cfg->fc_gateway, &rtmsg->rtmsg_gateway);
1915}
1916
Daniel Lezcano55786892008-03-04 13:47:47 -08001917int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918{
Thomas Graf86872cb2006-08-22 00:01:08 -07001919 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 struct in6_rtmsg rtmsg;
1921 int err;
1922
1923 switch(cmd) {
1924 case SIOCADDRT: /* Add a route */
1925 case SIOCDELRT: /* Delete a route */
1926 if (!capable(CAP_NET_ADMIN))
1927 return -EPERM;
1928 err = copy_from_user(&rtmsg, arg,
1929 sizeof(struct in6_rtmsg));
1930 if (err)
1931 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001932
Daniel Lezcano55786892008-03-04 13:47:47 -08001933 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001934
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 rtnl_lock();
1936 switch (cmd) {
1937 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001938 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 break;
1940 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001941 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 break;
1943 default:
1944 err = -EINVAL;
1945 }
1946 rtnl_unlock();
1947
1948 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001949 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950
1951 return -EINVAL;
1952}
1953
1954/*
1955 * Drop the packet on the floor
1956 */
1957
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07001958static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001960 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00001961 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001962 switch (ipstats_mib_noroutes) {
1963 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001964 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00001965 if (type == IPV6_ADDR_ANY) {
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07001966 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
1967 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001968 break;
1969 }
1970 /* FALLTHROUGH */
1971 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07001972 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
1973 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001974 break;
1975 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00001976 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 kfree_skb(skb);
1978 return 0;
1979}
1980
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001981static int ip6_pkt_discard(struct sk_buff *skb)
1982{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001983 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001984}
1985
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03001986static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987{
Eric Dumazetadf30902009-06-02 05:19:30 +00001988 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001989 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990}
1991
David S. Miller6723ab52006-10-18 21:20:57 -07001992#ifdef CONFIG_IPV6_MULTIPLE_TABLES
1993
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001994static int ip6_pkt_prohibit(struct sk_buff *skb)
1995{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001996 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001997}
1998
1999static int ip6_pkt_prohibit_out(struct sk_buff *skb)
2000{
Eric Dumazetadf30902009-06-02 05:19:30 +00002001 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07002002 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07002003}
2004
David S. Miller6723ab52006-10-18 21:20:57 -07002005#endif
2006
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007/*
2008 * Allocate a dst for local (unicast / anycast) address.
2009 */
2010
2011struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
2012 const struct in6_addr *addr,
2013 int anycast)
2014{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002015 struct net *net = dev_net(idev->dev);
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002016 struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops,
David S. Miller957c6652011-06-24 15:25:00 -07002017 net->loopback_dev, 0);
David S. Miller14deae42009-01-04 16:04:39 -08002018 struct neighbour *neigh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019
Ben Greear40385652010-11-08 12:33:48 +00002020 if (rt == NULL) {
2021 if (net_ratelimit())
2022 pr_warning("IPv6: Maximum number of routes reached,"
2023 " consider increasing route/max_size.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 return ERR_PTR(-ENOMEM);
Ben Greear40385652010-11-08 12:33:48 +00002025 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 in6_dev_hold(idev);
2028
David S. Miller11d53b42011-06-24 15:23:34 -07002029 rt->dst.flags |= DST_HOST;
Changli Gaod8d1f302010-06-10 23:31:35 -07002030 rt->dst.input = ip6_input;
2031 rt->dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 rt->rt6i_idev = idev;
Changli Gaod8d1f302010-06-10 23:31:35 -07002033 rt->dst.obsolete = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034
2035 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09002036 if (anycast)
2037 rt->rt6i_flags |= RTF_ANYCAST;
2038 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 rt->rt6i_flags |= RTF_LOCAL;
David S. Miller14deae42009-01-04 16:04:39 -08002040 neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
2041 if (IS_ERR(neigh)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002042 dst_free(&rt->dst);
David S. Miller14deae42009-01-04 16:04:39 -08002043
David S. Miller29546a62011-03-03 12:10:37 -08002044 return ERR_CAST(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045 }
Eric Dumazet8a533662012-02-09 16:13:19 -05002046 dst_set_neighbour(&rt->dst, neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047
2048 ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
2049 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08002050 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051
Changli Gaod8d1f302010-06-10 23:31:35 -07002052 atomic_set(&rt->dst.__refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053
2054 return rt;
2055}
2056
Daniel Walterc3968a82011-04-13 21:10:57 +00002057int ip6_route_get_saddr(struct net *net,
2058 struct rt6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002059 const struct in6_addr *daddr,
Daniel Walterc3968a82011-04-13 21:10:57 +00002060 unsigned int prefs,
2061 struct in6_addr *saddr)
2062{
2063 struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt);
2064 int err = 0;
2065 if (rt->rt6i_prefsrc.plen)
2066 ipv6_addr_copy(saddr, &rt->rt6i_prefsrc.addr);
2067 else
2068 err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
2069 daddr, prefs, saddr);
2070 return err;
2071}
2072
2073/* remove deleted ip from prefsrc entries */
2074struct arg_dev_net_ip {
2075 struct net_device *dev;
2076 struct net *net;
2077 struct in6_addr *addr;
2078};
2079
2080static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
2081{
2082 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
2083 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
2084 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
2085
2086 if (((void *)rt->rt6i_dev == dev || dev == NULL) &&
2087 rt != net->ipv6.ip6_null_entry &&
2088 ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
2089 /* remove prefsrc entry */
2090 rt->rt6i_prefsrc.plen = 0;
2091 }
2092 return 0;
2093}
2094
2095void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
2096{
2097 struct net *net = dev_net(ifp->idev->dev);
2098 struct arg_dev_net_ip adni = {
2099 .dev = ifp->idev->dev,
2100 .net = net,
2101 .addr = &ifp->addr,
2102 };
2103 fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni);
2104}
2105
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002106struct arg_dev_net {
2107 struct net_device *dev;
2108 struct net *net;
2109};
2110
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111static int fib6_ifdown(struct rt6_info *rt, void *arg)
2112{
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002113 const struct arg_dev_net *adn = arg;
2114 const struct net_device *dev = adn->dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002115
stephen hemmingerbc3ef662010-12-16 17:42:40 +00002116 if ((rt->rt6i_dev == dev || dev == NULL) &&
2117 rt != adn->net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 RT6_TRACE("deleted by ifdown %p\n", rt);
2119 return -1;
2120 }
2121 return 0;
2122}
2123
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002124void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002126 struct arg_dev_net adn = {
2127 .dev = dev,
2128 .net = net,
2129 };
2130
2131 fib6_clean_all(net, fib6_ifdown, 0, &adn);
David S. Miller1e493d12008-09-10 17:27:15 -07002132 icmp6_clean_all(fib6_ifdown, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133}
2134
2135struct rt6_mtu_change_arg
2136{
2137 struct net_device *dev;
2138 unsigned mtu;
2139};
2140
2141static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
2142{
2143 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
2144 struct inet6_dev *idev;
2145
2146 /* In IPv6 pmtu discovery is not optional,
2147 so that RTAX_MTU lock cannot disable it.
2148 We still use this lock to block changes
2149 caused by addrconf/ndisc.
2150 */
2151
2152 idev = __in6_dev_get(arg->dev);
2153 if (idev == NULL)
2154 return 0;
2155
2156 /* For administrative MTU increase, there is no way to discover
2157 IPv6 PMTU increase, so PMTU increase should be updated here.
2158 Since RFC 1981 doesn't include administrative MTU increase
2159 update PMTU increase is a MUST. (i.e. jumbo frame)
2160 */
2161 /*
2162 If new MTU is less than route PMTU, this new MTU will be the
2163 lowest MTU in the path, update the route PMTU to reflect PMTU
2164 decreases; if new MTU is greater than route PMTU, and the
2165 old MTU is the lowest MTU in the path, update the route PMTU
2166 to reflect the increase. In this case if the other nodes' MTU
2167 also have the lowest MTU, TOO BIG MESSAGE will be lead to
2168 PMTU discouvery.
2169 */
2170 if (rt->rt6i_dev == arg->dev &&
Changli Gaod8d1f302010-06-10 23:31:35 -07002171 !dst_metric_locked(&rt->dst, RTAX_MTU) &&
2172 (dst_mtu(&rt->dst) >= arg->mtu ||
2173 (dst_mtu(&rt->dst) < arg->mtu &&
2174 dst_mtu(&rt->dst) == idev->cnf.mtu6))) {
David S. Millerdefb3512010-12-08 21:16:57 -08002175 dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07002176 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 return 0;
2178}
2179
2180void rt6_mtu_change(struct net_device *dev, unsigned mtu)
2181{
Thomas Grafc71099a2006-08-04 23:20:06 -07002182 struct rt6_mtu_change_arg arg = {
2183 .dev = dev,
2184 .mtu = mtu,
2185 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002187 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188}
2189
Patrick McHardyef7c79e2007-06-05 12:38:30 -07002190static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07002191 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07002192 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07002193 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07002194 [RTA_PRIORITY] = { .type = NLA_U32 },
2195 [RTA_METRICS] = { .type = NLA_NESTED },
2196};
2197
2198static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2199 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200{
Thomas Graf86872cb2006-08-22 00:01:08 -07002201 struct rtmsg *rtm;
2202 struct nlattr *tb[RTA_MAX+1];
2203 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204
Thomas Graf86872cb2006-08-22 00:01:08 -07002205 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2206 if (err < 0)
2207 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208
Thomas Graf86872cb2006-08-22 00:01:08 -07002209 err = -EINVAL;
2210 rtm = nlmsg_data(nlh);
2211 memset(cfg, 0, sizeof(*cfg));
2212
2213 cfg->fc_table = rtm->rtm_table;
2214 cfg->fc_dst_len = rtm->rtm_dst_len;
2215 cfg->fc_src_len = rtm->rtm_src_len;
2216 cfg->fc_flags = RTF_UP;
2217 cfg->fc_protocol = rtm->rtm_protocol;
2218
2219 if (rtm->rtm_type == RTN_UNREACHABLE)
2220 cfg->fc_flags |= RTF_REJECT;
2221
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002222 if (rtm->rtm_type == RTN_LOCAL)
2223 cfg->fc_flags |= RTF_LOCAL;
2224
Thomas Graf86872cb2006-08-22 00:01:08 -07002225 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2226 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002227 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002228
2229 if (tb[RTA_GATEWAY]) {
2230 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2231 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002233
2234 if (tb[RTA_DST]) {
2235 int plen = (rtm->rtm_dst_len + 7) >> 3;
2236
2237 if (nla_len(tb[RTA_DST]) < plen)
2238 goto errout;
2239
2240 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002242
2243 if (tb[RTA_SRC]) {
2244 int plen = (rtm->rtm_src_len + 7) >> 3;
2245
2246 if (nla_len(tb[RTA_SRC]) < plen)
2247 goto errout;
2248
2249 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002251
Daniel Walterc3968a82011-04-13 21:10:57 +00002252 if (tb[RTA_PREFSRC])
2253 nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16);
2254
Thomas Graf86872cb2006-08-22 00:01:08 -07002255 if (tb[RTA_OIF])
2256 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2257
2258 if (tb[RTA_PRIORITY])
2259 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2260
2261 if (tb[RTA_METRICS]) {
2262 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2263 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002265
2266 if (tb[RTA_TABLE])
2267 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2268
2269 err = 0;
2270errout:
2271 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272}
2273
Thomas Grafc127ea22007-03-22 11:58:32 -07002274static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275{
Thomas Graf86872cb2006-08-22 00:01:08 -07002276 struct fib6_config cfg;
2277 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
Thomas Graf86872cb2006-08-22 00:01:08 -07002279 err = rtm_to_fib6_config(skb, nlh, &cfg);
2280 if (err < 0)
2281 return err;
2282
2283 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284}
2285
Thomas Grafc127ea22007-03-22 11:58:32 -07002286static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287{
Thomas Graf86872cb2006-08-22 00:01:08 -07002288 struct fib6_config cfg;
2289 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290
Thomas Graf86872cb2006-08-22 00:01:08 -07002291 err = rtm_to_fib6_config(skb, nlh, &cfg);
2292 if (err < 0)
2293 return err;
2294
2295 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296}
2297
Thomas Graf339bf982006-11-10 14:10:15 -08002298static inline size_t rt6_nlmsg_size(void)
2299{
2300 return NLMSG_ALIGN(sizeof(struct rtmsg))
2301 + nla_total_size(16) /* RTA_SRC */
2302 + nla_total_size(16) /* RTA_DST */
2303 + nla_total_size(16) /* RTA_GATEWAY */
2304 + nla_total_size(16) /* RTA_PREFSRC */
2305 + nla_total_size(4) /* RTA_TABLE */
2306 + nla_total_size(4) /* RTA_IIF */
2307 + nla_total_size(4) /* RTA_OIF */
2308 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002309 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002310 + nla_total_size(sizeof(struct rta_cacheinfo));
2311}
2312
Brian Haley191cd582008-08-14 15:33:21 -07002313static int rt6_fill_node(struct net *net,
2314 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002315 struct in6_addr *dst, struct in6_addr *src,
2316 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002317 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318{
2319 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002320 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002321 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002322 u32 table;
Eric Dumazet8a533662012-02-09 16:13:19 -05002323 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324
2325 if (prefix) { /* user wants prefix routes only */
2326 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2327 /* success since this is not a prefix route */
2328 return 1;
2329 }
2330 }
2331
Thomas Graf2d7202b2006-08-22 00:01:27 -07002332 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
2333 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08002334 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002335
2336 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337 rtm->rtm_family = AF_INET6;
2338 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2339 rtm->rtm_src_len = rt->rt6i_src.plen;
2340 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002341 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002342 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002343 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002344 table = RT6_TABLE_UNSPEC;
2345 rtm->rtm_table = table;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002346 NLA_PUT_U32(skb, RTA_TABLE, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347 if (rt->rt6i_flags&RTF_REJECT)
2348 rtm->rtm_type = RTN_UNREACHABLE;
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00002349 else if (rt->rt6i_flags&RTF_LOCAL)
2350 rtm->rtm_type = RTN_LOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK))
2352 rtm->rtm_type = RTN_LOCAL;
2353 else
2354 rtm->rtm_type = RTN_UNICAST;
2355 rtm->rtm_flags = 0;
2356 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2357 rtm->rtm_protocol = rt->rt6i_protocol;
2358 if (rt->rt6i_flags&RTF_DYNAMIC)
2359 rtm->rtm_protocol = RTPROT_REDIRECT;
2360 else if (rt->rt6i_flags & RTF_ADDRCONF)
2361 rtm->rtm_protocol = RTPROT_KERNEL;
2362 else if (rt->rt6i_flags&RTF_DEFAULT)
2363 rtm->rtm_protocol = RTPROT_RA;
2364
2365 if (rt->rt6i_flags&RTF_CACHE)
2366 rtm->rtm_flags |= RTM_F_CLONED;
2367
2368 if (dst) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002369 NLA_PUT(skb, RTA_DST, 16, dst);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002370 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371 } else if (rtm->rtm_dst_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002372 NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373#ifdef CONFIG_IPV6_SUBTREES
2374 if (src) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002375 NLA_PUT(skb, RTA_SRC, 16, src);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002376 rtm->rtm_src_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 } else if (rtm->rtm_src_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002378 NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002380 if (iif) {
2381#ifdef CONFIG_IPV6_MROUTE
2382 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
Benjamin Thery8229efd2008-12-10 16:30:15 -08002383 int err = ip6mr_get_route(net, skb, rtm, nowait);
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002384 if (err <= 0) {
2385 if (!nowait) {
2386 if (err == 0)
2387 return 0;
2388 goto nla_put_failure;
2389 } else {
2390 if (err == -EMSGSIZE)
2391 goto nla_put_failure;
2392 }
2393 }
2394 } else
2395#endif
2396 NLA_PUT_U32(skb, RTA_IIF, iif);
2397 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 struct in6_addr saddr_buf;
Daniel Walterc3968a82011-04-13 21:10:57 +00002399 if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002400 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002402
Daniel Walterc3968a82011-04-13 21:10:57 +00002403 if (rt->rt6i_prefsrc.plen) {
2404 struct in6_addr saddr_buf;
2405 ipv6_addr_copy(&saddr_buf, &rt->rt6i_prefsrc.addr);
2406 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
2407 }
2408
David S. Millerdefb3512010-12-08 21:16:57 -08002409 if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002410 goto nla_put_failure;
2411
Eric Dumazet8a533662012-02-09 16:13:19 -05002412 rcu_read_lock();
2413 n = dst_get_neighbour(&rt->dst);
2414 if (n)
2415 NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
2416 rcu_read_unlock();
Thomas Graf2d7202b2006-08-22 00:01:27 -07002417
Changli Gaod8d1f302010-06-10 23:31:35 -07002418 if (rt->dst.dev)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002419 NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
2420
2421 NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
Thomas Grafe3703b32006-11-27 09:27:07 -08002422
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002423 if (!(rt->rt6i_flags & RTF_EXPIRES))
2424 expires = 0;
2425 else if (rt->rt6i_expires - jiffies < INT_MAX)
2426 expires = rt->rt6i_expires - jiffies;
2427 else
2428 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002429
Changli Gaod8d1f302010-06-10 23:31:35 -07002430 if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0,
2431 expires, rt->dst.error) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08002432 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433
Thomas Graf2d7202b2006-08-22 00:01:27 -07002434 return nlmsg_end(skb, nlh);
2435
2436nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002437 nlmsg_cancel(skb, nlh);
2438 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439}
2440
Patrick McHardy1b43af52006-08-10 23:11:17 -07002441int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442{
2443 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2444 int prefix;
2445
Thomas Graf2d7202b2006-08-22 00:01:27 -07002446 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2447 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2449 } else
2450 prefix = 0;
2451
Brian Haley191cd582008-08-14 15:33:21 -07002452 return rt6_fill_node(arg->net,
2453 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002455 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456}
2457
Thomas Grafc127ea22007-03-22 11:58:32 -07002458static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002460 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002461 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002463 struct sk_buff *skb;
2464 struct rtmsg *rtm;
David S. Miller4c9483b2011-03-12 16:22:43 -05002465 struct flowi6 fl6;
Thomas Grafab364a62006-08-22 00:01:47 -07002466 int err, iif = 0;
2467
2468 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2469 if (err < 0)
2470 goto errout;
2471
2472 err = -EINVAL;
David S. Miller4c9483b2011-03-12 16:22:43 -05002473 memset(&fl6, 0, sizeof(fl6));
Thomas Grafab364a62006-08-22 00:01:47 -07002474
2475 if (tb[RTA_SRC]) {
2476 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2477 goto errout;
2478
David S. Miller4c9483b2011-03-12 16:22:43 -05002479 ipv6_addr_copy(&fl6.saddr, nla_data(tb[RTA_SRC]));
Thomas Grafab364a62006-08-22 00:01:47 -07002480 }
2481
2482 if (tb[RTA_DST]) {
2483 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2484 goto errout;
2485
David S. Miller4c9483b2011-03-12 16:22:43 -05002486 ipv6_addr_copy(&fl6.daddr, nla_data(tb[RTA_DST]));
Thomas Grafab364a62006-08-22 00:01:47 -07002487 }
2488
2489 if (tb[RTA_IIF])
2490 iif = nla_get_u32(tb[RTA_IIF]);
2491
2492 if (tb[RTA_OIF])
David S. Miller4c9483b2011-03-12 16:22:43 -05002493 fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07002494
2495 if (iif) {
2496 struct net_device *dev;
Daniel Lezcano55786892008-03-04 13:47:47 -08002497 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002498 if (!dev) {
2499 err = -ENODEV;
2500 goto errout;
2501 }
2502 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503
2504 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
Thomas Grafab364a62006-08-22 00:01:47 -07002505 if (skb == NULL) {
2506 err = -ENOBUFS;
2507 goto errout;
2508 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509
2510 /* Reserve room for dummy headers, this skb can pass
2511 through good chunk of routing engine.
2512 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002513 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2515
David S. Miller4c9483b2011-03-12 16:22:43 -05002516 rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6);
Changli Gaod8d1f302010-06-10 23:31:35 -07002517 skb_dst_set(skb, &rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518
David S. Miller4c9483b2011-03-12 16:22:43 -05002519 err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002521 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002523 kfree_skb(skb);
2524 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 }
2526
Daniel Lezcano55786892008-03-04 13:47:47 -08002527 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002528errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530}
2531
Thomas Graf86872cb2006-08-22 00:01:08 -07002532void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533{
2534 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002535 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002536 u32 seq;
2537 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002539 err = -ENOBUFS;
2540 seq = info->nlh != NULL ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002541
Thomas Graf339bf982006-11-10 14:10:15 -08002542 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
Thomas Graf21713eb2006-08-15 00:35:24 -07002543 if (skb == NULL)
2544 goto errout;
2545
Brian Haley191cd582008-08-14 15:33:21 -07002546 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002547 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002548 if (err < 0) {
2549 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2550 WARN_ON(err == -EMSGSIZE);
2551 kfree_skb(skb);
2552 goto errout;
2553 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08002554 rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2555 info->nlh, gfp_any());
2556 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07002557errout:
2558 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002559 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560}
2561
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002562static int ip6_route_dev_notify(struct notifier_block *this,
2563 unsigned long event, void *data)
2564{
2565 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002566 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002567
2568 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
Changli Gaod8d1f302010-06-10 23:31:35 -07002569 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002570 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2571#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002572 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002573 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002574 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002575 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2576#endif
2577 }
2578
2579 return NOTIFY_OK;
2580}
2581
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582/*
2583 * /proc
2584 */
2585
2586#ifdef CONFIG_PROC_FS
2587
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588struct rt6_proc_arg
2589{
2590 char *buffer;
2591 int offset;
2592 int length;
2593 int skip;
2594 int len;
2595};
2596
2597static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2598{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002599 struct seq_file *m = p_arg;
Eric Dumazet8a533662012-02-09 16:13:19 -05002600 struct neighbour *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002602 seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603
2604#ifdef CONFIG_IPV6_SUBTREES
Harvey Harrison4b7a4272008-10-29 12:50:24 -07002605 seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002607 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608#endif
Eric Dumazet8a533662012-02-09 16:13:19 -05002609 rcu_read_lock();
2610 n = dst_get_neighbour(&rt->dst);
2611 if (n) {
2612 seq_printf(m, "%pi6", n->primary_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002614 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 }
Eric Dumazet8a533662012-02-09 16:13:19 -05002616 rcu_read_unlock();
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002617 seq_printf(m, " %08x %08x %08x %08x %8s\n",
Changli Gaod8d1f302010-06-10 23:31:35 -07002618 rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
2619 rt->dst.__use, rt->rt6i_flags,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002620 rt->rt6i_dev ? rt->rt6i_dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 return 0;
2622}
2623
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002624static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002626 struct net *net = (struct net *)m->private;
2627 fib6_clean_all(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002628 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629}
2630
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002631static int ipv6_route_open(struct inode *inode, struct file *file)
2632{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002633 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002634}
2635
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002636static const struct file_operations ipv6_route_proc_fops = {
2637 .owner = THIS_MODULE,
2638 .open = ipv6_route_open,
2639 .read = seq_read,
2640 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002641 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002642};
2643
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2645{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002646 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002648 net->ipv6.rt6_stats->fib_nodes,
2649 net->ipv6.rt6_stats->fib_route_nodes,
2650 net->ipv6.rt6_stats->fib_rt_alloc,
2651 net->ipv6.rt6_stats->fib_rt_entries,
2652 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00002653 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002654 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655
2656 return 0;
2657}
2658
2659static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2660{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002661 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002662}
2663
Arjan van de Ven9a321442007-02-12 00:55:35 -08002664static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 .owner = THIS_MODULE,
2666 .open = rt6_stats_seq_open,
2667 .read = seq_read,
2668 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002669 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670};
2671#endif /* CONFIG_PROC_FS */
2672
2673#ifdef CONFIG_SYSCTL
2674
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675static
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07002676int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 void __user *buffer, size_t *lenp, loff_t *ppos)
2678{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002679 struct net *net;
2680 int delay;
2681 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002683
2684 net = (struct net *)ctl->extra1;
2685 delay = net->ipv6.sysctl.flush_delay;
2686 proc_dointvec(ctl, write, buffer, lenp, ppos);
2687 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2688 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689}
2690
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002691ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002692 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002694 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 .maxlen = sizeof(int),
Dave Jones89c8b3a2005-04-28 12:11:49 -07002696 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002697 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 },
2699 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002701 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 .maxlen = sizeof(int),
2703 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002704 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 },
2706 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002708 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709 .maxlen = sizeof(int),
2710 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002711 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 },
2713 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002715 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716 .maxlen = sizeof(int),
2717 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002718 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719 },
2720 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002722 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 .maxlen = sizeof(int),
2724 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002725 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726 },
2727 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002729 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 .maxlen = sizeof(int),
2731 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002732 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733 },
2734 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002736 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 .maxlen = sizeof(int),
2738 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002739 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740 },
2741 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002743 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744 .maxlen = sizeof(int),
2745 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002746 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 },
2748 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002750 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 .maxlen = sizeof(int),
2752 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07002753 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 },
2755 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002757 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 .maxlen = sizeof(int),
2759 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08002760 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08002762 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763};
2764
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002765struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002766{
2767 struct ctl_table *table;
2768
2769 table = kmemdup(ipv6_route_table_template,
2770 sizeof(ipv6_route_table_template),
2771 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002772
2773 if (table) {
2774 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00002775 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002776 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002777 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2778 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2779 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2780 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2781 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2782 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2783 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08002784 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002785 }
2786
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002787 return table;
2788}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789#endif
2790
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002791static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002792{
Pavel Emelyanov633d4242008-04-21 14:25:23 -07002793 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002794
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002795 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
2796 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002797
Eric Dumazetfc66f952010-10-08 06:37:34 +00002798 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
2799 goto out_ip6_dst_ops;
2800
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002801 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2802 sizeof(*net->ipv6.ip6_null_entry),
2803 GFP_KERNEL);
2804 if (!net->ipv6.ip6_null_entry)
Eric Dumazetfc66f952010-10-08 06:37:34 +00002805 goto out_ip6_dst_entries;
Changli Gaod8d1f302010-06-10 23:31:35 -07002806 net->ipv6.ip6_null_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002807 (struct dst_entry *)net->ipv6.ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002808 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002809 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
2810 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002811
2812#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2813 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2814 sizeof(*net->ipv6.ip6_prohibit_entry),
2815 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002816 if (!net->ipv6.ip6_prohibit_entry)
2817 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002818 net->ipv6.ip6_prohibit_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002819 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002820 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002821 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
2822 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002823
2824 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2825 sizeof(*net->ipv6.ip6_blk_hole_entry),
2826 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002827 if (!net->ipv6.ip6_blk_hole_entry)
2828 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002829 net->ipv6.ip6_blk_hole_entry->dst.path =
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002830 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07002831 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08002832 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
2833 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002834#endif
2835
Peter Zijlstrab339a472008-10-07 14:15:00 -07002836 net->ipv6.sysctl.flush_delay = 0;
2837 net->ipv6.sysctl.ip6_rt_max_size = 4096;
2838 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
2839 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
2840 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
2841 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
2842 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
2843 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
2844
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002845#ifdef CONFIG_PROC_FS
2846 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2847 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2848#endif
Benjamin Thery6891a342008-03-04 13:49:47 -08002849 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2850
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002851 ret = 0;
2852out:
2853 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002854
Peter Zijlstra68fffc62008-10-07 14:12:10 -07002855#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2856out_ip6_prohibit_entry:
2857 kfree(net->ipv6.ip6_prohibit_entry);
2858out_ip6_null_entry:
2859 kfree(net->ipv6.ip6_null_entry);
2860#endif
Eric Dumazetfc66f952010-10-08 06:37:34 +00002861out_ip6_dst_entries:
2862 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002863out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002864 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002865}
2866
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00002867static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002868{
2869#ifdef CONFIG_PROC_FS
2870 proc_net_remove(net, "ipv6_route");
2871 proc_net_remove(net, "rt6_stats");
2872#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002873 kfree(net->ipv6.ip6_null_entry);
2874#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2875 kfree(net->ipv6.ip6_prohibit_entry);
2876 kfree(net->ipv6.ip6_blk_hole_entry);
2877#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002878 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002879}
2880
2881static struct pernet_operations ip6_route_net_ops = {
2882 .init = ip6_route_net_init,
2883 .exit = ip6_route_net_exit,
2884};
2885
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002886static struct notifier_block ip6_route_dev_notifier = {
2887 .notifier_call = ip6_route_dev_notify,
2888 .priority = 0,
2889};
2890
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002891int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002893 int ret;
2894
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002895 ret = -ENOMEM;
2896 ip6_dst_ops_template.kmem_cachep =
2897 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
2898 SLAB_HWCACHE_ALIGN, NULL);
2899 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08002900 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07002901
Eric Dumazetfc66f952010-10-08 06:37:34 +00002902 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002903 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002904 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002905
Eric Dumazetfc66f952010-10-08 06:37:34 +00002906 ret = register_pernet_subsys(&ip6_route_net_ops);
2907 if (ret)
2908 goto out_dst_entries;
2909
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07002910 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
2911
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002912 /* Registering of the loopback is done before this portion of code,
2913 * the loopback reference in rt6_info will not be taken, do it
2914 * manually for init_net */
Changli Gaod8d1f302010-06-10 23:31:35 -07002915 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002916 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2917 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07002918 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002919 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07002920 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002921 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2922 #endif
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002923 ret = fib6_init();
2924 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002925 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002926
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002927 ret = xfrm6_init();
2928 if (ret)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002929 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08002930
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002931 ret = fib6_rules_init();
2932 if (ret)
2933 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08002934
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002935 ret = -ENOBUFS;
2936 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL) ||
2937 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL) ||
2938 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL))
2939 goto fib6_rules_init;
2940
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002941 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002942 if (ret)
2943 goto fib6_rules_init;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002944
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002945out:
2946 return ret;
2947
2948fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002949 fib6_rules_cleanup();
2950xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002951 xfrm6_fini();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002952out_fib6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002953 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002954out_register_subsys:
2955 unregister_pernet_subsys(&ip6_route_net_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002956out_dst_entries:
2957 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002958out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002959 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002960 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961}
2962
2963void ip6_route_cleanup(void)
2964{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002965 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Graf101367c2006-08-04 03:39:02 -07002966 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002969 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00002970 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002971 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972}