blob: dbad96c58baa0dd0a81497f1c554aa2eaff91956 [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>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020043#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <net/snmp.h>
45#include <net/ipv6.h>
46#include <net/ip6_fib.h>
47#include <net/ip6_route.h>
48#include <net/ndisc.h>
49#include <net/addrconf.h>
50#include <net/tcp.h>
51#include <linux/rtnetlink.h>
52#include <net/dst.h>
53#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070054#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070055#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57#include <asm/uaccess.h>
58
59#ifdef CONFIG_SYSCTL
60#include <linux/sysctl.h>
61#endif
62
63/* Set to 3 to get tracing. */
64#define RT6_DEBUG 2
65
66#if RT6_DEBUG >= 3
67#define RDBG(x) printk x
68#define RT6_TRACE(x...) printk(KERN_DEBUG x)
69#else
70#define RDBG(x)
71#define RT6_TRACE(x...) do { ; } while (0)
72#endif
73
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -080074#define CLONE_OFFLINK_ROUTE 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
77static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
78static struct dst_entry *ip6_negative_advice(struct dst_entry *);
79static void ip6_dst_destroy(struct dst_entry *);
80static void ip6_dst_ifdown(struct dst_entry *,
81 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080082static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
84static int ip6_pkt_discard(struct sk_buff *skb);
85static int ip6_pkt_discard_out(struct sk_buff *skb);
86static void ip6_link_failure(struct sk_buff *skb);
87static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
88
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080089#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080090static struct rt6_info *rt6_add_route_info(struct net *net,
91 struct in6_addr *prefix, int prefixlen,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080092 struct in6_addr *gwaddr, int ifindex,
93 unsigned pref);
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -080094static struct rt6_info *rt6_get_route_info(struct net *net,
95 struct in6_addr *prefix, int prefixlen,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -080096 struct in6_addr *gwaddr, int ifindex);
97#endif
98
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -080099static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 .family = AF_INET6,
101 .protocol = __constant_htons(ETH_P_IPV6),
102 .gc = ip6_dst_gc,
103 .gc_thresh = 1024,
104 .check = ip6_dst_check,
105 .destroy = ip6_dst_destroy,
106 .ifdown = ip6_dst_ifdown,
107 .negative_advice = ip6_negative_advice,
108 .link_failure = ip6_link_failure,
109 .update_pmtu = ip6_rt_update_pmtu,
Herbert Xu1ac06e02008-05-20 14:32:14 -0700110 .local_out = __ip6_local_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 .entry_size = sizeof(struct rt6_info),
Eric Dumazete2422972008-01-30 20:07:45 -0800112 .entries = ATOMIC_INIT(0),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113};
114
David S. Miller14e50e52007-05-24 18:17:54 -0700115static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
116{
117}
118
119static struct dst_ops ip6_dst_blackhole_ops = {
120 .family = AF_INET6,
121 .protocol = __constant_htons(ETH_P_IPV6),
122 .destroy = ip6_dst_destroy,
123 .check = ip6_dst_check,
124 .update_pmtu = ip6_rt_blackhole_update_pmtu,
125 .entry_size = sizeof(struct rt6_info),
Eric Dumazete2422972008-01-30 20:07:45 -0800126 .entries = ATOMIC_INIT(0),
David S. Miller14e50e52007-05-24 18:17:54 -0700127};
128
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800129static struct rt6_info ip6_null_entry_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 .u = {
131 .dst = {
132 .__refcnt = ATOMIC_INIT(1),
133 .__use = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 .obsolete = -1,
135 .error = -ENETUNREACH,
136 .metrics = { [RTAX_HOPLIMIT - 1] = 255, },
137 .input = ip6_pkt_discard,
138 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 }
140 },
141 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
142 .rt6i_metric = ~(u32) 0,
143 .rt6i_ref = ATOMIC_INIT(1),
144};
145
Thomas Graf101367c2006-08-04 03:39:02 -0700146#ifdef CONFIG_IPV6_MULTIPLE_TABLES
147
David S. Miller6723ab52006-10-18 21:20:57 -0700148static int ip6_pkt_prohibit(struct sk_buff *skb);
149static int ip6_pkt_prohibit_out(struct sk_buff *skb);
David S. Miller6723ab52006-10-18 21:20:57 -0700150
Adrian Bunk280a34c2008-04-21 02:29:32 -0700151static struct rt6_info ip6_prohibit_entry_template = {
Thomas Graf101367c2006-08-04 03:39:02 -0700152 .u = {
153 .dst = {
154 .__refcnt = ATOMIC_INIT(1),
155 .__use = 1,
Thomas Graf101367c2006-08-04 03:39:02 -0700156 .obsolete = -1,
157 .error = -EACCES,
158 .metrics = { [RTAX_HOPLIMIT - 1] = 255, },
Thomas Graf9ce8ade2006-10-18 20:46:54 -0700159 .input = ip6_pkt_prohibit,
160 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700161 }
162 },
163 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
164 .rt6i_metric = ~(u32) 0,
165 .rt6i_ref = ATOMIC_INIT(1),
166};
167
Daniel Lezcanobdb32892008-03-04 13:48:10 -0800168static struct rt6_info ip6_blk_hole_entry_template = {
Thomas Graf101367c2006-08-04 03:39:02 -0700169 .u = {
170 .dst = {
171 .__refcnt = ATOMIC_INIT(1),
172 .__use = 1,
Thomas Graf101367c2006-08-04 03:39:02 -0700173 .obsolete = -1,
174 .error = -EINVAL,
175 .metrics = { [RTAX_HOPLIMIT - 1] = 255, },
Herbert Xu352e5122007-11-13 21:34:06 -0800176 .input = dst_discard,
177 .output = dst_discard,
Thomas Graf101367c2006-08-04 03:39:02 -0700178 }
179 },
180 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
181 .rt6i_metric = ~(u32) 0,
182 .rt6i_ref = ATOMIC_INIT(1),
183};
184
185#endif
186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187/* allocate dst with ip6_dst_ops */
Benjamin Theryf2fc6a52008-03-04 13:49:23 -0800188static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
Benjamin Theryf2fc6a52008-03-04 13:49:23 -0800190 return (struct rt6_info *)dst_alloc(ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191}
192
193static void ip6_dst_destroy(struct dst_entry *dst)
194{
195 struct rt6_info *rt = (struct rt6_info *)dst;
196 struct inet6_dev *idev = rt->rt6i_idev;
197
198 if (idev != NULL) {
199 rt->rt6i_idev = NULL;
200 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900201 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202}
203
204static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
205 int how)
206{
207 struct rt6_info *rt = (struct rt6_info *)dst;
208 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800209 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900210 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800212 if (dev != loopback_dev && idev != NULL && idev->dev == dev) {
213 struct inet6_dev *loopback_idev =
214 in6_dev_get(loopback_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 if (loopback_idev != NULL) {
216 rt->rt6i_idev = loopback_idev;
217 in6_dev_put(idev);
218 }
219 }
220}
221
222static __inline__ int rt6_check_expired(const struct rt6_info *rt)
223{
224 return (rt->rt6i_flags & RTF_EXPIRES &&
225 time_after(jiffies, rt->rt6i_expires));
226}
227
Thomas Grafc71099a2006-08-04 23:20:06 -0700228static inline int rt6_need_strict(struct in6_addr *daddr)
229{
230 return (ipv6_addr_type(daddr) &
YOSHIFUJI Hideaki5ce83af2008-06-25 16:58:17 +0900231 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK));
Thomas Grafc71099a2006-08-04 23:20:06 -0700232}
233
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234/*
Thomas Grafc71099a2006-08-04 23:20:06 -0700235 * Route lookup. Any table->tb6_lock is implied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 */
237
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800238static inline struct rt6_info *rt6_device_match(struct net *net,
239 struct rt6_info *rt,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700241 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 struct rt6_info *local = NULL;
244 struct rt6_info *sprt;
245
246 if (oif) {
Eric Dumazet7cc48262007-02-09 16:22:57 -0800247 for (sprt = rt; sprt; sprt = sprt->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 struct net_device *dev = sprt->rt6i_dev;
249 if (dev->ifindex == oif)
250 return sprt;
251 if (dev->flags & IFF_LOOPBACK) {
252 if (sprt->rt6i_idev == NULL ||
253 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700254 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900256 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 local->rt6i_idev->dev->ifindex == oif))
258 continue;
259 }
260 local = sprt;
261 }
262 }
263
264 if (local)
265 return local;
266
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700267 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800268 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 }
270 return rt;
271}
272
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800273#ifdef CONFIG_IPV6_ROUTER_PREF
274static void rt6_probe(struct rt6_info *rt)
275{
276 struct neighbour *neigh = rt ? rt->rt6i_nexthop : NULL;
277 /*
278 * Okay, this does not seem to be appropriate
279 * for now, however, we need to check if it
280 * is really so; aka Router Reachability Probing.
281 *
282 * Router Reachability Probe MUST be rate-limited
283 * to no more than one per minute.
284 */
285 if (!neigh || (neigh->nud_state & NUD_VALID))
286 return;
287 read_lock_bh(&neigh->lock);
288 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800289 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800290 struct in6_addr mcaddr;
291 struct in6_addr *target;
292
293 neigh->updated = jiffies;
294 read_unlock_bh(&neigh->lock);
295
296 target = (struct in6_addr *)&neigh->primary_key;
297 addrconf_addr_solict_mult(target, &mcaddr);
298 ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
299 } else
300 read_unlock_bh(&neigh->lock);
301}
302#else
303static inline void rt6_probe(struct rt6_info *rt)
304{
305 return;
306}
307#endif
308
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800310 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700312static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800314 struct net_device *dev = rt->rt6i_dev;
David S. Miller161980f2007-04-06 11:42:27 -0700315 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800316 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700317 if ((dev->flags & IFF_LOOPBACK) &&
318 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
319 return 1;
320 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321}
322
Dave Jonesb6f99a22007-03-22 12:27:49 -0700323static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324{
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800325 struct neighbour *neigh = rt->rt6i_nexthop;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800326 int m;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700327 if (rt->rt6i_flags & RTF_NONEXTHOP ||
328 !(rt->rt6i_flags & RTF_GATEWAY))
329 m = 1;
330 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800331 read_lock_bh(&neigh->lock);
332 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700333 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800334#ifdef CONFIG_IPV6_ROUTER_PREF
335 else if (neigh->nud_state & NUD_FAILED)
336 m = 0;
337#endif
338 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800339 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800340 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800341 } else
342 m = 0;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800343 return m;
344}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800346static int rt6_score_route(struct rt6_info *rt, int oif,
347 int strict)
348{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700349 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900350
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700351 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700352 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800353 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800354#ifdef CONFIG_IPV6_ROUTER_PREF
355 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
356#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700357 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800358 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800359 return -1;
360 return m;
361}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
David S. Millerf11e6652007-03-24 20:36:25 -0700363static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
364 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800365{
David S. Millerf11e6652007-03-24 20:36:25 -0700366 int m;
367
368 if (rt6_check_expired(rt))
369 goto out;
370
371 m = rt6_score_route(rt, oif, strict);
372 if (m < 0)
373 goto out;
374
375 if (m > *mpri) {
376 if (strict & RT6_LOOKUP_F_REACHABLE)
377 rt6_probe(match);
378 *mpri = m;
379 match = rt;
380 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
381 rt6_probe(rt);
382 }
383
384out:
385 return match;
386}
387
388static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
389 struct rt6_info *rr_head,
390 u32 metric, int oif, int strict)
391{
392 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800393 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
David S. Millerf11e6652007-03-24 20:36:25 -0700395 match = NULL;
396 for (rt = rr_head; rt && rt->rt6i_metric == metric;
397 rt = rt->u.dst.rt6_next)
398 match = find_match(rt, oif, strict, &mpri, match);
399 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
400 rt = rt->u.dst.rt6_next)
401 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800402
David S. Millerf11e6652007-03-24 20:36:25 -0700403 return match;
404}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800405
David S. Millerf11e6652007-03-24 20:36:25 -0700406static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
407{
408 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800409 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
David S. Millerf11e6652007-03-24 20:36:25 -0700411 RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800412 __func__, fn->leaf, oif);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
David S. Millerf11e6652007-03-24 20:36:25 -0700414 rt0 = fn->rr_ptr;
415 if (!rt0)
416 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
David S. Millerf11e6652007-03-24 20:36:25 -0700418 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800420 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700421 (strict & RT6_LOOKUP_F_REACHABLE)) {
422 struct rt6_info *next = rt0->u.dst.rt6_next;
423
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800424 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700425 if (!next || next->rt6i_metric != rt0->rt6i_metric)
426 next = fn->leaf;
427
428 if (next != rt0)
429 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 }
431
David S. Millerf11e6652007-03-24 20:36:25 -0700432 RT6_TRACE("%s() => %p\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800433 __func__, match);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900435 net = dev_net(rt0->rt6i_dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800436 return (match ? match : net->ipv6.ip6_null_entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437}
438
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800439#ifdef CONFIG_IPV6_ROUTE_INFO
440int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
441 struct in6_addr *gwaddr)
442{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900443 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800444 struct route_info *rinfo = (struct route_info *) opt;
445 struct in6_addr prefix_buf, *prefix;
446 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900447 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800448 struct rt6_info *rt;
449
450 if (len < sizeof(struct route_info)) {
451 return -EINVAL;
452 }
453
454 /* Sanity check for prefix_len and length */
455 if (rinfo->length > 3) {
456 return -EINVAL;
457 } else if (rinfo->prefix_len > 128) {
458 return -EINVAL;
459 } else if (rinfo->prefix_len > 64) {
460 if (rinfo->length < 2) {
461 return -EINVAL;
462 }
463 } else if (rinfo->prefix_len > 0) {
464 if (rinfo->length < 1) {
465 return -EINVAL;
466 }
467 }
468
469 pref = rinfo->route_pref;
470 if (pref == ICMPV6_ROUTER_PREF_INVALID)
471 pref = ICMPV6_ROUTER_PREF_MEDIUM;
472
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900473 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800474
475 if (rinfo->length == 3)
476 prefix = (struct in6_addr *)rinfo->prefix;
477 else {
478 /* this function is safe */
479 ipv6_addr_prefix(&prefix_buf,
480 (struct in6_addr *)rinfo->prefix,
481 rinfo->prefix_len);
482 prefix = &prefix_buf;
483 }
484
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800485 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
486 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800487
488 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700489 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800490 rt = NULL;
491 }
492
493 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800494 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800495 pref);
496 else if (rt)
497 rt->rt6i_flags = RTF_ROUTEINFO |
498 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
499
500 if (rt) {
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900501 if (!addrconf_finite_timeout(lifetime)) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800502 rt->rt6i_flags &= ~RTF_EXPIRES;
503 } else {
504 rt->rt6i_expires = jiffies + HZ * lifetime;
505 rt->rt6i_flags |= RTF_EXPIRES;
506 }
507 dst_release(&rt->u.dst);
508 }
509 return 0;
510}
511#endif
512
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800513#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700514do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800515 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700516 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700517 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700518 if (fn->fn_flags & RTN_TL_ROOT) \
519 goto out; \
520 pn = fn->parent; \
521 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800522 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700523 else \
524 fn = pn; \
525 if (fn->fn_flags & RTN_RTINFO) \
526 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700527 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700528 } \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700529} while(0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700530
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800531static struct rt6_info *ip6_pol_route_lookup(struct net *net,
532 struct fib6_table *table,
Thomas Grafc71099a2006-08-04 23:20:06 -0700533 struct flowi *fl, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
535 struct fib6_node *fn;
536 struct rt6_info *rt;
537
Thomas Grafc71099a2006-08-04 23:20:06 -0700538 read_lock_bh(&table->tb6_lock);
539 fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
540restart:
541 rt = fn->leaf;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800542 rt = rt6_device_match(net, rt, fl->oif, flags);
543 BACKTRACK(net, &fl->fl6_src);
Thomas Grafc71099a2006-08-04 23:20:06 -0700544out:
Pavel Emelyanov03f49f32007-11-10 21:28:34 -0800545 dst_use(&rt->u.dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700546 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700547 return rt;
548
549}
550
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900551struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
552 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700553{
554 struct flowi fl = {
555 .oif = oif,
556 .nl_u = {
557 .ip6_u = {
558 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700559 },
560 },
561 };
562 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700563 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700564
Thomas Grafadaa70b2006-10-13 15:01:03 -0700565 if (saddr) {
566 memcpy(&fl.fl6_src, saddr, sizeof(*saddr));
567 flags |= RT6_LOOKUP_F_HAS_SADDR;
568 }
569
Daniel Lezcano606a2b42008-03-04 13:45:59 -0800570 dst = fib6_rule_lookup(net, &fl, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700571 if (dst->error == 0)
572 return (struct rt6_info *) dst;
573
574 dst_release(dst);
575
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 return NULL;
577}
578
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900579EXPORT_SYMBOL(rt6_lookup);
580
Thomas Grafc71099a2006-08-04 23:20:06 -0700581/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 It takes new route entry, the addition fails by any reason the
583 route is freed. In any case, if caller does not hold it, it may
584 be destroyed.
585 */
586
Thomas Graf86872cb2006-08-22 00:01:08 -0700587static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588{
589 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700590 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Thomas Grafc71099a2006-08-04 23:20:06 -0700592 table = rt->rt6i_table;
593 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700594 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700595 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
597 return err;
598}
599
Thomas Graf40e22e82006-08-22 00:00:45 -0700600int ip6_ins_rt(struct rt6_info *rt)
601{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800602 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900603 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800604 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800605 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700606}
607
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800608static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *daddr,
609 struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 struct rt6_info *rt;
612
613 /*
614 * Clone the route.
615 */
616
617 rt = ip6_rt_copy(ort);
618
619 if (rt) {
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900620 if (!(rt->rt6i_flags&RTF_GATEWAY)) {
621 if (rt->rt6i_dst.plen != 128 &&
622 ipv6_addr_equal(&rt->rt6i_dst.addr, daddr))
623 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 ipv6_addr_copy(&rt->rt6i_gateway, daddr);
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900625 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900627 ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 rt->rt6i_dst.plen = 128;
629 rt->rt6i_flags |= RTF_CACHE;
630 rt->u.dst.flags |= DST_HOST;
631
632#ifdef CONFIG_IPV6_SUBTREES
633 if (rt->rt6i_src.plen && saddr) {
634 ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
635 rt->rt6i_src.plen = 128;
636 }
637#endif
638
639 rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
640
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800641 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800643 return rt;
644}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800646static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *daddr)
647{
648 struct rt6_info *rt = ip6_rt_copy(ort);
649 if (rt) {
650 ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
651 rt->rt6i_dst.plen = 128;
652 rt->rt6i_flags |= RTF_CACHE;
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800653 rt->u.dst.flags |= DST_HOST;
654 rt->rt6i_nexthop = neigh_clone(ort->rt6i_nexthop);
655 }
656 return rt;
657}
658
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800659static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
660 struct flowi *fl, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661{
662 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800663 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700664 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800666 int err;
YOSHIFUJI Hideakiea659e02006-11-06 09:45:45 -0800667 int reachable = ipv6_devconf.forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700669 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
671relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700672 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800674restart_2:
Thomas Grafc71099a2006-08-04 23:20:06 -0700675 fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
677restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700678 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800679
680 BACKTRACK(net, &fl->fl6_src);
681 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800682 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800683 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800685 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700686 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800687
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800688 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP))
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800689 nrt = rt6_alloc_cow(rt, &fl->fl6_dst, &fl->fl6_src);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800690 else {
691#if CLONE_OFFLINK_ROUTE
692 nrt = rt6_alloc_clone(rt, &fl->fl6_dst);
693#else
694 goto out2;
695#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 }
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800697
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800698 dst_release(&rt->u.dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800699 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800700
701 dst_hold(&rt->u.dst);
702 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700703 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800704 if (!err)
705 goto out2;
706 }
707
708 if (--attempts <= 0)
709 goto out2;
710
711 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700712 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800713 * released someone could insert this route. Relookup.
714 */
715 dst_release(&rt->u.dst);
716 goto relookup;
717
718out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800719 if (reachable) {
720 reachable = 0;
721 goto restart_2;
722 }
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800723 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700724 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725out2:
726 rt->u.dst.lastuse = jiffies;
727 rt->u.dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700728
729 return rt;
730}
731
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800732static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700733 struct flowi *fl, int flags)
734{
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800735 return ip6_pol_route(net, table, fl->iif, fl, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700736}
737
Thomas Grafc71099a2006-08-04 23:20:06 -0700738void ip6_route_input(struct sk_buff *skb)
739{
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700740 struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900741 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700742 int flags = RT6_LOOKUP_F_HAS_SADDR;
Thomas Grafc71099a2006-08-04 23:20:06 -0700743 struct flowi fl = {
744 .iif = skb->dev->ifindex,
745 .nl_u = {
746 .ip6_u = {
747 .daddr = iph->daddr,
748 .saddr = iph->saddr,
Al Viro90bcaf72006-11-08 00:25:17 -0800749 .flowlabel = (* (__be32 *) iph)&IPV6_FLOWINFO_MASK,
Thomas Grafc71099a2006-08-04 23:20:06 -0700750 },
751 },
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900752 .mark = skb->mark,
Thomas Grafc71099a2006-08-04 23:20:06 -0700753 .proto = iph->nexthdr,
754 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700755
756 if (rt6_need_strict(&iph->daddr))
757 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700758
Daniel Lezcano55786892008-03-04 13:47:47 -0800759 skb->dst = fib6_rule_lookup(net, &fl, flags, ip6_pol_route_input);
Thomas Grafc71099a2006-08-04 23:20:06 -0700760}
761
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800762static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
Thomas Grafc71099a2006-08-04 23:20:06 -0700763 struct flowi *fl, int flags)
764{
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800765 return ip6_pol_route(net, table, fl->oif, fl, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700766}
767
Daniel Lezcano4591db42008-03-05 10:48:10 -0800768struct dst_entry * ip6_route_output(struct net *net, struct sock *sk,
769 struct flowi *fl)
Thomas Grafc71099a2006-08-04 23:20:06 -0700770{
771 int flags = 0;
772
773 if (rt6_need_strict(&fl->fl6_dst))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700774 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700775
Thomas Grafadaa70b2006-10-13 15:01:03 -0700776 if (!ipv6_addr_any(&fl->fl6_src))
777 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900778 else if (sk) {
779 unsigned int prefs = inet6_sk(sk)->srcprefs;
780 if (prefs & IPV6_PREFER_SRC_TMP)
781 flags |= RT6_LOOKUP_F_SRCPREF_TMP;
782 if (prefs & IPV6_PREFER_SRC_PUBLIC)
783 flags |= RT6_LOOKUP_F_SRCPREF_PUBLIC;
784 if (prefs & IPV6_PREFER_SRC_COA)
785 flags |= RT6_LOOKUP_F_SRCPREF_COA;
786 }
Thomas Grafadaa70b2006-10-13 15:01:03 -0700787
Daniel Lezcano4591db42008-03-05 10:48:10 -0800788 return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789}
790
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900791EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
David S. Miller14e50e52007-05-24 18:17:54 -0700793int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl)
794{
795 struct rt6_info *ort = (struct rt6_info *) *dstp;
796 struct rt6_info *rt = (struct rt6_info *)
797 dst_alloc(&ip6_dst_blackhole_ops);
798 struct dst_entry *new = NULL;
799
800 if (rt) {
801 new = &rt->u.dst;
802
803 atomic_set(&new->__refcnt, 1);
804 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800805 new->input = dst_discard;
806 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700807
808 memcpy(new->metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
809 new->dev = ort->u.dst.dev;
810 if (new->dev)
811 dev_hold(new->dev);
812 rt->rt6i_idev = ort->rt6i_idev;
813 if (rt->rt6i_idev)
814 in6_dev_hold(rt->rt6i_idev);
815 rt->rt6i_expires = 0;
816
817 ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
818 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
819 rt->rt6i_metric = 0;
820
821 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
822#ifdef CONFIG_IPV6_SUBTREES
823 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
824#endif
825
826 dst_free(new);
827 }
828
829 dst_release(*dstp);
830 *dstp = new;
831 return (new ? 0 : -ENOMEM);
832}
833EXPORT_SYMBOL_GPL(ip6_dst_blackhole);
834
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835/*
836 * Destination cache support functions
837 */
838
839static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
840{
841 struct rt6_info *rt;
842
843 rt = (struct rt6_info *) dst;
844
845 if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
846 return dst;
847
848 return NULL;
849}
850
851static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
852{
853 struct rt6_info *rt = (struct rt6_info *) dst;
854
855 if (rt) {
856 if (rt->rt6i_flags & RTF_CACHE)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700857 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 else
859 dst_release(dst);
860 }
861 return NULL;
862}
863
864static void ip6_link_failure(struct sk_buff *skb)
865{
866 struct rt6_info *rt;
867
868 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
869
870 rt = (struct rt6_info *) skb->dst;
871 if (rt) {
872 if (rt->rt6i_flags&RTF_CACHE) {
873 dst_set_expires(&rt->u.dst, 0);
874 rt->rt6i_flags |= RTF_EXPIRES;
875 } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
876 rt->rt6i_node->fn_sernum = -1;
877 }
878}
879
880static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
881{
882 struct rt6_info *rt6 = (struct rt6_info*)dst;
883
884 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
885 rt6->rt6i_flags |= RTF_MODIFIED;
886 if (mtu < IPV6_MIN_MTU) {
887 mtu = IPV6_MIN_MTU;
888 dst->metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
889 }
890 dst->metrics[RTAX_MTU-1] = mtu;
Tom Tucker8d717402006-07-30 20:43:36 -0700891 call_netevent_notifiers(NETEVENT_PMTU_UPDATE, dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 }
893}
894
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895static int ipv6_get_mtu(struct net_device *dev);
896
Daniel Lezcano55786892008-03-04 13:47:47 -0800897static inline unsigned int ipv6_advmss(struct net *net, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898{
899 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
900
Daniel Lezcano55786892008-03-04 13:47:47 -0800901 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
902 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
904 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900905 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
906 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
907 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 * rely only on pmtu discovery"
909 */
910 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
911 mtu = IPV6_MAXPLEN;
912 return mtu;
913}
914
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800915static struct dst_entry *icmp6_dst_gc_list;
916static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -0700917
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800918struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900920 const struct in6_addr *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921{
922 struct rt6_info *rt;
923 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900924 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
926 if (unlikely(idev == NULL))
927 return NULL;
928
Benjamin Theryf2fc6a52008-03-04 13:49:23 -0800929 rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 if (unlikely(rt == NULL)) {
931 in6_dev_put(idev);
932 goto out;
933 }
934
935 dev_hold(dev);
936 if (neigh)
937 neigh_hold(neigh);
938 else
939 neigh = ndisc_get_neigh(dev, addr);
940
941 rt->rt6i_dev = dev;
942 rt->rt6i_idev = idev;
943 rt->rt6i_nexthop = neigh;
944 atomic_set(&rt->u.dst.__refcnt, 1);
945 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
946 rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev);
Daniel Lezcano55786892008-03-04 13:47:47 -0800947 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, dst_mtu(&rt->u.dst));
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800948 rt->u.dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
950#if 0 /* there's no chance to use these for ndisc */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900951 rt->u.dst.flags = ipv6_addr_type(addr) & IPV6_ADDR_UNICAST
952 ? DST_HOST
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 : 0;
954 ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
955 rt->rt6i_dst.plen = 128;
956#endif
957
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800958 spin_lock_bh(&icmp6_dst_lock);
959 rt->u.dst.next = icmp6_dst_gc_list;
960 icmp6_dst_gc_list = &rt->u.dst;
961 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962
Daniel Lezcano55786892008-03-04 13:47:47 -0800963 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
965out:
YOSHIFUJI Hideaki40aa7b92006-10-19 13:50:09 +0900966 return &rt->u.dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967}
968
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800969int icmp6_dst_gc(int *more)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970{
971 struct dst_entry *dst, *next, **pprev;
972 int freed;
973
974 next = NULL;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900975 freed = 0;
Thomas Graf5d0bbee2006-08-04 03:37:36 -0700976
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800977 spin_lock_bh(&icmp6_dst_lock);
978 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -0700979
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 while ((dst = *pprev) != NULL) {
981 if (!atomic_read(&dst->__refcnt)) {
982 *pprev = dst->next;
983 dst_free(dst);
984 freed++;
985 } else {
986 pprev = &dst->next;
987 (*more)++;
988 }
989 }
990
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800991 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -0700992
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 return freed;
994}
995
Daniel Lezcano569d3642008-01-18 03:56:57 -0800996static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 unsigned long now = jiffies;
Daniel Lezcano7019b782008-03-04 13:50:14 -0800999 struct net *net = ops->dst_net;
1000 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1001 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1002 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1003 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1004 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Daniel Lezcano7019b782008-03-04 13:50:14 -08001006 if (time_after(rt_last_gc + rt_min_interval, now) &&
1007 atomic_read(&ops->entries) <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 goto out;
1009
Benjamin Thery6891a342008-03-04 13:49:47 -08001010 net->ipv6.ip6_rt_gc_expire++;
1011 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1012 net->ipv6.ip6_rt_last_gc = now;
Daniel Lezcano7019b782008-03-04 13:50:14 -08001013 if (atomic_read(&ops->entries) < ops->gc_thresh)
1014 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001016 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1017 return (atomic_read(&ops->entries) > rt_max_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018}
1019
1020/* Clean host part of a prefix. Not necessary in radix tree,
1021 but results in cleaner routing tables.
1022
1023 Remove it only when all the things will work!
1024 */
1025
1026static int ipv6_get_mtu(struct net_device *dev)
1027{
1028 int mtu = IPV6_MIN_MTU;
1029 struct inet6_dev *idev;
1030
1031 idev = in6_dev_get(dev);
1032 if (idev) {
1033 mtu = idev->cnf.mtu6;
1034 in6_dev_put(idev);
1035 }
1036 return mtu;
1037}
1038
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001039int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040{
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001041 int hoplimit = dst_metric(dst, RTAX_HOPLIMIT);
1042 if (hoplimit < 0) {
1043 struct net_device *dev = dst->dev;
1044 struct inet6_dev *idev = in6_dev_get(dev);
1045 if (idev) {
1046 hoplimit = idev->cnf.hop_limit;
1047 in6_dev_put(idev);
1048 } else
1049 hoplimit = ipv6_devconf.hop_limit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 }
1051 return hoplimit;
1052}
1053
1054/*
1055 *
1056 */
1057
Thomas Graf86872cb2006-08-22 00:01:08 -07001058int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059{
1060 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001061 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 struct rt6_info *rt = NULL;
1063 struct net_device *dev = NULL;
1064 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001065 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 int addr_type;
1067
Thomas Graf86872cb2006-08-22 00:01:08 -07001068 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return -EINVAL;
1070#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001071 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 return -EINVAL;
1073#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001074 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001076 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 if (!dev)
1078 goto out;
1079 idev = in6_dev_get(dev);
1080 if (!idev)
1081 goto out;
1082 }
1083
Thomas Graf86872cb2006-08-22 00:01:08 -07001084 if (cfg->fc_metric == 0)
1085 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
Daniel Lezcano55786892008-03-04 13:47:47 -08001087 table = fib6_new_table(net, cfg->fc_table);
Thomas Grafc71099a2006-08-04 23:20:06 -07001088 if (table == NULL) {
1089 err = -ENOBUFS;
1090 goto out;
1091 }
1092
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08001093 rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094
1095 if (rt == NULL) {
1096 err = -ENOMEM;
1097 goto out;
1098 }
1099
1100 rt->u.dst.obsolete = -1;
YOSHIFUJI Hideaki6f704992008-05-19 16:56:11 -07001101 rt->rt6i_expires = (cfg->fc_flags & RTF_EXPIRES) ?
1102 jiffies + clock_t_to_jiffies(cfg->fc_expires) :
1103 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
Thomas Graf86872cb2006-08-22 00:01:08 -07001105 if (cfg->fc_protocol == RTPROT_UNSPEC)
1106 cfg->fc_protocol = RTPROT_BOOT;
1107 rt->rt6i_protocol = cfg->fc_protocol;
1108
1109 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110
1111 if (addr_type & IPV6_ADDR_MULTICAST)
1112 rt->u.dst.input = ip6_mc_input;
1113 else
1114 rt->u.dst.input = ip6_forward;
1115
1116 rt->u.dst.output = ip6_output;
1117
Thomas Graf86872cb2006-08-22 00:01:08 -07001118 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1119 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 if (rt->rt6i_dst.plen == 128)
1121 rt->u.dst.flags = DST_HOST;
1122
1123#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001124 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1125 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126#endif
1127
Thomas Graf86872cb2006-08-22 00:01:08 -07001128 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129
1130 /* We cannot add true routes via loopback here,
1131 they would result in kernel looping; promote them to reject routes
1132 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001133 if ((cfg->fc_flags & RTF_REJECT) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) {
1135 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001136 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (dev) {
1138 dev_put(dev);
1139 in6_dev_put(idev);
1140 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001141 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 dev_hold(dev);
1143 idev = in6_dev_get(dev);
1144 if (!idev) {
1145 err = -ENODEV;
1146 goto out;
1147 }
1148 }
1149 rt->u.dst.output = ip6_pkt_discard_out;
1150 rt->u.dst.input = ip6_pkt_discard;
1151 rt->u.dst.error = -ENETUNREACH;
1152 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1153 goto install_route;
1154 }
1155
Thomas Graf86872cb2006-08-22 00:01:08 -07001156 if (cfg->fc_flags & RTF_GATEWAY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 struct in6_addr *gw_addr;
1158 int gwa_type;
1159
Thomas Graf86872cb2006-08-22 00:01:08 -07001160 gw_addr = &cfg->fc_gateway;
1161 ipv6_addr_copy(&rt->rt6i_gateway, gw_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 gwa_type = ipv6_addr_type(gw_addr);
1163
1164 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1165 struct rt6_info *grt;
1166
1167 /* IPv6 strictly inhibits using not link-local
1168 addresses as nexthop address.
1169 Otherwise, router will not able to send redirects.
1170 It is very good, but in some (rare!) circumstances
1171 (SIT, PtP, NBMA NOARP links) it is handy to allow
1172 some exceptions. --ANK
1173 */
1174 err = -EINVAL;
1175 if (!(gwa_type&IPV6_ADDR_UNICAST))
1176 goto out;
1177
Daniel Lezcano55786892008-03-04 13:47:47 -08001178 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179
1180 err = -EHOSTUNREACH;
1181 if (grt == NULL)
1182 goto out;
1183 if (dev) {
1184 if (dev != grt->rt6i_dev) {
1185 dst_release(&grt->u.dst);
1186 goto out;
1187 }
1188 } else {
1189 dev = grt->rt6i_dev;
1190 idev = grt->rt6i_idev;
1191 dev_hold(dev);
1192 in6_dev_hold(grt->rt6i_idev);
1193 }
1194 if (!(grt->rt6i_flags&RTF_GATEWAY))
1195 err = 0;
1196 dst_release(&grt->u.dst);
1197
1198 if (err)
1199 goto out;
1200 }
1201 err = -EINVAL;
1202 if (dev == NULL || (dev->flags&IFF_LOOPBACK))
1203 goto out;
1204 }
1205
1206 err = -ENODEV;
1207 if (dev == NULL)
1208 goto out;
1209
Thomas Graf86872cb2006-08-22 00:01:08 -07001210 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);
1212 if (IS_ERR(rt->rt6i_nexthop)) {
1213 err = PTR_ERR(rt->rt6i_nexthop);
1214 rt->rt6i_nexthop = NULL;
1215 goto out;
1216 }
1217 }
1218
Thomas Graf86872cb2006-08-22 00:01:08 -07001219 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220
1221install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001222 if (cfg->fc_mx) {
1223 struct nlattr *nla;
1224 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
Thomas Graf86872cb2006-08-22 00:01:08 -07001226 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001227 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001228
1229 if (type) {
1230 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 err = -EINVAL;
1232 goto out;
1233 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001234
1235 rt->u.dst.metrics[type - 1] = nla_get_u32(nla);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 }
1238 }
1239
Satoru SATOH5ffc02a2008-05-04 22:14:42 -07001240 if (dst_metric(&rt->u.dst, RTAX_HOPLIMIT) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1;
Satoru SATOH5ffc02a2008-05-04 22:14:42 -07001242 if (!dst_metric(&rt->u.dst, RTAX_MTU))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(dev);
Satoru SATOH5ffc02a2008-05-04 22:14:42 -07001244 if (!dst_metric(&rt->u.dst, RTAX_ADVMSS))
Daniel Lezcano55786892008-03-04 13:47:47 -08001245 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, dst_mtu(&rt->u.dst));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 rt->u.dst.dev = dev;
1247 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001248 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001249
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001250 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001251
Thomas Graf86872cb2006-08-22 00:01:08 -07001252 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
1254out:
1255 if (dev)
1256 dev_put(dev);
1257 if (idev)
1258 in6_dev_put(idev);
1259 if (rt)
YOSHIFUJI Hideaki40aa7b92006-10-19 13:50:09 +09001260 dst_free(&rt->u.dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 return err;
1262}
1263
Thomas Graf86872cb2006-08-22 00:01:08 -07001264static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265{
1266 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001267 struct fib6_table *table;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001268 struct net *net = dev_net(rt->rt6i_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001270 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001271 return -ENOENT;
1272
Thomas Grafc71099a2006-08-04 23:20:06 -07001273 table = rt->rt6i_table;
1274 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Thomas Graf86872cb2006-08-22 00:01:08 -07001276 err = fib6_del(rt, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 dst_release(&rt->u.dst);
1278
Thomas Grafc71099a2006-08-04 23:20:06 -07001279 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
1281 return err;
1282}
1283
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001284int ip6_del_rt(struct rt6_info *rt)
1285{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001286 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001287 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001288 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001289 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001290}
1291
Thomas Graf86872cb2006-08-22 00:01:08 -07001292static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293{
Thomas Grafc71099a2006-08-04 23:20:06 -07001294 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 struct fib6_node *fn;
1296 struct rt6_info *rt;
1297 int err = -ESRCH;
1298
Daniel Lezcano55786892008-03-04 13:47:47 -08001299 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
Thomas Grafc71099a2006-08-04 23:20:06 -07001300 if (table == NULL)
1301 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
Thomas Grafc71099a2006-08-04 23:20:06 -07001303 read_lock_bh(&table->tb6_lock);
1304
1305 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001306 &cfg->fc_dst, cfg->fc_dst_len,
1307 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 if (fn) {
Eric Dumazet7cc48262007-02-09 16:22:57 -08001310 for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001311 if (cfg->fc_ifindex &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 (rt->rt6i_dev == NULL ||
Thomas Graf86872cb2006-08-22 00:01:08 -07001313 rt->rt6i_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001315 if (cfg->fc_flags & RTF_GATEWAY &&
1316 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001318 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 continue;
1320 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001321 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
Thomas Graf86872cb2006-08-22 00:01:08 -07001323 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 }
1325 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001326 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
1328 return err;
1329}
1330
1331/*
1332 * Handle redirects
1333 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001334struct ip6rd_flowi {
1335 struct flowi fl;
1336 struct in6_addr gateway;
1337};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001339static struct rt6_info *__ip6_route_redirect(struct net *net,
1340 struct fib6_table *table,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001341 struct flowi *fl,
1342 int flags)
1343{
1344 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl;
1345 struct rt6_info *rt;
1346 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001347
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001349 * Get the "current" route for this destination and
1350 * check if the redirect has come from approriate router.
1351 *
1352 * RFC 2461 specifies that redirects should only be
1353 * accepted if they come from the nexthop to the target.
1354 * Due to the way the routes are chosen, this notion
1355 * is a bit fuzzy and one might need to check all possible
1356 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358
Thomas Grafc71099a2006-08-04 23:20:06 -07001359 read_lock_bh(&table->tb6_lock);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001360 fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001361restart:
Eric Dumazet7cc48262007-02-09 16:22:57 -08001362 for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001363 /*
1364 * Current route is on-link; redirect is always invalid.
1365 *
1366 * Seems, previous statement is not true. It could
1367 * be node, which looks for us as on-link (f.e. proxy ndisc)
1368 * But then router serving it might decide, that we should
1369 * know truth 8)8) --ANK (980726).
1370 */
1371 if (rt6_check_expired(rt))
1372 continue;
1373 if (!(rt->rt6i_flags & RTF_GATEWAY))
1374 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001375 if (fl->oif != rt->rt6i_dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001376 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001377 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001378 continue;
1379 break;
1380 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001381
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001382 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001383 rt = net->ipv6.ip6_null_entry;
1384 BACKTRACK(net, &fl->fl6_src);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001385out:
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001386 dst_hold(&rt->u.dst);
1387
1388 read_unlock_bh(&table->tb6_lock);
1389
1390 return rt;
1391};
1392
1393static struct rt6_info *ip6_route_redirect(struct in6_addr *dest,
1394 struct in6_addr *src,
1395 struct in6_addr *gateway,
1396 struct net_device *dev)
1397{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001398 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001399 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001400 struct ip6rd_flowi rdfl = {
1401 .fl = {
1402 .oif = dev->ifindex,
1403 .nl_u = {
1404 .ip6_u = {
1405 .daddr = *dest,
1406 .saddr = *src,
1407 },
1408 },
1409 },
1410 .gateway = *gateway,
1411 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001412
1413 if (rt6_need_strict(dest))
1414 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001415
Daniel Lezcano55786892008-03-04 13:47:47 -08001416 return (struct rt6_info *)fib6_rule_lookup(net, (struct flowi *)&rdfl,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001417 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001418}
1419
1420void rt6_redirect(struct in6_addr *dest, struct in6_addr *src,
1421 struct in6_addr *saddr,
1422 struct neighbour *neigh, u8 *lladdr, int on_link)
1423{
1424 struct rt6_info *rt, *nrt = NULL;
1425 struct netevent_redirect netevent;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001426 struct net *net = dev_net(neigh->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001427
1428 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1429
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001430 if (rt == net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 if (net_ratelimit())
1432 printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
1433 "for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001434 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 }
1436
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 /*
1438 * We have finally decided to accept it.
1439 */
1440
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001441 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1443 NEIGH_UPDATE_F_OVERRIDE|
1444 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1445 NEIGH_UPDATE_F_ISROUTER))
1446 );
1447
1448 /*
1449 * Redirect received -> path was valid.
1450 * Look, redirects are sent only in response to data packets,
1451 * so that this nexthop apparently is reachable. --ANK
1452 */
1453 dst_confirm(&rt->u.dst);
1454
1455 /* Duplicate redirect: silently ignore. */
1456 if (neigh == rt->u.dst.neighbour)
1457 goto out;
1458
1459 nrt = ip6_rt_copy(rt);
1460 if (nrt == NULL)
1461 goto out;
1462
1463 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1464 if (on_link)
1465 nrt->rt6i_flags &= ~RTF_GATEWAY;
1466
1467 ipv6_addr_copy(&nrt->rt6i_dst.addr, dest);
1468 nrt->rt6i_dst.plen = 128;
1469 nrt->u.dst.flags |= DST_HOST;
1470
1471 ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key);
1472 nrt->rt6i_nexthop = neigh_clone(neigh);
1473 /* Reset pmtu, it may be better */
1474 nrt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(neigh->dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001475 nrt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dev_net(neigh->dev),
Daniel Lezcano55786892008-03-04 13:47:47 -08001476 dst_mtu(&nrt->u.dst));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477
Thomas Graf40e22e82006-08-22 00:00:45 -07001478 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 goto out;
1480
Tom Tucker8d717402006-07-30 20:43:36 -07001481 netevent.old = &rt->u.dst;
1482 netevent.new = &nrt->u.dst;
1483 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1484
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 if (rt->rt6i_flags&RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001486 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 return;
1488 }
1489
1490out:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001491 dst_release(&rt->u.dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 return;
1493}
1494
1495/*
1496 * Handle ICMP "packet too big" messages
1497 * i.e. Path MTU discovery
1498 */
1499
1500void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,
1501 struct net_device *dev, u32 pmtu)
1502{
1503 struct rt6_info *rt, *nrt;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001504 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 int allfrag = 0;
1506
Daniel Lezcano55786892008-03-04 13:47:47 -08001507 rt = rt6_lookup(net, daddr, saddr, dev->ifindex, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 if (rt == NULL)
1509 return;
1510
1511 if (pmtu >= dst_mtu(&rt->u.dst))
1512 goto out;
1513
1514 if (pmtu < IPV6_MIN_MTU) {
1515 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001516 * According to RFC2460, PMTU is set to the IPv6 Minimum Link
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 * MTU (1280) and a fragment header should always be included
1518 * after a node receiving Too Big message reporting PMTU is
1519 * less than the IPv6 Minimum Link MTU.
1520 */
1521 pmtu = IPV6_MIN_MTU;
1522 allfrag = 1;
1523 }
1524
1525 /* New mtu received -> path was valid.
1526 They are sent only in response to data packets,
1527 so that this nexthop apparently is reachable. --ANK
1528 */
1529 dst_confirm(&rt->u.dst);
1530
1531 /* Host route. If it is static, it would be better
1532 not to override it, but add new one, so that
1533 when cache entry will expire old pmtu
1534 would return automatically.
1535 */
1536 if (rt->rt6i_flags & RTF_CACHE) {
1537 rt->u.dst.metrics[RTAX_MTU-1] = pmtu;
1538 if (allfrag)
1539 rt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
Daniel Lezcano55786892008-03-04 13:47:47 -08001540 dst_set_expires(&rt->u.dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
1542 goto out;
1543 }
1544
1545 /* Network route.
1546 Two cases are possible:
1547 1. It is connected route. Action: COW
1548 2. It is gatewayed route or NONEXTHOP route. Action: clone it.
1549 */
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001550 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP))
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001551 nrt = rt6_alloc_cow(rt, daddr, saddr);
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001552 else
1553 nrt = rt6_alloc_clone(rt, daddr);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001554
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001555 if (nrt) {
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001556 nrt->u.dst.metrics[RTAX_MTU-1] = pmtu;
1557 if (allfrag)
1558 nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
1559
1560 /* According to RFC 1981, detecting PMTU increase shouldn't be
1561 * happened within 5 mins, the recommended timer is 10 mins.
1562 * Here this route expiration time is set to ip6_rt_mtu_expires
1563 * which is 10 mins. After 10 mins the decreased pmtu is expired
1564 * and detecting PMTU increase will be automatically happened.
1565 */
Daniel Lezcano55786892008-03-04 13:47:47 -08001566 dst_set_expires(&nrt->u.dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001567 nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
1568
Thomas Graf40e22e82006-08-22 00:00:45 -07001569 ip6_ins_rt(nrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571out:
1572 dst_release(&rt->u.dst);
1573}
1574
1575/*
1576 * Misc support functions
1577 */
1578
1579static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
1580{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001581 struct net *net = dev_net(ort->rt6i_dev);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08001582 struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
1584 if (rt) {
1585 rt->u.dst.input = ort->u.dst.input;
1586 rt->u.dst.output = ort->u.dst.output;
1587
1588 memcpy(rt->u.dst.metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
Ville Nuorvala22e1e4d2006-10-16 22:14:26 -07001589 rt->u.dst.error = ort->u.dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 rt->u.dst.dev = ort->u.dst.dev;
1591 if (rt->u.dst.dev)
1592 dev_hold(rt->u.dst.dev);
1593 rt->rt6i_idev = ort->rt6i_idev;
1594 if (rt->rt6i_idev)
1595 in6_dev_hold(rt->rt6i_idev);
1596 rt->u.dst.lastuse = jiffies;
1597 rt->rt6i_expires = 0;
1598
1599 ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
1600 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
1601 rt->rt6i_metric = 0;
1602
1603 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1604#ifdef CONFIG_IPV6_SUBTREES
1605 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1606#endif
Thomas Grafc71099a2006-08-04 23:20:06 -07001607 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 }
1609 return rt;
1610}
1611
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001612#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001613static struct rt6_info *rt6_get_route_info(struct net *net,
1614 struct in6_addr *prefix, int prefixlen,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001615 struct in6_addr *gwaddr, int ifindex)
1616{
1617 struct fib6_node *fn;
1618 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001619 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001620
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001621 table = fib6_get_table(net, RT6_TABLE_INFO);
Thomas Grafc71099a2006-08-04 23:20:06 -07001622 if (table == NULL)
1623 return NULL;
1624
1625 write_lock_bh(&table->tb6_lock);
1626 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001627 if (!fn)
1628 goto out;
1629
Eric Dumazet7cc48262007-02-09 16:22:57 -08001630 for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001631 if (rt->rt6i_dev->ifindex != ifindex)
1632 continue;
1633 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1634 continue;
1635 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1636 continue;
1637 dst_hold(&rt->u.dst);
1638 break;
1639 }
1640out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001641 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001642 return rt;
1643}
1644
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001645static struct rt6_info *rt6_add_route_info(struct net *net,
1646 struct in6_addr *prefix, int prefixlen,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001647 struct in6_addr *gwaddr, int ifindex,
1648 unsigned pref)
1649{
Thomas Graf86872cb2006-08-22 00:01:08 -07001650 struct fib6_config cfg = {
1651 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001652 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001653 .fc_ifindex = ifindex,
1654 .fc_dst_len = prefixlen,
1655 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1656 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001657 .fc_nlinfo.pid = 0,
1658 .fc_nlinfo.nlh = NULL,
1659 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001660 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001661
Thomas Graf86872cb2006-08-22 00:01:08 -07001662 ipv6_addr_copy(&cfg.fc_dst, prefix);
1663 ipv6_addr_copy(&cfg.fc_gateway, gwaddr);
1664
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001665 /* We should treat it as a default route if prefix length is 0. */
1666 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001667 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001668
Thomas Graf86872cb2006-08-22 00:01:08 -07001669 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001670
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001671 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001672}
1673#endif
1674
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001676{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001678 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001680 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
Thomas Grafc71099a2006-08-04 23:20:06 -07001681 if (table == NULL)
1682 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
Thomas Grafc71099a2006-08-04 23:20:06 -07001684 write_lock_bh(&table->tb6_lock);
Eric Dumazet7cc48262007-02-09 16:22:57 -08001685 for (rt = table->tb6_root.leaf; rt; rt=rt->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 if (dev == rt->rt6i_dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001687 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1689 break;
1690 }
1691 if (rt)
1692 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001693 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 return rt;
1695}
1696
1697struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001698 struct net_device *dev,
1699 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700{
Thomas Graf86872cb2006-08-22 00:01:08 -07001701 struct fib6_config cfg = {
1702 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001703 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001704 .fc_ifindex = dev->ifindex,
1705 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1706 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001707 .fc_nlinfo.pid = 0,
1708 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001709 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001710 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
Thomas Graf86872cb2006-08-22 00:01:08 -07001712 ipv6_addr_copy(&cfg.fc_gateway, gwaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713
Thomas Graf86872cb2006-08-22 00:01:08 -07001714 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 return rt6_get_dflt_router(gwaddr, dev);
1717}
1718
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001719void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720{
1721 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001722 struct fib6_table *table;
1723
1724 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001725 table = fib6_get_table(net, RT6_TABLE_DFLT);
Thomas Grafc71099a2006-08-04 23:20:06 -07001726 if (table == NULL)
1727 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
1729restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001730 read_lock_bh(&table->tb6_lock);
Eric Dumazet7cc48262007-02-09 16:22:57 -08001731 for (rt = table->tb6_root.leaf; rt; rt = rt->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
1733 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001734 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001735 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 goto restart;
1737 }
1738 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001739 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740}
1741
Daniel Lezcano55786892008-03-04 13:47:47 -08001742static void rtmsg_to_fib6_config(struct net *net,
1743 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001744 struct fib6_config *cfg)
1745{
1746 memset(cfg, 0, sizeof(*cfg));
1747
1748 cfg->fc_table = RT6_TABLE_MAIN;
1749 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1750 cfg->fc_metric = rtmsg->rtmsg_metric;
1751 cfg->fc_expires = rtmsg->rtmsg_info;
1752 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1753 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1754 cfg->fc_flags = rtmsg->rtmsg_flags;
1755
Daniel Lezcano55786892008-03-04 13:47:47 -08001756 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001757
Thomas Graf86872cb2006-08-22 00:01:08 -07001758 ipv6_addr_copy(&cfg->fc_dst, &rtmsg->rtmsg_dst);
1759 ipv6_addr_copy(&cfg->fc_src, &rtmsg->rtmsg_src);
1760 ipv6_addr_copy(&cfg->fc_gateway, &rtmsg->rtmsg_gateway);
1761}
1762
Daniel Lezcano55786892008-03-04 13:47:47 -08001763int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764{
Thomas Graf86872cb2006-08-22 00:01:08 -07001765 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 struct in6_rtmsg rtmsg;
1767 int err;
1768
1769 switch(cmd) {
1770 case SIOCADDRT: /* Add a route */
1771 case SIOCDELRT: /* Delete a route */
1772 if (!capable(CAP_NET_ADMIN))
1773 return -EPERM;
1774 err = copy_from_user(&rtmsg, arg,
1775 sizeof(struct in6_rtmsg));
1776 if (err)
1777 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001778
Daniel Lezcano55786892008-03-04 13:47:47 -08001779 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001780
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 rtnl_lock();
1782 switch (cmd) {
1783 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001784 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785 break;
1786 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001787 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 break;
1789 default:
1790 err = -EINVAL;
1791 }
1792 rtnl_unlock();
1793
1794 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001795 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796
1797 return -EINVAL;
1798}
1799
1800/*
1801 * Drop the packet on the floor
1802 */
1803
Ilpo Järvinen50eb4312008-01-12 03:21:00 -08001804static int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001806 int type;
1807 switch (ipstats_mib_noroutes) {
1808 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001809 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001810 if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) {
1811 IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
1812 break;
1813 }
1814 /* FALLTHROUGH */
1815 case IPSTATS_MIB_OUTNOROUTES:
1816 IP6_INC_STATS(ip6_dst_idev(skb->dst), ipstats_mib_noroutes);
1817 break;
1818 }
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001819 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820 kfree_skb(skb);
1821 return 0;
1822}
1823
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001824static int ip6_pkt_discard(struct sk_buff *skb)
1825{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001826 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001827}
1828
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03001829static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830{
1831 skb->dev = skb->dst->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001832 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833}
1834
David S. Miller6723ab52006-10-18 21:20:57 -07001835#ifdef CONFIG_IPV6_MULTIPLE_TABLES
1836
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001837static int ip6_pkt_prohibit(struct sk_buff *skb)
1838{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001839 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001840}
1841
1842static int ip6_pkt_prohibit_out(struct sk_buff *skb)
1843{
1844 skb->dev = skb->dst->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001845 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001846}
1847
David S. Miller6723ab52006-10-18 21:20:57 -07001848#endif
1849
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850/*
1851 * Allocate a dst for local (unicast / anycast) address.
1852 */
1853
1854struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
1855 const struct in6_addr *addr,
1856 int anycast)
1857{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001858 struct net *net = dev_net(idev->dev);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08001859 struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
1861 if (rt == NULL)
1862 return ERR_PTR(-ENOMEM);
1863
Daniel Lezcano55786892008-03-04 13:47:47 -08001864 dev_hold(net->loopback_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 in6_dev_hold(idev);
1866
1867 rt->u.dst.flags = DST_HOST;
1868 rt->u.dst.input = ip6_input;
1869 rt->u.dst.output = ip6_output;
Daniel Lezcano55786892008-03-04 13:47:47 -08001870 rt->rt6i_dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 rt->rt6i_idev = idev;
1872 rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev);
Daniel Lezcano55786892008-03-04 13:47:47 -08001873 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, dst_mtu(&rt->u.dst));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1;
1875 rt->u.dst.obsolete = -1;
1876
1877 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09001878 if (anycast)
1879 rt->rt6i_flags |= RTF_ANYCAST;
1880 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 rt->rt6i_flags |= RTF_LOCAL;
1882 rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
1883 if (rt->rt6i_nexthop == NULL) {
YOSHIFUJI Hideaki40aa7b92006-10-19 13:50:09 +09001884 dst_free(&rt->u.dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 return ERR_PTR(-ENOMEM);
1886 }
1887
1888 ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
1889 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08001890 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891
1892 atomic_set(&rt->u.dst.__refcnt, 1);
1893
1894 return rt;
1895}
1896
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001897struct arg_dev_net {
1898 struct net_device *dev;
1899 struct net *net;
1900};
1901
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902static int fib6_ifdown(struct rt6_info *rt, void *arg)
1903{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001904 struct net_device *dev = ((struct arg_dev_net *)arg)->dev;
1905 struct net *net = ((struct arg_dev_net *)arg)->net;
1906
1907 if (((void *)rt->rt6i_dev == dev || dev == NULL) &&
1908 rt != net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 RT6_TRACE("deleted by ifdown %p\n", rt);
1910 return -1;
1911 }
1912 return 0;
1913}
1914
Daniel Lezcanof3db4852008-03-03 23:27:06 -08001915void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001917 struct arg_dev_net adn = {
1918 .dev = dev,
1919 .net = net,
1920 };
1921
1922 fib6_clean_all(net, fib6_ifdown, 0, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923}
1924
1925struct rt6_mtu_change_arg
1926{
1927 struct net_device *dev;
1928 unsigned mtu;
1929};
1930
1931static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
1932{
1933 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
1934 struct inet6_dev *idev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001935 struct net *net = dev_net(arg->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936
1937 /* In IPv6 pmtu discovery is not optional,
1938 so that RTAX_MTU lock cannot disable it.
1939 We still use this lock to block changes
1940 caused by addrconf/ndisc.
1941 */
1942
1943 idev = __in6_dev_get(arg->dev);
1944 if (idev == NULL)
1945 return 0;
1946
1947 /* For administrative MTU increase, there is no way to discover
1948 IPv6 PMTU increase, so PMTU increase should be updated here.
1949 Since RFC 1981 doesn't include administrative MTU increase
1950 update PMTU increase is a MUST. (i.e. jumbo frame)
1951 */
1952 /*
1953 If new MTU is less than route PMTU, this new MTU will be the
1954 lowest MTU in the path, update the route PMTU to reflect PMTU
1955 decreases; if new MTU is greater than route PMTU, and the
1956 old MTU is the lowest MTU in the path, update the route PMTU
1957 to reflect the increase. In this case if the other nodes' MTU
1958 also have the lowest MTU, TOO BIG MESSAGE will be lead to
1959 PMTU discouvery.
1960 */
1961 if (rt->rt6i_dev == arg->dev &&
1962 !dst_metric_locked(&rt->u.dst, RTAX_MTU) &&
Jim Paris23717792008-01-31 16:36:25 -08001963 (dst_mtu(&rt->u.dst) >= arg->mtu ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001964 (dst_mtu(&rt->u.dst) < arg->mtu &&
Simon Arlott566cfd82007-07-26 00:09:55 -07001965 dst_mtu(&rt->u.dst) == idev->cnf.mtu6))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966 rt->u.dst.metrics[RTAX_MTU-1] = arg->mtu;
Daniel Lezcano55786892008-03-04 13:47:47 -08001967 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07001968 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969 return 0;
1970}
1971
1972void rt6_mtu_change(struct net_device *dev, unsigned mtu)
1973{
Thomas Grafc71099a2006-08-04 23:20:06 -07001974 struct rt6_mtu_change_arg arg = {
1975 .dev = dev,
1976 .mtu = mtu,
1977 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001979 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980}
1981
Patrick McHardyef7c79e2007-06-05 12:38:30 -07001982static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07001983 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07001984 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07001985 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07001986 [RTA_PRIORITY] = { .type = NLA_U32 },
1987 [RTA_METRICS] = { .type = NLA_NESTED },
1988};
1989
1990static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
1991 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992{
Thomas Graf86872cb2006-08-22 00:01:08 -07001993 struct rtmsg *rtm;
1994 struct nlattr *tb[RTA_MAX+1];
1995 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996
Thomas Graf86872cb2006-08-22 00:01:08 -07001997 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
1998 if (err < 0)
1999 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000
Thomas Graf86872cb2006-08-22 00:01:08 -07002001 err = -EINVAL;
2002 rtm = nlmsg_data(nlh);
2003 memset(cfg, 0, sizeof(*cfg));
2004
2005 cfg->fc_table = rtm->rtm_table;
2006 cfg->fc_dst_len = rtm->rtm_dst_len;
2007 cfg->fc_src_len = rtm->rtm_src_len;
2008 cfg->fc_flags = RTF_UP;
2009 cfg->fc_protocol = rtm->rtm_protocol;
2010
2011 if (rtm->rtm_type == RTN_UNREACHABLE)
2012 cfg->fc_flags |= RTF_REJECT;
2013
2014 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2015 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002016 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002017
2018 if (tb[RTA_GATEWAY]) {
2019 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2020 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002022
2023 if (tb[RTA_DST]) {
2024 int plen = (rtm->rtm_dst_len + 7) >> 3;
2025
2026 if (nla_len(tb[RTA_DST]) < plen)
2027 goto errout;
2028
2029 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002031
2032 if (tb[RTA_SRC]) {
2033 int plen = (rtm->rtm_src_len + 7) >> 3;
2034
2035 if (nla_len(tb[RTA_SRC]) < plen)
2036 goto errout;
2037
2038 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002040
2041 if (tb[RTA_OIF])
2042 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2043
2044 if (tb[RTA_PRIORITY])
2045 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2046
2047 if (tb[RTA_METRICS]) {
2048 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2049 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002051
2052 if (tb[RTA_TABLE])
2053 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2054
2055 err = 0;
2056errout:
2057 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058}
2059
Thomas Grafc127ea22007-03-22 11:58:32 -07002060static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061{
Thomas Graf86872cb2006-08-22 00:01:08 -07002062 struct fib6_config cfg;
2063 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064
Thomas Graf86872cb2006-08-22 00:01:08 -07002065 err = rtm_to_fib6_config(skb, nlh, &cfg);
2066 if (err < 0)
2067 return err;
2068
2069 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070}
2071
Thomas Grafc127ea22007-03-22 11:58:32 -07002072static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073{
Thomas Graf86872cb2006-08-22 00:01:08 -07002074 struct fib6_config cfg;
2075 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076
Thomas Graf86872cb2006-08-22 00:01:08 -07002077 err = rtm_to_fib6_config(skb, nlh, &cfg);
2078 if (err < 0)
2079 return err;
2080
2081 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082}
2083
Thomas Graf339bf982006-11-10 14:10:15 -08002084static inline size_t rt6_nlmsg_size(void)
2085{
2086 return NLMSG_ALIGN(sizeof(struct rtmsg))
2087 + nla_total_size(16) /* RTA_SRC */
2088 + nla_total_size(16) /* RTA_DST */
2089 + nla_total_size(16) /* RTA_GATEWAY */
2090 + nla_total_size(16) /* RTA_PREFSRC */
2091 + nla_total_size(4) /* RTA_TABLE */
2092 + nla_total_size(4) /* RTA_IIF */
2093 + nla_total_size(4) /* RTA_OIF */
2094 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002095 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002096 + nla_total_size(sizeof(struct rta_cacheinfo));
2097}
2098
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002100 struct in6_addr *dst, struct in6_addr *src,
2101 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002102 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103{
2104 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002105 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002106 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002107 u32 table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108
2109 if (prefix) { /* user wants prefix routes only */
2110 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2111 /* success since this is not a prefix route */
2112 return 1;
2113 }
2114 }
2115
Thomas Graf2d7202b2006-08-22 00:01:27 -07002116 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
2117 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08002118 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002119
2120 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 rtm->rtm_family = AF_INET6;
2122 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2123 rtm->rtm_src_len = rt->rt6i_src.plen;
2124 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002125 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002126 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002127 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002128 table = RT6_TABLE_UNSPEC;
2129 rtm->rtm_table = table;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002130 NLA_PUT_U32(skb, RTA_TABLE, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 if (rt->rt6i_flags&RTF_REJECT)
2132 rtm->rtm_type = RTN_UNREACHABLE;
2133 else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK))
2134 rtm->rtm_type = RTN_LOCAL;
2135 else
2136 rtm->rtm_type = RTN_UNICAST;
2137 rtm->rtm_flags = 0;
2138 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2139 rtm->rtm_protocol = rt->rt6i_protocol;
2140 if (rt->rt6i_flags&RTF_DYNAMIC)
2141 rtm->rtm_protocol = RTPROT_REDIRECT;
2142 else if (rt->rt6i_flags & RTF_ADDRCONF)
2143 rtm->rtm_protocol = RTPROT_KERNEL;
2144 else if (rt->rt6i_flags&RTF_DEFAULT)
2145 rtm->rtm_protocol = RTPROT_RA;
2146
2147 if (rt->rt6i_flags&RTF_CACHE)
2148 rtm->rtm_flags |= RTM_F_CLONED;
2149
2150 if (dst) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002151 NLA_PUT(skb, RTA_DST, 16, dst);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002152 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 } else if (rtm->rtm_dst_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002154 NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155#ifdef CONFIG_IPV6_SUBTREES
2156 if (src) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002157 NLA_PUT(skb, RTA_SRC, 16, src);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002158 rtm->rtm_src_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 } else if (rtm->rtm_src_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002160 NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002162 if (iif) {
2163#ifdef CONFIG_IPV6_MROUTE
2164 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
2165 int err = ip6mr_get_route(skb, rtm, nowait);
2166 if (err <= 0) {
2167 if (!nowait) {
2168 if (err == 0)
2169 return 0;
2170 goto nla_put_failure;
2171 } else {
2172 if (err == -EMSGSIZE)
2173 goto nla_put_failure;
2174 }
2175 }
2176 } else
2177#endif
2178 NLA_PUT_U32(skb, RTA_IIF, iif);
2179 } else if (dst) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 struct in6_addr saddr_buf;
YOSHIFUJI Hideaki5e5f3f02008-03-03 21:44:34 +09002181 if (ipv6_dev_get_saddr(ip6_dst_idev(&rt->u.dst)->dev,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +09002182 dst, 0, &saddr_buf) == 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002183 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002185
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186 if (rtnetlink_put_metrics(skb, rt->u.dst.metrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002187 goto nla_put_failure;
2188
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 if (rt->u.dst.neighbour)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002190 NLA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
2191
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 if (rt->u.dst.dev)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002193 NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
2194
2195 NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
Thomas Grafe3703b32006-11-27 09:27:07 -08002196
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002197 if (!(rt->rt6i_flags & RTF_EXPIRES))
2198 expires = 0;
2199 else if (rt->rt6i_expires - jiffies < INT_MAX)
2200 expires = rt->rt6i_expires - jiffies;
2201 else
2202 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002203
Thomas Grafe3703b32006-11-27 09:27:07 -08002204 if (rtnl_put_cacheinfo(skb, &rt->u.dst, 0, 0, 0,
2205 expires, rt->u.dst.error) < 0)
2206 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207
Thomas Graf2d7202b2006-08-22 00:01:27 -07002208 return nlmsg_end(skb, nlh);
2209
2210nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002211 nlmsg_cancel(skb, nlh);
2212 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213}
2214
Patrick McHardy1b43af52006-08-10 23:11:17 -07002215int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216{
2217 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2218 int prefix;
2219
Thomas Graf2d7202b2006-08-22 00:01:27 -07002220 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2221 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2223 } else
2224 prefix = 0;
2225
2226 return rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
2227 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002228 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229}
2230
Thomas Grafc127ea22007-03-22 11:58:32 -07002231static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002233 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002234 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002236 struct sk_buff *skb;
2237 struct rtmsg *rtm;
2238 struct flowi fl;
2239 int err, iif = 0;
2240
2241 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2242 if (err < 0)
2243 goto errout;
2244
2245 err = -EINVAL;
2246 memset(&fl, 0, sizeof(fl));
2247
2248 if (tb[RTA_SRC]) {
2249 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2250 goto errout;
2251
2252 ipv6_addr_copy(&fl.fl6_src, nla_data(tb[RTA_SRC]));
2253 }
2254
2255 if (tb[RTA_DST]) {
2256 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2257 goto errout;
2258
2259 ipv6_addr_copy(&fl.fl6_dst, nla_data(tb[RTA_DST]));
2260 }
2261
2262 if (tb[RTA_IIF])
2263 iif = nla_get_u32(tb[RTA_IIF]);
2264
2265 if (tb[RTA_OIF])
2266 fl.oif = nla_get_u32(tb[RTA_OIF]);
2267
2268 if (iif) {
2269 struct net_device *dev;
Daniel Lezcano55786892008-03-04 13:47:47 -08002270 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002271 if (!dev) {
2272 err = -ENODEV;
2273 goto errout;
2274 }
2275 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276
2277 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
Thomas Grafab364a62006-08-22 00:01:47 -07002278 if (skb == NULL) {
2279 err = -ENOBUFS;
2280 goto errout;
2281 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282
2283 /* Reserve room for dummy headers, this skb can pass
2284 through good chunk of routing engine.
2285 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002286 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2288
Daniel Lezcano8a3edd82008-03-07 11:14:16 -08002289 rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 skb->dst = &rt->u.dst;
2291
Thomas Grafab364a62006-08-22 00:01:47 -07002292 err = rt6_fill_node(skb, rt, &fl.fl6_dst, &fl.fl6_src, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002294 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002296 kfree_skb(skb);
2297 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 }
2299
Daniel Lezcano55786892008-03-04 13:47:47 -08002300 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002301errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303}
2304
Thomas Graf86872cb2006-08-22 00:01:08 -07002305void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306{
2307 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002308 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002309 u32 seq;
2310 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002312 err = -ENOBUFS;
2313 seq = info->nlh != NULL ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002314
Thomas Graf339bf982006-11-10 14:10:15 -08002315 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
Thomas Graf21713eb2006-08-15 00:35:24 -07002316 if (skb == NULL)
2317 goto errout;
2318
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002319 err = rt6_fill_node(skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002320 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002321 if (err < 0) {
2322 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2323 WARN_ON(err == -EMSGSIZE);
2324 kfree_skb(skb);
2325 goto errout;
2326 }
Daniel Lezcano55786892008-03-04 13:47:47 -08002327 err = rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2328 info->nlh, gfp_any());
Thomas Graf21713eb2006-08-15 00:35:24 -07002329errout:
2330 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002331 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332}
2333
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002334static int ip6_route_dev_notify(struct notifier_block *this,
2335 unsigned long event, void *data)
2336{
2337 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002338 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002339
2340 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
2341 net->ipv6.ip6_null_entry->u.dst.dev = dev;
2342 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2343#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2344 net->ipv6.ip6_prohibit_entry->u.dst.dev = dev;
2345 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
2346 net->ipv6.ip6_blk_hole_entry->u.dst.dev = dev;
2347 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2348#endif
2349 }
2350
2351 return NOTIFY_OK;
2352}
2353
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354/*
2355 * /proc
2356 */
2357
2358#ifdef CONFIG_PROC_FS
2359
2360#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
2361
2362struct rt6_proc_arg
2363{
2364 char *buffer;
2365 int offset;
2366 int length;
2367 int skip;
2368 int len;
2369};
2370
2371static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2372{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002373 struct seq_file *m = p_arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002375 seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_dst.addr),
2376 rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
2378#ifdef CONFIG_IPV6_SUBTREES
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002379 seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_src.addr),
2380 rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002382 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383#endif
2384
2385 if (rt->rt6i_nexthop) {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002386 seq_printf(m, NIP6_SEQFMT,
2387 NIP6(*((struct in6_addr *)rt->rt6i_nexthop->primary_key)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002389 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 }
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002391 seq_printf(m, " %08x %08x %08x %08x %8s\n",
2392 rt->rt6i_metric, atomic_read(&rt->u.dst.__refcnt),
2393 rt->u.dst.__use, rt->rt6i_flags,
2394 rt->rt6i_dev ? rt->rt6i_dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395 return 0;
2396}
2397
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002398static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002400 struct net *net = (struct net *)m->private;
2401 fib6_clean_all(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002402 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403}
2404
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002405static int ipv6_route_open(struct inode *inode, struct file *file)
2406{
Pavel Emelyanova2333522008-03-26 16:49:40 -07002407 int err;
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002408 struct net *net = get_proc_net(inode);
2409 if (!net)
2410 return -ENXIO;
Pavel Emelyanova2333522008-03-26 16:49:40 -07002411
2412 err = single_open(file, ipv6_route_show, net);
2413 if (err < 0) {
2414 put_net(net);
2415 return err;
2416 }
2417
2418 return 0;
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002419}
2420
2421static int ipv6_route_release(struct inode *inode, struct file *file)
2422{
2423 struct seq_file *seq = file->private_data;
2424 struct net *net = seq->private;
2425 put_net(net);
2426 return single_release(inode, file);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002427}
2428
2429static const struct file_operations ipv6_route_proc_fops = {
2430 .owner = THIS_MODULE,
2431 .open = ipv6_route_open,
2432 .read = seq_read,
2433 .llseek = seq_lseek,
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002434 .release = ipv6_route_release,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002435};
2436
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2438{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002439 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002441 net->ipv6.rt6_stats->fib_nodes,
2442 net->ipv6.rt6_stats->fib_route_nodes,
2443 net->ipv6.rt6_stats->fib_rt_alloc,
2444 net->ipv6.rt6_stats->fib_rt_entries,
2445 net->ipv6.rt6_stats->fib_rt_cache,
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002446 atomic_read(&net->ipv6.ip6_dst_ops->entries),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002447 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448
2449 return 0;
2450}
2451
2452static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2453{
Pavel Emelyanova2333522008-03-26 16:49:40 -07002454 int err;
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002455 struct net *net = get_proc_net(inode);
Pavel Emelyanova2333522008-03-26 16:49:40 -07002456 if (!net)
2457 return -ENXIO;
2458
2459 err = single_open(file, rt6_stats_seq_show, net);
2460 if (err < 0) {
2461 put_net(net);
2462 return err;
2463 }
2464
2465 return 0;
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002466}
2467
2468static int rt6_stats_seq_release(struct inode *inode, struct file *file)
2469{
2470 struct seq_file *seq = file->private_data;
2471 struct net *net = (struct net *)seq->private;
2472 put_net(net);
2473 return single_release(inode, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474}
2475
Arjan van de Ven9a321442007-02-12 00:55:35 -08002476static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 .owner = THIS_MODULE,
2478 .open = rt6_stats_seq_open,
2479 .read = seq_read,
2480 .llseek = seq_lseek,
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002481 .release = rt6_stats_seq_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482};
2483#endif /* CONFIG_PROC_FS */
2484
2485#ifdef CONFIG_SYSCTL
2486
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487static
2488int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
2489 void __user *buffer, size_t *lenp, loff_t *ppos)
2490{
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08002491 struct net *net = current->nsproxy->net_ns;
2492 int delay = net->ipv6.sysctl.flush_delay;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493 if (write) {
2494 proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08002495 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 return 0;
2497 } else
2498 return -EINVAL;
2499}
2500
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002501ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002502 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002504 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002506 .mode = 0200,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002507 .proc_handler = &ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508 },
2509 {
2510 .ctl_name = NET_IPV6_ROUTE_GC_THRESH,
2511 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002512 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513 .maxlen = sizeof(int),
2514 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002515 .proc_handler = &proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516 },
2517 {
2518 .ctl_name = NET_IPV6_ROUTE_MAX_SIZE,
2519 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002520 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 .maxlen = sizeof(int),
2522 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002523 .proc_handler = &proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 },
2525 {
2526 .ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL,
2527 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002528 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 .maxlen = sizeof(int),
2530 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002531 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532 .strategy = &sysctl_jiffies,
2533 },
2534 {
2535 .ctl_name = NET_IPV6_ROUTE_GC_TIMEOUT,
2536 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002537 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538 .maxlen = sizeof(int),
2539 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002540 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541 .strategy = &sysctl_jiffies,
2542 },
2543 {
2544 .ctl_name = NET_IPV6_ROUTE_GC_INTERVAL,
2545 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002546 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 .maxlen = sizeof(int),
2548 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002549 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 .strategy = &sysctl_jiffies,
2551 },
2552 {
2553 .ctl_name = NET_IPV6_ROUTE_GC_ELASTICITY,
2554 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002555 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 .maxlen = sizeof(int),
2557 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002558 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 .strategy = &sysctl_jiffies,
2560 },
2561 {
2562 .ctl_name = NET_IPV6_ROUTE_MTU_EXPIRES,
2563 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002564 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 .maxlen = sizeof(int),
2566 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002567 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 .strategy = &sysctl_jiffies,
2569 },
2570 {
2571 .ctl_name = NET_IPV6_ROUTE_MIN_ADVMSS,
2572 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002573 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 .maxlen = sizeof(int),
2575 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002576 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 .strategy = &sysctl_jiffies,
2578 },
2579 {
2580 .ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
2581 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002582 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 .maxlen = sizeof(int),
2584 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002585 .proc_handler = &proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586 .strategy = &sysctl_ms_jiffies,
2587 },
2588 { .ctl_name = 0 }
2589};
2590
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002591struct ctl_table *ipv6_route_sysctl_init(struct net *net)
2592{
2593 struct ctl_table *table;
2594
2595 table = kmemdup(ipv6_route_table_template,
2596 sizeof(ipv6_route_table_template),
2597 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002598
2599 if (table) {
2600 table[0].data = &net->ipv6.sysctl.flush_delay;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002601 table[1].data = &net->ipv6.ip6_dst_ops->gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002602 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2603 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2604 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2605 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2606 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2607 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2608 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
2609 }
2610
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002611 return table;
2612}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613#endif
2614
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002615static int ip6_route_net_init(struct net *net)
2616{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002617 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002618
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002619 net->ipv6.ip6_dst_ops = kmemdup(&ip6_dst_ops_template,
2620 sizeof(*net->ipv6.ip6_dst_ops),
2621 GFP_KERNEL);
2622 if (!net->ipv6.ip6_dst_ops)
2623 goto out;
Denis V. Lunev48115be2008-04-16 02:01:34 -07002624 net->ipv6.ip6_dst_ops->dst_net = hold_net(net);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002625
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002626 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2627 sizeof(*net->ipv6.ip6_null_entry),
2628 GFP_KERNEL);
2629 if (!net->ipv6.ip6_null_entry)
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002630 goto out_ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002631 net->ipv6.ip6_null_entry->u.dst.path =
2632 (struct dst_entry *)net->ipv6.ip6_null_entry;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002633 net->ipv6.ip6_null_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002634
2635#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2636 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2637 sizeof(*net->ipv6.ip6_prohibit_entry),
2638 GFP_KERNEL);
2639 if (!net->ipv6.ip6_prohibit_entry) {
2640 kfree(net->ipv6.ip6_null_entry);
2641 goto out;
2642 }
2643 net->ipv6.ip6_prohibit_entry->u.dst.path =
2644 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002645 net->ipv6.ip6_prohibit_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002646
2647 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2648 sizeof(*net->ipv6.ip6_blk_hole_entry),
2649 GFP_KERNEL);
2650 if (!net->ipv6.ip6_blk_hole_entry) {
2651 kfree(net->ipv6.ip6_null_entry);
2652 kfree(net->ipv6.ip6_prohibit_entry);
2653 goto out;
2654 }
2655 net->ipv6.ip6_blk_hole_entry->u.dst.path =
2656 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002657 net->ipv6.ip6_blk_hole_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002658#endif
2659
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002660#ifdef CONFIG_PROC_FS
2661 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2662 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2663#endif
Benjamin Thery6891a342008-03-04 13:49:47 -08002664 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2665
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002666 ret = 0;
2667out:
2668 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002669
2670out_ip6_dst_ops:
Denis V. Lunev48115be2008-04-16 02:01:34 -07002671 release_net(net->ipv6.ip6_dst_ops->dst_net);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002672 kfree(net->ipv6.ip6_dst_ops);
2673 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002674}
2675
2676static void ip6_route_net_exit(struct net *net)
2677{
2678#ifdef CONFIG_PROC_FS
2679 proc_net_remove(net, "ipv6_route");
2680 proc_net_remove(net, "rt6_stats");
2681#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002682 kfree(net->ipv6.ip6_null_entry);
2683#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2684 kfree(net->ipv6.ip6_prohibit_entry);
2685 kfree(net->ipv6.ip6_blk_hole_entry);
2686#endif
Denis V. Lunev48115be2008-04-16 02:01:34 -07002687 release_net(net->ipv6.ip6_dst_ops->dst_net);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002688 kfree(net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002689}
2690
2691static struct pernet_operations ip6_route_net_ops = {
2692 .init = ip6_route_net_init,
2693 .exit = ip6_route_net_exit,
2694};
2695
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002696static struct notifier_block ip6_route_dev_notifier = {
2697 .notifier_call = ip6_route_dev_notify,
2698 .priority = 0,
2699};
2700
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002701int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002703 int ret;
2704
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002705 ret = -ENOMEM;
2706 ip6_dst_ops_template.kmem_cachep =
2707 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
2708 SLAB_HWCACHE_ALIGN, NULL);
2709 if (!ip6_dst_ops_template.kmem_cachep)
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002710 goto out;;
David S. Miller14e50e52007-05-24 18:17:54 -07002711
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002712 ret = register_pernet_subsys(&ip6_route_net_ops);
2713 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002714 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002715
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002716 /* Registering of the loopback is done before this portion of code,
2717 * the loopback reference in rt6_info will not be taken, do it
2718 * manually for init_net */
2719 init_net.ipv6.ip6_null_entry->u.dst.dev = init_net.loopback_dev;
2720 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2721 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2722 init_net.ipv6.ip6_prohibit_entry->u.dst.dev = init_net.loopback_dev;
2723 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2724 init_net.ipv6.ip6_blk_hole_entry->u.dst.dev = init_net.loopback_dev;
2725 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2726 #endif
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002727 ret = fib6_init();
2728 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002729 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002730
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002731 ret = xfrm6_init();
2732 if (ret)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002733 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08002734
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002735 ret = fib6_rules_init();
2736 if (ret)
2737 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08002738
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002739 ret = -ENOBUFS;
2740 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL) ||
2741 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL) ||
2742 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL))
2743 goto fib6_rules_init;
2744
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002745 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002746 if (ret)
2747 goto fib6_rules_init;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002748
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002749out:
2750 return ret;
2751
2752fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002753 fib6_rules_cleanup();
2754xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002755 xfrm6_fini();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002756out_fib6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002757 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002758out_register_subsys:
2759 unregister_pernet_subsys(&ip6_route_net_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002760out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002761 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002762 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763}
2764
2765void ip6_route_cleanup(void)
2766{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002767 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Graf101367c2006-08-04 03:39:02 -07002768 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002771 unregister_pernet_subsys(&ip6_route_net_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002772 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773}