blob: 9af6115f0f500822c94ff6cb751ab24cd34de911 [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,
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900240 struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700242 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243{
244 struct rt6_info *local = NULL;
245 struct rt6_info *sprt;
246
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900247 if (!oif && ipv6_addr_any(saddr))
248 goto out;
249
250 for (sprt = rt; sprt; sprt = sprt->u.dst.rt6_next) {
251 struct net_device *dev = sprt->rt6i_dev;
252
253 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 if (dev->ifindex == oif)
255 return sprt;
256 if (dev->flags & IFF_LOOPBACK) {
257 if (sprt->rt6i_idev == NULL ||
258 sprt->rt6i_idev->dev->ifindex != oif) {
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700259 if (flags & RT6_LOOKUP_F_IFACE && oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 continue;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900261 if (local && (!oif ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 local->rt6i_idev->dev->ifindex == oif))
263 continue;
264 }
265 local = sprt;
266 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900267 } else {
268 if (ipv6_chk_addr(net, saddr, dev,
269 flags & RT6_LOOKUP_F_IFACE))
270 return sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900272 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900274 if (oif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 if (local)
276 return local;
277
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700278 if (flags & RT6_LOOKUP_F_IFACE)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800279 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900281out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 return rt;
283}
284
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800285#ifdef CONFIG_IPV6_ROUTER_PREF
286static void rt6_probe(struct rt6_info *rt)
287{
288 struct neighbour *neigh = rt ? rt->rt6i_nexthop : NULL;
289 /*
290 * Okay, this does not seem to be appropriate
291 * for now, however, we need to check if it
292 * is really so; aka Router Reachability Probing.
293 *
294 * Router Reachability Probe MUST be rate-limited
295 * to no more than one per minute.
296 */
297 if (!neigh || (neigh->nud_state & NUD_VALID))
298 return;
299 read_lock_bh(&neigh->lock);
300 if (!(neigh->nud_state & NUD_VALID) &&
YOSHIFUJI Hideaki52e16352006-03-20 17:05:47 -0800301 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800302 struct in6_addr mcaddr;
303 struct in6_addr *target;
304
305 neigh->updated = jiffies;
306 read_unlock_bh(&neigh->lock);
307
308 target = (struct in6_addr *)&neigh->primary_key;
309 addrconf_addr_solict_mult(target, &mcaddr);
310 ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
311 } else
312 read_unlock_bh(&neigh->lock);
313}
314#else
315static inline void rt6_probe(struct rt6_info *rt)
316{
317 return;
318}
319#endif
320
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800322 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 */
Dave Jonesb6f99a22007-03-22 12:27:49 -0700324static inline int rt6_check_dev(struct rt6_info *rt, int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800326 struct net_device *dev = rt->rt6i_dev;
David S. Miller161980f2007-04-06 11:42:27 -0700327 if (!oif || dev->ifindex == oif)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800328 return 2;
David S. Miller161980f2007-04-06 11:42:27 -0700329 if ((dev->flags & IFF_LOOPBACK) &&
330 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
331 return 1;
332 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
Dave Jonesb6f99a22007-03-22 12:27:49 -0700335static inline int rt6_check_neigh(struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800337 struct neighbour *neigh = rt->rt6i_nexthop;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800338 int m;
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700339 if (rt->rt6i_flags & RTF_NONEXTHOP ||
340 !(rt->rt6i_flags & RTF_GATEWAY))
341 m = 1;
342 else if (neigh) {
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800343 read_lock_bh(&neigh->lock);
344 if (neigh->nud_state & NUD_VALID)
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700345 m = 2;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800346#ifdef CONFIG_IPV6_ROUTER_PREF
347 else if (neigh->nud_state & NUD_FAILED)
348 m = 0;
349#endif
350 else
YOSHIFUJI Hideakiea73ee22006-11-06 09:45:44 -0800351 m = 1;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800352 read_unlock_bh(&neigh->lock);
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800353 } else
354 m = 0;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800355 return m;
356}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800358static int rt6_score_route(struct rt6_info *rt, int oif,
359 int strict)
360{
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700361 int m, n;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900362
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700363 m = rt6_check_dev(rt, oif);
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700364 if (!m && (strict & RT6_LOOKUP_F_IFACE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800365 return -1;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800366#ifdef CONFIG_IPV6_ROUTER_PREF
367 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
368#endif
YOSHIFUJI Hideaki4d0c5912006-05-26 13:23:41 -0700369 n = rt6_check_neigh(rt);
YOSHIFUJI Hideaki557e92e2006-11-06 09:45:45 -0800370 if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800371 return -1;
372 return m;
373}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
David S. Millerf11e6652007-03-24 20:36:25 -0700375static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
376 int *mpri, struct rt6_info *match)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800377{
David S. Millerf11e6652007-03-24 20:36:25 -0700378 int m;
379
380 if (rt6_check_expired(rt))
381 goto out;
382
383 m = rt6_score_route(rt, oif, strict);
384 if (m < 0)
385 goto out;
386
387 if (m > *mpri) {
388 if (strict & RT6_LOOKUP_F_REACHABLE)
389 rt6_probe(match);
390 *mpri = m;
391 match = rt;
392 } else if (strict & RT6_LOOKUP_F_REACHABLE) {
393 rt6_probe(rt);
394 }
395
396out:
397 return match;
398}
399
400static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
401 struct rt6_info *rr_head,
402 u32 metric, int oif, int strict)
403{
404 struct rt6_info *rt, *match;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800405 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
David S. Millerf11e6652007-03-24 20:36:25 -0700407 match = NULL;
408 for (rt = rr_head; rt && rt->rt6i_metric == metric;
409 rt = rt->u.dst.rt6_next)
410 match = find_match(rt, oif, strict, &mpri, match);
411 for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
412 rt = rt->u.dst.rt6_next)
413 match = find_match(rt, oif, strict, &mpri, match);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800414
David S. Millerf11e6652007-03-24 20:36:25 -0700415 return match;
416}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800417
David S. Millerf11e6652007-03-24 20:36:25 -0700418static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
419{
420 struct rt6_info *match, *rt0;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800421 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
David S. Millerf11e6652007-03-24 20:36:25 -0700423 RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800424 __func__, fn->leaf, oif);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
David S. Millerf11e6652007-03-24 20:36:25 -0700426 rt0 = fn->rr_ptr;
427 if (!rt0)
428 fn->rr_ptr = rt0 = fn->leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
David S. Millerf11e6652007-03-24 20:36:25 -0700430 match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800432 if (!match &&
David S. Millerf11e6652007-03-24 20:36:25 -0700433 (strict & RT6_LOOKUP_F_REACHABLE)) {
434 struct rt6_info *next = rt0->u.dst.rt6_next;
435
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800436 /* no entries matched; do round-robin */
David S. Millerf11e6652007-03-24 20:36:25 -0700437 if (!next || next->rt6i_metric != rt0->rt6i_metric)
438 next = fn->leaf;
439
440 if (next != rt0)
441 fn->rr_ptr = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 }
443
David S. Millerf11e6652007-03-24 20:36:25 -0700444 RT6_TRACE("%s() => %p\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800445 __func__, match);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900447 net = dev_net(rt0->rt6i_dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800448 return (match ? match : net->ipv6.ip6_null_entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800451#ifdef CONFIG_IPV6_ROUTE_INFO
452int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
453 struct in6_addr *gwaddr)
454{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900455 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800456 struct route_info *rinfo = (struct route_info *) opt;
457 struct in6_addr prefix_buf, *prefix;
458 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900459 unsigned long lifetime;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800460 struct rt6_info *rt;
461
462 if (len < sizeof(struct route_info)) {
463 return -EINVAL;
464 }
465
466 /* Sanity check for prefix_len and length */
467 if (rinfo->length > 3) {
468 return -EINVAL;
469 } else if (rinfo->prefix_len > 128) {
470 return -EINVAL;
471 } else if (rinfo->prefix_len > 64) {
472 if (rinfo->length < 2) {
473 return -EINVAL;
474 }
475 } else if (rinfo->prefix_len > 0) {
476 if (rinfo->length < 1) {
477 return -EINVAL;
478 }
479 }
480
481 pref = rinfo->route_pref;
482 if (pref == ICMPV6_ROUTER_PREF_INVALID)
483 pref = ICMPV6_ROUTER_PREF_MEDIUM;
484
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900485 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800486
487 if (rinfo->length == 3)
488 prefix = (struct in6_addr *)rinfo->prefix;
489 else {
490 /* this function is safe */
491 ipv6_addr_prefix(&prefix_buf,
492 (struct in6_addr *)rinfo->prefix,
493 rinfo->prefix_len);
494 prefix = &prefix_buf;
495 }
496
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800497 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
498 dev->ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800499
500 if (rt && !lifetime) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700501 ip6_del_rt(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800502 rt = NULL;
503 }
504
505 if (!rt && lifetime)
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -0800506 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800507 pref);
508 else if (rt)
509 rt->rt6i_flags = RTF_ROUTEINFO |
510 (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
511
512 if (rt) {
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900513 if (!addrconf_finite_timeout(lifetime)) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800514 rt->rt6i_flags &= ~RTF_EXPIRES;
515 } else {
516 rt->rt6i_expires = jiffies + HZ * lifetime;
517 rt->rt6i_flags |= RTF_EXPIRES;
518 }
519 dst_release(&rt->u.dst);
520 }
521 return 0;
522}
523#endif
524
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800525#define BACKTRACK(__net, saddr) \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700526do { \
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800527 if (rt == __net->ipv6.ip6_null_entry) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700528 struct fib6_node *pn; \
Ville Nuorvalae0eda7b2006-10-16 22:11:11 -0700529 while (1) { \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700530 if (fn->fn_flags & RTN_TL_ROOT) \
531 goto out; \
532 pn = fn->parent; \
533 if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
Kim Nordlund8bce65b2006-12-13 16:38:29 -0800534 fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700535 else \
536 fn = pn; \
537 if (fn->fn_flags & RTN_RTINFO) \
538 goto restart; \
Thomas Grafc71099a2006-08-04 23:20:06 -0700539 } \
Thomas Grafc71099a2006-08-04 23:20:06 -0700540 } \
YOSHIFUJI Hideaki982f56f2006-08-23 17:22:39 -0700541} while(0)
Thomas Grafc71099a2006-08-04 23:20:06 -0700542
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800543static struct rt6_info *ip6_pol_route_lookup(struct net *net,
544 struct fib6_table *table,
Thomas Grafc71099a2006-08-04 23:20:06 -0700545 struct flowi *fl, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546{
547 struct fib6_node *fn;
548 struct rt6_info *rt;
549
Thomas Grafc71099a2006-08-04 23:20:06 -0700550 read_lock_bh(&table->tb6_lock);
551 fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
552restart:
553 rt = fn->leaf;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900554 rt = rt6_device_match(net, rt, &fl->fl6_src, fl->oif, flags);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800555 BACKTRACK(net, &fl->fl6_src);
Thomas Grafc71099a2006-08-04 23:20:06 -0700556out:
Pavel Emelyanov03f49f32007-11-10 21:28:34 -0800557 dst_use(&rt->u.dst, jiffies);
Thomas Grafc71099a2006-08-04 23:20:06 -0700558 read_unlock_bh(&table->tb6_lock);
Thomas Grafc71099a2006-08-04 23:20:06 -0700559 return rt;
560
561}
562
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900563struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
564 const struct in6_addr *saddr, int oif, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -0700565{
566 struct flowi fl = {
567 .oif = oif,
568 .nl_u = {
569 .ip6_u = {
570 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -0700571 },
572 },
573 };
574 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700575 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -0700576
Thomas Grafadaa70b2006-10-13 15:01:03 -0700577 if (saddr) {
578 memcpy(&fl.fl6_src, saddr, sizeof(*saddr));
579 flags |= RT6_LOOKUP_F_HAS_SADDR;
580 }
581
Daniel Lezcano606a2b42008-03-04 13:45:59 -0800582 dst = fib6_rule_lookup(net, &fl, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -0700583 if (dst->error == 0)
584 return (struct rt6_info *) dst;
585
586 dst_release(dst);
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 return NULL;
589}
590
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900591EXPORT_SYMBOL(rt6_lookup);
592
Thomas Grafc71099a2006-08-04 23:20:06 -0700593/* ip6_ins_rt is called with FREE table->tb6_lock.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 It takes new route entry, the addition fails by any reason the
595 route is freed. In any case, if caller does not hold it, it may
596 be destroyed.
597 */
598
Thomas Graf86872cb2006-08-22 00:01:08 -0700599static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600{
601 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -0700602 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Thomas Grafc71099a2006-08-04 23:20:06 -0700604 table = rt->rt6i_table;
605 write_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -0700606 err = fib6_add(&table->tb6_root, rt, info);
Thomas Grafc71099a2006-08-04 23:20:06 -0700607 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609 return err;
610}
611
Thomas Graf40e22e82006-08-22 00:00:45 -0700612int ip6_ins_rt(struct rt6_info *rt)
613{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800614 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900615 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -0800616 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -0800617 return __ip6_ins_rt(rt, &info);
Thomas Graf40e22e82006-08-22 00:00:45 -0700618}
619
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800620static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *daddr,
621 struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 struct rt6_info *rt;
624
625 /*
626 * Clone the route.
627 */
628
629 rt = ip6_rt_copy(ort);
630
631 if (rt) {
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900632 if (!(rt->rt6i_flags&RTF_GATEWAY)) {
633 if (rt->rt6i_dst.plen != 128 &&
634 ipv6_addr_equal(&rt->rt6i_dst.addr, daddr))
635 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 ipv6_addr_copy(&rt->rt6i_gateway, daddr);
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900637 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +0900639 ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 rt->rt6i_dst.plen = 128;
641 rt->rt6i_flags |= RTF_CACHE;
642 rt->u.dst.flags |= DST_HOST;
643
644#ifdef CONFIG_IPV6_SUBTREES
645 if (rt->rt6i_src.plen && saddr) {
646 ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
647 rt->rt6i_src.plen = 128;
648 }
649#endif
650
651 rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
652
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800653 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -0800655 return rt;
656}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800658static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *daddr)
659{
660 struct rt6_info *rt = ip6_rt_copy(ort);
661 if (rt) {
662 ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
663 rt->rt6i_dst.plen = 128;
664 rt->rt6i_flags |= RTF_CACHE;
YOSHIFUJI Hideaki299d9932006-03-20 16:58:32 -0800665 rt->u.dst.flags |= DST_HOST;
666 rt->rt6i_nexthop = neigh_clone(ort->rt6i_nexthop);
667 }
668 return rt;
669}
670
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800671static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
672 struct flowi *fl, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673{
674 struct fib6_node *fn;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800675 struct rt6_info *rt, *nrt;
Thomas Grafc71099a2006-08-04 23:20:06 -0700676 int strict = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 int attempts = 3;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800678 int err;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700679 int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700681 strict |= flags & RT6_LOOKUP_F_IFACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
683relookup:
Thomas Grafc71099a2006-08-04 23:20:06 -0700684 read_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800686restart_2:
Thomas Grafc71099a2006-08-04 23:20:06 -0700687 fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688
689restart:
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700690 rt = rt6_select(fn, oif, strict | reachable);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800691
692 BACKTRACK(net, &fl->fl6_src);
693 if (rt == net->ipv6.ip6_null_entry ||
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800694 rt->rt6i_flags & RTF_CACHE)
YOSHIFUJI Hideaki1ddef0442006-03-20 17:01:24 -0800695 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800697 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700698 read_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -0800699
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800700 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP))
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800701 nrt = rt6_alloc_cow(rt, &fl->fl6_dst, &fl->fl6_src);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800702 else {
703#if CLONE_OFFLINK_ROUTE
704 nrt = rt6_alloc_clone(rt, &fl->fl6_dst);
705#else
706 goto out2;
707#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
YOSHIFUJI Hideakie40cf352006-03-20 16:59:27 -0800709
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800710 dst_release(&rt->u.dst);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800711 rt = nrt ? : net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800712
713 dst_hold(&rt->u.dst);
714 if (nrt) {
Thomas Graf40e22e82006-08-22 00:00:45 -0700715 err = ip6_ins_rt(nrt);
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800716 if (!err)
717 goto out2;
718 }
719
720 if (--attempts <= 0)
721 goto out2;
722
723 /*
Thomas Grafc71099a2006-08-04 23:20:06 -0700724 * Race condition! In the gap, when table->tb6_lock was
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800725 * released someone could insert this route. Relookup.
726 */
727 dst_release(&rt->u.dst);
728 goto relookup;
729
730out:
YOSHIFUJI Hideaki8238dd02006-03-20 17:04:35 -0800731 if (reachable) {
732 reachable = 0;
733 goto restart_2;
734 }
YOSHIFUJI Hideaki519fbd82006-03-20 17:00:05 -0800735 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -0700736 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737out2:
738 rt->u.dst.lastuse = jiffies;
739 rt->u.dst.__use++;
Thomas Grafc71099a2006-08-04 23:20:06 -0700740
741 return rt;
742}
743
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800744static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700745 struct flowi *fl, int flags)
746{
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800747 return ip6_pol_route(net, table, fl->iif, fl, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -0700748}
749
Thomas Grafc71099a2006-08-04 23:20:06 -0700750void ip6_route_input(struct sk_buff *skb)
751{
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700752 struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900753 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -0700754 int flags = RT6_LOOKUP_F_HAS_SADDR;
Thomas Grafc71099a2006-08-04 23:20:06 -0700755 struct flowi fl = {
756 .iif = skb->dev->ifindex,
757 .nl_u = {
758 .ip6_u = {
759 .daddr = iph->daddr,
760 .saddr = iph->saddr,
Al Viro90bcaf72006-11-08 00:25:17 -0800761 .flowlabel = (* (__be32 *) iph)&IPV6_FLOWINFO_MASK,
Thomas Grafc71099a2006-08-04 23:20:06 -0700762 },
763 },
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900764 .mark = skb->mark,
Thomas Grafc71099a2006-08-04 23:20:06 -0700765 .proto = iph->nexthdr,
766 };
Thomas Grafadaa70b2006-10-13 15:01:03 -0700767
768 if (rt6_need_strict(&iph->daddr))
769 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700770
Daniel Lezcano55786892008-03-04 13:47:47 -0800771 skb->dst = fib6_rule_lookup(net, &fl, flags, ip6_pol_route_input);
Thomas Grafc71099a2006-08-04 23:20:06 -0700772}
773
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800774static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
Thomas Grafc71099a2006-08-04 23:20:06 -0700775 struct flowi *fl, int flags)
776{
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800777 return ip6_pol_route(net, table, fl->oif, fl, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700778}
779
Daniel Lezcano4591db42008-03-05 10:48:10 -0800780struct dst_entry * ip6_route_output(struct net *net, struct sock *sk,
781 struct flowi *fl)
Thomas Grafc71099a2006-08-04 23:20:06 -0700782{
783 int flags = 0;
784
785 if (rt6_need_strict(&fl->fl6_dst))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700786 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -0700787
Thomas Grafadaa70b2006-10-13 15:01:03 -0700788 if (!ipv6_addr_any(&fl->fl6_src))
789 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900790 else if (sk) {
791 unsigned int prefs = inet6_sk(sk)->srcprefs;
792 if (prefs & IPV6_PREFER_SRC_TMP)
793 flags |= RT6_LOOKUP_F_SRCPREF_TMP;
794 if (prefs & IPV6_PREFER_SRC_PUBLIC)
795 flags |= RT6_LOOKUP_F_SRCPREF_PUBLIC;
796 if (prefs & IPV6_PREFER_SRC_COA)
797 flags |= RT6_LOOKUP_F_SRCPREF_COA;
798 }
Thomas Grafadaa70b2006-10-13 15:01:03 -0700799
Daniel Lezcano4591db42008-03-05 10:48:10 -0800800 return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801}
802
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900803EXPORT_SYMBOL(ip6_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
David S. Miller14e50e52007-05-24 18:17:54 -0700805int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl)
806{
807 struct rt6_info *ort = (struct rt6_info *) *dstp;
808 struct rt6_info *rt = (struct rt6_info *)
809 dst_alloc(&ip6_dst_blackhole_ops);
810 struct dst_entry *new = NULL;
811
812 if (rt) {
813 new = &rt->u.dst;
814
815 atomic_set(&new->__refcnt, 1);
816 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -0800817 new->input = dst_discard;
818 new->output = dst_discard;
David S. Miller14e50e52007-05-24 18:17:54 -0700819
820 memcpy(new->metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
821 new->dev = ort->u.dst.dev;
822 if (new->dev)
823 dev_hold(new->dev);
824 rt->rt6i_idev = ort->rt6i_idev;
825 if (rt->rt6i_idev)
826 in6_dev_hold(rt->rt6i_idev);
827 rt->rt6i_expires = 0;
828
829 ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
830 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
831 rt->rt6i_metric = 0;
832
833 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
834#ifdef CONFIG_IPV6_SUBTREES
835 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
836#endif
837
838 dst_free(new);
839 }
840
841 dst_release(*dstp);
842 *dstp = new;
843 return (new ? 0 : -ENOMEM);
844}
845EXPORT_SYMBOL_GPL(ip6_dst_blackhole);
846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847/*
848 * Destination cache support functions
849 */
850
851static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
852{
853 struct rt6_info *rt;
854
855 rt = (struct rt6_info *) dst;
856
857 if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
858 return dst;
859
860 return NULL;
861}
862
863static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
864{
865 struct rt6_info *rt = (struct rt6_info *) dst;
866
867 if (rt) {
868 if (rt->rt6i_flags & RTF_CACHE)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700869 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 else
871 dst_release(dst);
872 }
873 return NULL;
874}
875
876static void ip6_link_failure(struct sk_buff *skb)
877{
878 struct rt6_info *rt;
879
880 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
881
882 rt = (struct rt6_info *) skb->dst;
883 if (rt) {
884 if (rt->rt6i_flags&RTF_CACHE) {
885 dst_set_expires(&rt->u.dst, 0);
886 rt->rt6i_flags |= RTF_EXPIRES;
887 } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
888 rt->rt6i_node->fn_sernum = -1;
889 }
890}
891
892static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
893{
894 struct rt6_info *rt6 = (struct rt6_info*)dst;
895
896 if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) {
897 rt6->rt6i_flags |= RTF_MODIFIED;
898 if (mtu < IPV6_MIN_MTU) {
899 mtu = IPV6_MIN_MTU;
900 dst->metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
901 }
902 dst->metrics[RTAX_MTU-1] = mtu;
Tom Tucker8d717402006-07-30 20:43:36 -0700903 call_netevent_notifiers(NETEVENT_PMTU_UPDATE, dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 }
905}
906
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907static int ipv6_get_mtu(struct net_device *dev);
908
Daniel Lezcano55786892008-03-04 13:47:47 -0800909static inline unsigned int ipv6_advmss(struct net *net, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910{
911 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
912
Daniel Lezcano55786892008-03-04 13:47:47 -0800913 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
914 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900917 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
918 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
919 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 * rely only on pmtu discovery"
921 */
922 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
923 mtu = IPV6_MAXPLEN;
924 return mtu;
925}
926
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800927static struct dst_entry *icmp6_dst_gc_list;
928static DEFINE_SPINLOCK(icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -0700929
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800930struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900932 const struct in6_addr *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
934 struct rt6_info *rt;
935 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900936 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 if (unlikely(idev == NULL))
939 return NULL;
940
Benjamin Theryf2fc6a52008-03-04 13:49:23 -0800941 rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 if (unlikely(rt == NULL)) {
943 in6_dev_put(idev);
944 goto out;
945 }
946
947 dev_hold(dev);
948 if (neigh)
949 neigh_hold(neigh);
950 else
951 neigh = ndisc_get_neigh(dev, addr);
952
953 rt->rt6i_dev = dev;
954 rt->rt6i_idev = idev;
955 rt->rt6i_nexthop = neigh;
956 atomic_set(&rt->u.dst.__refcnt, 1);
957 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
958 rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev);
Daniel Lezcano55786892008-03-04 13:47:47 -0800959 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, dst_mtu(&rt->u.dst));
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800960 rt->u.dst.output = ip6_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
962#if 0 /* there's no chance to use these for ndisc */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900963 rt->u.dst.flags = ipv6_addr_type(addr) & IPV6_ADDR_UNICAST
964 ? DST_HOST
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 : 0;
966 ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
967 rt->rt6i_dst.plen = 128;
968#endif
969
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800970 spin_lock_bh(&icmp6_dst_lock);
971 rt->u.dst.next = icmp6_dst_gc_list;
972 icmp6_dst_gc_list = &rt->u.dst;
973 spin_unlock_bh(&icmp6_dst_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974
Daniel Lezcano55786892008-03-04 13:47:47 -0800975 fib6_force_start_gc(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
977out:
YOSHIFUJI Hideaki40aa7b92006-10-19 13:50:09 +0900978 return &rt->u.dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979}
980
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -0700981int icmp6_dst_gc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982{
983 struct dst_entry *dst, *next, **pprev;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -0700984 int more = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
986 next = NULL;
Thomas Graf5d0bbee2006-08-04 03:37:36 -0700987
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800988 spin_lock_bh(&icmp6_dst_lock);
989 pprev = &icmp6_dst_gc_list;
Thomas Graf5d0bbee2006-08-04 03:37:36 -0700990
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 while ((dst = *pprev) != NULL) {
992 if (!atomic_read(&dst->__refcnt)) {
993 *pprev = dst->next;
994 dst_free(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 } else {
996 pprev = &dst->next;
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -0700997 ++more;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 }
999 }
1000
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08001001 spin_unlock_bh(&icmp6_dst_lock);
Thomas Graf5d0bbee2006-08-04 03:37:36 -07001002
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001003 return more;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004}
1005
Daniel Lezcano569d3642008-01-18 03:56:57 -08001006static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 unsigned long now = jiffies;
Daniel Lezcano7019b782008-03-04 13:50:14 -08001009 struct net *net = ops->dst_net;
1010 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
1011 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
1012 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
1013 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
1014 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
Daniel Lezcano7019b782008-03-04 13:50:14 -08001016 if (time_after(rt_last_gc + rt_min_interval, now) &&
1017 atomic_read(&ops->entries) <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 goto out;
1019
Benjamin Thery6891a342008-03-04 13:49:47 -08001020 net->ipv6.ip6_rt_gc_expire++;
1021 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1022 net->ipv6.ip6_rt_last_gc = now;
Daniel Lezcano7019b782008-03-04 13:50:14 -08001023 if (atomic_read(&ops->entries) < ops->gc_thresh)
1024 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08001026 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
1027 return (atomic_read(&ops->entries) > rt_max_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028}
1029
1030/* Clean host part of a prefix. Not necessary in radix tree,
1031 but results in cleaner routing tables.
1032
1033 Remove it only when all the things will work!
1034 */
1035
1036static int ipv6_get_mtu(struct net_device *dev)
1037{
1038 int mtu = IPV6_MIN_MTU;
1039 struct inet6_dev *idev;
1040
1041 idev = in6_dev_get(dev);
1042 if (idev) {
1043 mtu = idev->cnf.mtu6;
1044 in6_dev_put(idev);
1045 }
1046 return mtu;
1047}
1048
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001049int ip6_dst_hoplimit(struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050{
YOSHIFUJI Hideaki6b75d092008-03-10 06:00:30 -04001051 int hoplimit = dst_metric(dst, RTAX_HOPLIMIT);
1052 if (hoplimit < 0) {
1053 struct net_device *dev = dst->dev;
1054 struct inet6_dev *idev = in6_dev_get(dev);
1055 if (idev) {
1056 hoplimit = idev->cnf.hop_limit;
1057 in6_dev_put(idev);
1058 } else
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001059 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 }
1061 return hoplimit;
1062}
1063
1064/*
1065 *
1066 */
1067
Thomas Graf86872cb2006-08-22 00:01:08 -07001068int ip6_route_add(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069{
1070 int err;
Daniel Lezcano55786892008-03-04 13:47:47 -08001071 struct net *net = cfg->fc_nlinfo.nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 struct rt6_info *rt = NULL;
1073 struct net_device *dev = NULL;
1074 struct inet6_dev *idev = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001075 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 int addr_type;
1077
Thomas Graf86872cb2006-08-22 00:01:08 -07001078 if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 return -EINVAL;
1080#ifndef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001081 if (cfg->fc_src_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return -EINVAL;
1083#endif
Thomas Graf86872cb2006-08-22 00:01:08 -07001084 if (cfg->fc_ifindex) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 err = -ENODEV;
Daniel Lezcano55786892008-03-04 13:47:47 -08001086 dev = dev_get_by_index(net, cfg->fc_ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 if (!dev)
1088 goto out;
1089 idev = in6_dev_get(dev);
1090 if (!idev)
1091 goto out;
1092 }
1093
Thomas Graf86872cb2006-08-22 00:01:08 -07001094 if (cfg->fc_metric == 0)
1095 cfg->fc_metric = IP6_RT_PRIO_USER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
Daniel Lezcano55786892008-03-04 13:47:47 -08001097 table = fib6_new_table(net, cfg->fc_table);
Thomas Grafc71099a2006-08-04 23:20:06 -07001098 if (table == NULL) {
1099 err = -ENOBUFS;
1100 goto out;
1101 }
1102
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08001103 rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
1105 if (rt == NULL) {
1106 err = -ENOMEM;
1107 goto out;
1108 }
1109
1110 rt->u.dst.obsolete = -1;
YOSHIFUJI Hideaki6f704992008-05-19 16:56:11 -07001111 rt->rt6i_expires = (cfg->fc_flags & RTF_EXPIRES) ?
1112 jiffies + clock_t_to_jiffies(cfg->fc_expires) :
1113 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
Thomas Graf86872cb2006-08-22 00:01:08 -07001115 if (cfg->fc_protocol == RTPROT_UNSPEC)
1116 cfg->fc_protocol = RTPROT_BOOT;
1117 rt->rt6i_protocol = cfg->fc_protocol;
1118
1119 addr_type = ipv6_addr_type(&cfg->fc_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120
1121 if (addr_type & IPV6_ADDR_MULTICAST)
1122 rt->u.dst.input = ip6_mc_input;
1123 else
1124 rt->u.dst.input = ip6_forward;
1125
1126 rt->u.dst.output = ip6_output;
1127
Thomas Graf86872cb2006-08-22 00:01:08 -07001128 ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
1129 rt->rt6i_dst.plen = cfg->fc_dst_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 if (rt->rt6i_dst.plen == 128)
1131 rt->u.dst.flags = DST_HOST;
1132
1133#ifdef CONFIG_IPV6_SUBTREES
Thomas Graf86872cb2006-08-22 00:01:08 -07001134 ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
1135 rt->rt6i_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#endif
1137
Thomas Graf86872cb2006-08-22 00:01:08 -07001138 rt->rt6i_metric = cfg->fc_metric;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
1140 /* We cannot add true routes via loopback here,
1141 they would result in kernel looping; promote them to reject routes
1142 */
Thomas Graf86872cb2006-08-22 00:01:08 -07001143 if ((cfg->fc_flags & RTF_REJECT) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) {
1145 /* hold loopback dev/idev if we haven't done so. */
Daniel Lezcano55786892008-03-04 13:47:47 -08001146 if (dev != net->loopback_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 if (dev) {
1148 dev_put(dev);
1149 in6_dev_put(idev);
1150 }
Daniel Lezcano55786892008-03-04 13:47:47 -08001151 dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 dev_hold(dev);
1153 idev = in6_dev_get(dev);
1154 if (!idev) {
1155 err = -ENODEV;
1156 goto out;
1157 }
1158 }
1159 rt->u.dst.output = ip6_pkt_discard_out;
1160 rt->u.dst.input = ip6_pkt_discard;
1161 rt->u.dst.error = -ENETUNREACH;
1162 rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
1163 goto install_route;
1164 }
1165
Thomas Graf86872cb2006-08-22 00:01:08 -07001166 if (cfg->fc_flags & RTF_GATEWAY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 struct in6_addr *gw_addr;
1168 int gwa_type;
1169
Thomas Graf86872cb2006-08-22 00:01:08 -07001170 gw_addr = &cfg->fc_gateway;
1171 ipv6_addr_copy(&rt->rt6i_gateway, gw_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 gwa_type = ipv6_addr_type(gw_addr);
1173
1174 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
1175 struct rt6_info *grt;
1176
1177 /* IPv6 strictly inhibits using not link-local
1178 addresses as nexthop address.
1179 Otherwise, router will not able to send redirects.
1180 It is very good, but in some (rare!) circumstances
1181 (SIT, PtP, NBMA NOARP links) it is handy to allow
1182 some exceptions. --ANK
1183 */
1184 err = -EINVAL;
1185 if (!(gwa_type&IPV6_ADDR_UNICAST))
1186 goto out;
1187
Daniel Lezcano55786892008-03-04 13:47:47 -08001188 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
1190 err = -EHOSTUNREACH;
1191 if (grt == NULL)
1192 goto out;
1193 if (dev) {
1194 if (dev != grt->rt6i_dev) {
1195 dst_release(&grt->u.dst);
1196 goto out;
1197 }
1198 } else {
1199 dev = grt->rt6i_dev;
1200 idev = grt->rt6i_idev;
1201 dev_hold(dev);
1202 in6_dev_hold(grt->rt6i_idev);
1203 }
1204 if (!(grt->rt6i_flags&RTF_GATEWAY))
1205 err = 0;
1206 dst_release(&grt->u.dst);
1207
1208 if (err)
1209 goto out;
1210 }
1211 err = -EINVAL;
1212 if (dev == NULL || (dev->flags&IFF_LOOPBACK))
1213 goto out;
1214 }
1215
1216 err = -ENODEV;
1217 if (dev == NULL)
1218 goto out;
1219
Thomas Graf86872cb2006-08-22 00:01:08 -07001220 if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);
1222 if (IS_ERR(rt->rt6i_nexthop)) {
1223 err = PTR_ERR(rt->rt6i_nexthop);
1224 rt->rt6i_nexthop = NULL;
1225 goto out;
1226 }
1227 }
1228
Thomas Graf86872cb2006-08-22 00:01:08 -07001229 rt->rt6i_flags = cfg->fc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230
1231install_route:
Thomas Graf86872cb2006-08-22 00:01:08 -07001232 if (cfg->fc_mx) {
1233 struct nlattr *nla;
1234 int remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
Thomas Graf86872cb2006-08-22 00:01:08 -07001236 nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
Thomas Graf8f4c1f92007-09-12 14:44:36 +02001237 int type = nla_type(nla);
Thomas Graf86872cb2006-08-22 00:01:08 -07001238
1239 if (type) {
1240 if (type > RTAX_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 err = -EINVAL;
1242 goto out;
1243 }
Thomas Graf86872cb2006-08-22 00:01:08 -07001244
1245 rt->u.dst.metrics[type - 1] = nla_get_u32(nla);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 }
1248 }
1249
Satoru SATOH5ffc02a2008-05-04 22:14:42 -07001250 if (dst_metric(&rt->u.dst, RTAX_HOPLIMIT) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1;
Rami Rosen1ca615f2008-08-06 02:34:21 -07001252 if (!dst_mtu(&rt->u.dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(dev);
Satoru SATOH5ffc02a2008-05-04 22:14:42 -07001254 if (!dst_metric(&rt->u.dst, RTAX_ADVMSS))
Daniel Lezcano55786892008-03-04 13:47:47 -08001255 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, dst_mtu(&rt->u.dst));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 rt->u.dst.dev = dev;
1257 rt->rt6i_idev = idev;
Thomas Grafc71099a2006-08-04 23:20:06 -07001258 rt->rt6i_table = table;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001259
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001260 cfg->fc_nlinfo.nl_net = dev_net(dev);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001261
Thomas Graf86872cb2006-08-22 00:01:08 -07001262 return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264out:
1265 if (dev)
1266 dev_put(dev);
1267 if (idev)
1268 in6_dev_put(idev);
1269 if (rt)
YOSHIFUJI Hideaki40aa7b92006-10-19 13:50:09 +09001270 dst_free(&rt->u.dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 return err;
1272}
1273
Thomas Graf86872cb2006-08-22 00:01:08 -07001274static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275{
1276 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001277 struct fib6_table *table;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001278 struct net *net = dev_net(rt->rt6i_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001280 if (rt == net->ipv6.ip6_null_entry)
Patrick McHardy6c813a72006-08-06 22:22:47 -07001281 return -ENOENT;
1282
Thomas Grafc71099a2006-08-04 23:20:06 -07001283 table = rt->rt6i_table;
1284 write_lock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
Thomas Graf86872cb2006-08-22 00:01:08 -07001286 err = fib6_del(rt, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 dst_release(&rt->u.dst);
1288
Thomas Grafc71099a2006-08-04 23:20:06 -07001289 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
1291 return err;
1292}
1293
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001294int ip6_del_rt(struct rt6_info *rt)
1295{
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001296 struct nl_info info = {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001297 .nl_net = dev_net(rt->rt6i_dev),
Denis V. Lunev4d1169c2008-01-10 03:26:13 -08001298 };
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001299 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001300}
1301
Thomas Graf86872cb2006-08-22 00:01:08 -07001302static int ip6_route_del(struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303{
Thomas Grafc71099a2006-08-04 23:20:06 -07001304 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 struct fib6_node *fn;
1306 struct rt6_info *rt;
1307 int err = -ESRCH;
1308
Daniel Lezcano55786892008-03-04 13:47:47 -08001309 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
Thomas Grafc71099a2006-08-04 23:20:06 -07001310 if (table == NULL)
1311 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312
Thomas Grafc71099a2006-08-04 23:20:06 -07001313 read_lock_bh(&table->tb6_lock);
1314
1315 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07001316 &cfg->fc_dst, cfg->fc_dst_len,
1317 &cfg->fc_src, cfg->fc_src_len);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001318
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 if (fn) {
Eric Dumazet7cc48262007-02-09 16:22:57 -08001320 for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001321 if (cfg->fc_ifindex &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 (rt->rt6i_dev == NULL ||
Thomas Graf86872cb2006-08-22 00:01:08 -07001323 rt->rt6i_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001325 if (cfg->fc_flags & RTF_GATEWAY &&
1326 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07001328 if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 continue;
1330 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001331 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
Thomas Graf86872cb2006-08-22 00:01:08 -07001333 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 }
1335 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001336 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 return err;
1339}
1340
1341/*
1342 * Handle redirects
1343 */
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001344struct ip6rd_flowi {
1345 struct flowi fl;
1346 struct in6_addr gateway;
1347};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001349static struct rt6_info *__ip6_route_redirect(struct net *net,
1350 struct fib6_table *table,
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001351 struct flowi *fl,
1352 int flags)
1353{
1354 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl;
1355 struct rt6_info *rt;
1356 struct fib6_node *fn;
Thomas Grafc71099a2006-08-04 23:20:06 -07001357
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 /*
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001359 * Get the "current" route for this destination and
1360 * check if the redirect has come from approriate router.
1361 *
1362 * RFC 2461 specifies that redirects should only be
1363 * accepted if they come from the nexthop to the target.
1364 * Due to the way the routes are chosen, this notion
1365 * is a bit fuzzy and one might need to check all possible
1366 * routes.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Thomas Grafc71099a2006-08-04 23:20:06 -07001369 read_lock_bh(&table->tb6_lock);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001370 fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001371restart:
Eric Dumazet7cc48262007-02-09 16:22:57 -08001372 for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001373 /*
1374 * Current route is on-link; redirect is always invalid.
1375 *
1376 * Seems, previous statement is not true. It could
1377 * be node, which looks for us as on-link (f.e. proxy ndisc)
1378 * But then router serving it might decide, that we should
1379 * know truth 8)8) --ANK (980726).
1380 */
1381 if (rt6_check_expired(rt))
1382 continue;
1383 if (!(rt->rt6i_flags & RTF_GATEWAY))
1384 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001385 if (fl->oif != rt->rt6i_dev->ifindex)
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001386 continue;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001387 if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001388 continue;
1389 break;
1390 }
YOSHIFUJI Hideakie843b9e2006-03-20 17:07:49 -08001391
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001392 if (!rt)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001393 rt = net->ipv6.ip6_null_entry;
1394 BACKTRACK(net, &fl->fl6_src);
YOSHIFUJI Hideakicb15d9c2006-08-23 17:23:11 -07001395out:
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001396 dst_hold(&rt->u.dst);
1397
1398 read_unlock_bh(&table->tb6_lock);
1399
1400 return rt;
1401};
1402
1403static struct rt6_info *ip6_route_redirect(struct in6_addr *dest,
1404 struct in6_addr *src,
1405 struct in6_addr *gateway,
1406 struct net_device *dev)
1407{
Thomas Grafadaa70b2006-10-13 15:01:03 -07001408 int flags = RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001409 struct net *net = dev_net(dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001410 struct ip6rd_flowi rdfl = {
1411 .fl = {
1412 .oif = dev->ifindex,
1413 .nl_u = {
1414 .ip6_u = {
1415 .daddr = *dest,
1416 .saddr = *src,
1417 },
1418 },
1419 },
1420 .gateway = *gateway,
1421 };
Thomas Grafadaa70b2006-10-13 15:01:03 -07001422
1423 if (rt6_need_strict(dest))
1424 flags |= RT6_LOOKUP_F_IFACE;
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001425
Daniel Lezcano55786892008-03-04 13:47:47 -08001426 return (struct rt6_info *)fib6_rule_lookup(net, (struct flowi *)&rdfl,
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001427 flags, __ip6_route_redirect);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001428}
1429
1430void rt6_redirect(struct in6_addr *dest, struct in6_addr *src,
1431 struct in6_addr *saddr,
1432 struct neighbour *neigh, u8 *lladdr, int on_link)
1433{
1434 struct rt6_info *rt, *nrt = NULL;
1435 struct netevent_redirect netevent;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001436 struct net *net = dev_net(neigh->dev);
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001437
1438 rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
1439
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001440 if (rt == net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 if (net_ratelimit())
1442 printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
1443 "for redirect target\n");
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07001444 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 }
1446
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 /*
1448 * We have finally decided to accept it.
1449 */
1450
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001451 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1453 NEIGH_UPDATE_F_OVERRIDE|
1454 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1455 NEIGH_UPDATE_F_ISROUTER))
1456 );
1457
1458 /*
1459 * Redirect received -> path was valid.
1460 * Look, redirects are sent only in response to data packets,
1461 * so that this nexthop apparently is reachable. --ANK
1462 */
1463 dst_confirm(&rt->u.dst);
1464
1465 /* Duplicate redirect: silently ignore. */
1466 if (neigh == rt->u.dst.neighbour)
1467 goto out;
1468
1469 nrt = ip6_rt_copy(rt);
1470 if (nrt == NULL)
1471 goto out;
1472
1473 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
1474 if (on_link)
1475 nrt->rt6i_flags &= ~RTF_GATEWAY;
1476
1477 ipv6_addr_copy(&nrt->rt6i_dst.addr, dest);
1478 nrt->rt6i_dst.plen = 128;
1479 nrt->u.dst.flags |= DST_HOST;
1480
1481 ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key);
1482 nrt->rt6i_nexthop = neigh_clone(neigh);
1483 /* Reset pmtu, it may be better */
1484 nrt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(neigh->dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001485 nrt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dev_net(neigh->dev),
Daniel Lezcano55786892008-03-04 13:47:47 -08001486 dst_mtu(&nrt->u.dst));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
Thomas Graf40e22e82006-08-22 00:00:45 -07001488 if (ip6_ins_rt(nrt))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 goto out;
1490
Tom Tucker8d717402006-07-30 20:43:36 -07001491 netevent.old = &rt->u.dst;
1492 netevent.new = &nrt->u.dst;
1493 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
1494
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 if (rt->rt6i_flags&RTF_CACHE) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001496 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 return;
1498 }
1499
1500out:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001501 dst_release(&rt->u.dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 return;
1503}
1504
1505/*
1506 * Handle ICMP "packet too big" messages
1507 * i.e. Path MTU discovery
1508 */
1509
1510void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,
1511 struct net_device *dev, u32 pmtu)
1512{
1513 struct rt6_info *rt, *nrt;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001514 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 int allfrag = 0;
1516
Daniel Lezcano55786892008-03-04 13:47:47 -08001517 rt = rt6_lookup(net, daddr, saddr, dev->ifindex, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 if (rt == NULL)
1519 return;
1520
1521 if (pmtu >= dst_mtu(&rt->u.dst))
1522 goto out;
1523
1524 if (pmtu < IPV6_MIN_MTU) {
1525 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001526 * According to RFC2460, PMTU is set to the IPv6 Minimum Link
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 * MTU (1280) and a fragment header should always be included
1528 * after a node receiving Too Big message reporting PMTU is
1529 * less than the IPv6 Minimum Link MTU.
1530 */
1531 pmtu = IPV6_MIN_MTU;
1532 allfrag = 1;
1533 }
1534
1535 /* New mtu received -> path was valid.
1536 They are sent only in response to data packets,
1537 so that this nexthop apparently is reachable. --ANK
1538 */
1539 dst_confirm(&rt->u.dst);
1540
1541 /* Host route. If it is static, it would be better
1542 not to override it, but add new one, so that
1543 when cache entry will expire old pmtu
1544 would return automatically.
1545 */
1546 if (rt->rt6i_flags & RTF_CACHE) {
1547 rt->u.dst.metrics[RTAX_MTU-1] = pmtu;
1548 if (allfrag)
1549 rt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
Daniel Lezcano55786892008-03-04 13:47:47 -08001550 dst_set_expires(&rt->u.dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
1552 goto out;
1553 }
1554
1555 /* Network route.
1556 Two cases are possible:
1557 1. It is connected route. Action: COW
1558 2. It is gatewayed route or NONEXTHOP route. Action: clone it.
1559 */
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001560 if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP))
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001561 nrt = rt6_alloc_cow(rt, daddr, saddr);
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001562 else
1563 nrt = rt6_alloc_clone(rt, daddr);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001564
YOSHIFUJI Hideakid5315b52006-03-20 16:58:48 -08001565 if (nrt) {
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001566 nrt->u.dst.metrics[RTAX_MTU-1] = pmtu;
1567 if (allfrag)
1568 nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
1569
1570 /* According to RFC 1981, detecting PMTU increase shouldn't be
1571 * happened within 5 mins, the recommended timer is 10 mins.
1572 * Here this route expiration time is set to ip6_rt_mtu_expires
1573 * which is 10 mins. After 10 mins the decreased pmtu is expired
1574 * and detecting PMTU increase will be automatically happened.
1575 */
Daniel Lezcano55786892008-03-04 13:47:47 -08001576 dst_set_expires(&nrt->u.dst, net->ipv6.sysctl.ip6_rt_mtu_expires);
YOSHIFUJI Hideakia1e78362006-03-20 16:56:32 -08001577 nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
1578
Thomas Graf40e22e82006-08-22 00:00:45 -07001579 ip6_ins_rt(nrt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581out:
1582 dst_release(&rt->u.dst);
1583}
1584
1585/*
1586 * Misc support functions
1587 */
1588
1589static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
1590{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001591 struct net *net = dev_net(ort->rt6i_dev);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08001592 struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593
1594 if (rt) {
1595 rt->u.dst.input = ort->u.dst.input;
1596 rt->u.dst.output = ort->u.dst.output;
1597
1598 memcpy(rt->u.dst.metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
Ville Nuorvala22e1e4d2006-10-16 22:14:26 -07001599 rt->u.dst.error = ort->u.dst.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 rt->u.dst.dev = ort->u.dst.dev;
1601 if (rt->u.dst.dev)
1602 dev_hold(rt->u.dst.dev);
1603 rt->rt6i_idev = ort->rt6i_idev;
1604 if (rt->rt6i_idev)
1605 in6_dev_hold(rt->rt6i_idev);
1606 rt->u.dst.lastuse = jiffies;
1607 rt->rt6i_expires = 0;
1608
1609 ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
1610 rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
1611 rt->rt6i_metric = 0;
1612
1613 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
1614#ifdef CONFIG_IPV6_SUBTREES
1615 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
1616#endif
Thomas Grafc71099a2006-08-04 23:20:06 -07001617 rt->rt6i_table = ort->rt6i_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 }
1619 return rt;
1620}
1621
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001622#ifdef CONFIG_IPV6_ROUTE_INFO
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001623static struct rt6_info *rt6_get_route_info(struct net *net,
1624 struct in6_addr *prefix, int prefixlen,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001625 struct in6_addr *gwaddr, int ifindex)
1626{
1627 struct fib6_node *fn;
1628 struct rt6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07001629 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001630
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001631 table = fib6_get_table(net, RT6_TABLE_INFO);
Thomas Grafc71099a2006-08-04 23:20:06 -07001632 if (table == NULL)
1633 return NULL;
1634
1635 write_lock_bh(&table->tb6_lock);
1636 fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001637 if (!fn)
1638 goto out;
1639
Eric Dumazet7cc48262007-02-09 16:22:57 -08001640 for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001641 if (rt->rt6i_dev->ifindex != ifindex)
1642 continue;
1643 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
1644 continue;
1645 if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
1646 continue;
1647 dst_hold(&rt->u.dst);
1648 break;
1649 }
1650out:
Thomas Grafc71099a2006-08-04 23:20:06 -07001651 write_unlock_bh(&table->tb6_lock);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001652 return rt;
1653}
1654
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001655static struct rt6_info *rt6_add_route_info(struct net *net,
1656 struct in6_addr *prefix, int prefixlen,
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001657 struct in6_addr *gwaddr, int ifindex,
1658 unsigned pref)
1659{
Thomas Graf86872cb2006-08-22 00:01:08 -07001660 struct fib6_config cfg = {
1661 .fc_table = RT6_TABLE_INFO,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001662 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001663 .fc_ifindex = ifindex,
1664 .fc_dst_len = prefixlen,
1665 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
1666 RTF_UP | RTF_PREF(pref),
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001667 .fc_nlinfo.pid = 0,
1668 .fc_nlinfo.nlh = NULL,
1669 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07001670 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001671
Thomas Graf86872cb2006-08-22 00:01:08 -07001672 ipv6_addr_copy(&cfg.fc_dst, prefix);
1673 ipv6_addr_copy(&cfg.fc_gateway, gwaddr);
1674
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08001675 /* We should treat it as a default route if prefix length is 0. */
1676 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07001677 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001678
Thomas Graf86872cb2006-08-22 00:01:08 -07001679 ip6_route_add(&cfg);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001680
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08001681 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001682}
1683#endif
1684
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001686{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001688 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001690 table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT);
Thomas Grafc71099a2006-08-04 23:20:06 -07001691 if (table == NULL)
1692 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
Thomas Grafc71099a2006-08-04 23:20:06 -07001694 write_lock_bh(&table->tb6_lock);
Eric Dumazet7cc48262007-02-09 16:22:57 -08001695 for (rt = table->tb6_root.leaf; rt; rt=rt->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 if (dev == rt->rt6i_dev &&
YOSHIFUJI Hideaki045927f2006-03-20 17:00:48 -08001697 ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 ipv6_addr_equal(&rt->rt6i_gateway, addr))
1699 break;
1700 }
1701 if (rt)
1702 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001703 write_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 return rt;
1705}
1706
1707struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001708 struct net_device *dev,
1709 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710{
Thomas Graf86872cb2006-08-22 00:01:08 -07001711 struct fib6_config cfg = {
1712 .fc_table = RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08001713 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07001714 .fc_ifindex = dev->ifindex,
1715 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
1716 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Daniel Lezcano55786892008-03-04 13:47:47 -08001717 .fc_nlinfo.pid = 0,
1718 .fc_nlinfo.nlh = NULL,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001719 .fc_nlinfo.nl_net = dev_net(dev),
Thomas Graf86872cb2006-08-22 00:01:08 -07001720 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721
Thomas Graf86872cb2006-08-22 00:01:08 -07001722 ipv6_addr_copy(&cfg.fc_gateway, gwaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723
Thomas Graf86872cb2006-08-22 00:01:08 -07001724 ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 return rt6_get_dflt_router(gwaddr, dev);
1727}
1728
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001729void rt6_purge_dflt_routers(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730{
1731 struct rt6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001732 struct fib6_table *table;
1733
1734 /* NOTE: Keep consistent with rt6_get_dflt_router */
Daniel Lezcano7b4da532008-03-04 13:47:14 -08001735 table = fib6_get_table(net, RT6_TABLE_DFLT);
Thomas Grafc71099a2006-08-04 23:20:06 -07001736 if (table == NULL)
1737 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
1739restart:
Thomas Grafc71099a2006-08-04 23:20:06 -07001740 read_lock_bh(&table->tb6_lock);
Eric Dumazet7cc48262007-02-09 16:22:57 -08001741 for (rt = table->tb6_root.leaf; rt; rt = rt->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
1743 dst_hold(&rt->u.dst);
Thomas Grafc71099a2006-08-04 23:20:06 -07001744 read_unlock_bh(&table->tb6_lock);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001745 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 goto restart;
1747 }
1748 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001749 read_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750}
1751
Daniel Lezcano55786892008-03-04 13:47:47 -08001752static void rtmsg_to_fib6_config(struct net *net,
1753 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07001754 struct fib6_config *cfg)
1755{
1756 memset(cfg, 0, sizeof(*cfg));
1757
1758 cfg->fc_table = RT6_TABLE_MAIN;
1759 cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
1760 cfg->fc_metric = rtmsg->rtmsg_metric;
1761 cfg->fc_expires = rtmsg->rtmsg_info;
1762 cfg->fc_dst_len = rtmsg->rtmsg_dst_len;
1763 cfg->fc_src_len = rtmsg->rtmsg_src_len;
1764 cfg->fc_flags = rtmsg->rtmsg_flags;
1765
Daniel Lezcano55786892008-03-04 13:47:47 -08001766 cfg->fc_nlinfo.nl_net = net;
Benjamin Theryf1243c22008-02-26 18:10:03 -08001767
Thomas Graf86872cb2006-08-22 00:01:08 -07001768 ipv6_addr_copy(&cfg->fc_dst, &rtmsg->rtmsg_dst);
1769 ipv6_addr_copy(&cfg->fc_src, &rtmsg->rtmsg_src);
1770 ipv6_addr_copy(&cfg->fc_gateway, &rtmsg->rtmsg_gateway);
1771}
1772
Daniel Lezcano55786892008-03-04 13:47:47 -08001773int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774{
Thomas Graf86872cb2006-08-22 00:01:08 -07001775 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 struct in6_rtmsg rtmsg;
1777 int err;
1778
1779 switch(cmd) {
1780 case SIOCADDRT: /* Add a route */
1781 case SIOCDELRT: /* Delete a route */
1782 if (!capable(CAP_NET_ADMIN))
1783 return -EPERM;
1784 err = copy_from_user(&rtmsg, arg,
1785 sizeof(struct in6_rtmsg));
1786 if (err)
1787 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07001788
Daniel Lezcano55786892008-03-04 13:47:47 -08001789 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07001790
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 rtnl_lock();
1792 switch (cmd) {
1793 case SIOCADDRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001794 err = ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795 break;
1796 case SIOCDELRT:
Thomas Graf86872cb2006-08-22 00:01:08 -07001797 err = ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 break;
1799 default:
1800 err = -EINVAL;
1801 }
1802 rtnl_unlock();
1803
1804 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001805 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
1807 return -EINVAL;
1808}
1809
1810/*
1811 * Drop the packet on the floor
1812 */
1813
Ilpo Järvinen50eb4312008-01-12 03:21:00 -08001814static int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001816 int type;
1817 switch (ipstats_mib_noroutes) {
1818 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001819 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001820 if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) {
1821 IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
1822 break;
1823 }
1824 /* FALLTHROUGH */
1825 case IPSTATS_MIB_OUTNOROUTES:
1826 IP6_INC_STATS(ip6_dst_idev(skb->dst), ipstats_mib_noroutes);
1827 break;
1828 }
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001829 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830 kfree_skb(skb);
1831 return 0;
1832}
1833
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001834static int ip6_pkt_discard(struct sk_buff *skb)
1835{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001836 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001837}
1838
Arnaldo Carvalho de Melo20380732005-08-16 02:18:02 -03001839static int ip6_pkt_discard_out(struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840{
1841 skb->dev = skb->dst->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001842 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843}
1844
David S. Miller6723ab52006-10-18 21:20:57 -07001845#ifdef CONFIG_IPV6_MULTIPLE_TABLES
1846
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001847static int ip6_pkt_prohibit(struct sk_buff *skb)
1848{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001849 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001850}
1851
1852static int ip6_pkt_prohibit_out(struct sk_buff *skb)
1853{
1854 skb->dev = skb->dst->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07001855 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07001856}
1857
David S. Miller6723ab52006-10-18 21:20:57 -07001858#endif
1859
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860/*
1861 * Allocate a dst for local (unicast / anycast) address.
1862 */
1863
1864struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
1865 const struct in6_addr *addr,
1866 int anycast)
1867{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001868 struct net *net = dev_net(idev->dev);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08001869 struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
1871 if (rt == NULL)
1872 return ERR_PTR(-ENOMEM);
1873
Daniel Lezcano55786892008-03-04 13:47:47 -08001874 dev_hold(net->loopback_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875 in6_dev_hold(idev);
1876
1877 rt->u.dst.flags = DST_HOST;
1878 rt->u.dst.input = ip6_input;
1879 rt->u.dst.output = ip6_output;
Daniel Lezcano55786892008-03-04 13:47:47 -08001880 rt->rt6i_dev = net->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 rt->rt6i_idev = idev;
1882 rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev);
Daniel Lezcano55786892008-03-04 13:47:47 -08001883 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, dst_mtu(&rt->u.dst));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1;
1885 rt->u.dst.obsolete = -1;
1886
1887 rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
YOSHIFUJI Hideaki58c4fb82005-12-21 22:56:42 +09001888 if (anycast)
1889 rt->rt6i_flags |= RTF_ANYCAST;
1890 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 rt->rt6i_flags |= RTF_LOCAL;
1892 rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
1893 if (rt->rt6i_nexthop == NULL) {
YOSHIFUJI Hideaki40aa7b92006-10-19 13:50:09 +09001894 dst_free(&rt->u.dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 return ERR_PTR(-ENOMEM);
1896 }
1897
1898 ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
1899 rt->rt6i_dst.plen = 128;
Daniel Lezcano55786892008-03-04 13:47:47 -08001900 rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901
1902 atomic_set(&rt->u.dst.__refcnt, 1);
1903
1904 return rt;
1905}
1906
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001907struct arg_dev_net {
1908 struct net_device *dev;
1909 struct net *net;
1910};
1911
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912static int fib6_ifdown(struct rt6_info *rt, void *arg)
1913{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001914 struct net_device *dev = ((struct arg_dev_net *)arg)->dev;
1915 struct net *net = ((struct arg_dev_net *)arg)->net;
1916
1917 if (((void *)rt->rt6i_dev == dev || dev == NULL) &&
1918 rt != net->ipv6.ip6_null_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 RT6_TRACE("deleted by ifdown %p\n", rt);
1920 return -1;
1921 }
1922 return 0;
1923}
1924
Daniel Lezcanof3db4852008-03-03 23:27:06 -08001925void rt6_ifdown(struct net *net, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001927 struct arg_dev_net adn = {
1928 .dev = dev,
1929 .net = net,
1930 };
1931
1932 fib6_clean_all(net, fib6_ifdown, 0, &adn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933}
1934
1935struct rt6_mtu_change_arg
1936{
1937 struct net_device *dev;
1938 unsigned mtu;
1939};
1940
1941static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
1942{
1943 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
1944 struct inet6_dev *idev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001945 struct net *net = dev_net(arg->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946
1947 /* In IPv6 pmtu discovery is not optional,
1948 so that RTAX_MTU lock cannot disable it.
1949 We still use this lock to block changes
1950 caused by addrconf/ndisc.
1951 */
1952
1953 idev = __in6_dev_get(arg->dev);
1954 if (idev == NULL)
1955 return 0;
1956
1957 /* For administrative MTU increase, there is no way to discover
1958 IPv6 PMTU increase, so PMTU increase should be updated here.
1959 Since RFC 1981 doesn't include administrative MTU increase
1960 update PMTU increase is a MUST. (i.e. jumbo frame)
1961 */
1962 /*
1963 If new MTU is less than route PMTU, this new MTU will be the
1964 lowest MTU in the path, update the route PMTU to reflect PMTU
1965 decreases; if new MTU is greater than route PMTU, and the
1966 old MTU is the lowest MTU in the path, update the route PMTU
1967 to reflect the increase. In this case if the other nodes' MTU
1968 also have the lowest MTU, TOO BIG MESSAGE will be lead to
1969 PMTU discouvery.
1970 */
1971 if (rt->rt6i_dev == arg->dev &&
1972 !dst_metric_locked(&rt->u.dst, RTAX_MTU) &&
Jim Paris23717792008-01-31 16:36:25 -08001973 (dst_mtu(&rt->u.dst) >= arg->mtu ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001974 (dst_mtu(&rt->u.dst) < arg->mtu &&
Simon Arlott566cfd82007-07-26 00:09:55 -07001975 dst_mtu(&rt->u.dst) == idev->cnf.mtu6))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 rt->u.dst.metrics[RTAX_MTU-1] = arg->mtu;
Daniel Lezcano55786892008-03-04 13:47:47 -08001977 rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(net, arg->mtu);
Simon Arlott566cfd82007-07-26 00:09:55 -07001978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 return 0;
1980}
1981
1982void rt6_mtu_change(struct net_device *dev, unsigned mtu)
1983{
Thomas Grafc71099a2006-08-04 23:20:06 -07001984 struct rt6_mtu_change_arg arg = {
1985 .dev = dev,
1986 .mtu = mtu,
1987 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001989 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990}
1991
Patrick McHardyef7c79e2007-06-05 12:38:30 -07001992static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07001993 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07001994 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07001995 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07001996 [RTA_PRIORITY] = { .type = NLA_U32 },
1997 [RTA_METRICS] = { .type = NLA_NESTED },
1998};
1999
2000static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2001 struct fib6_config *cfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002{
Thomas Graf86872cb2006-08-22 00:01:08 -07002003 struct rtmsg *rtm;
2004 struct nlattr *tb[RTA_MAX+1];
2005 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006
Thomas Graf86872cb2006-08-22 00:01:08 -07002007 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2008 if (err < 0)
2009 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010
Thomas Graf86872cb2006-08-22 00:01:08 -07002011 err = -EINVAL;
2012 rtm = nlmsg_data(nlh);
2013 memset(cfg, 0, sizeof(*cfg));
2014
2015 cfg->fc_table = rtm->rtm_table;
2016 cfg->fc_dst_len = rtm->rtm_dst_len;
2017 cfg->fc_src_len = rtm->rtm_src_len;
2018 cfg->fc_flags = RTF_UP;
2019 cfg->fc_protocol = rtm->rtm_protocol;
2020
2021 if (rtm->rtm_type == RTN_UNREACHABLE)
2022 cfg->fc_flags |= RTF_REJECT;
2023
2024 cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid;
2025 cfg->fc_nlinfo.nlh = nlh;
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002026 cfg->fc_nlinfo.nl_net = sock_net(skb->sk);
Thomas Graf86872cb2006-08-22 00:01:08 -07002027
2028 if (tb[RTA_GATEWAY]) {
2029 nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16);
2030 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002032
2033 if (tb[RTA_DST]) {
2034 int plen = (rtm->rtm_dst_len + 7) >> 3;
2035
2036 if (nla_len(tb[RTA_DST]) < plen)
2037 goto errout;
2038
2039 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002041
2042 if (tb[RTA_SRC]) {
2043 int plen = (rtm->rtm_src_len + 7) >> 3;
2044
2045 if (nla_len(tb[RTA_SRC]) < plen)
2046 goto errout;
2047
2048 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002050
2051 if (tb[RTA_OIF])
2052 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
2053
2054 if (tb[RTA_PRIORITY])
2055 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
2056
2057 if (tb[RTA_METRICS]) {
2058 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
2059 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 }
Thomas Graf86872cb2006-08-22 00:01:08 -07002061
2062 if (tb[RTA_TABLE])
2063 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
2064
2065 err = 0;
2066errout:
2067 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068}
2069
Thomas Grafc127ea22007-03-22 11:58:32 -07002070static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071{
Thomas Graf86872cb2006-08-22 00:01:08 -07002072 struct fib6_config cfg;
2073 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074
Thomas Graf86872cb2006-08-22 00:01:08 -07002075 err = rtm_to_fib6_config(skb, nlh, &cfg);
2076 if (err < 0)
2077 return err;
2078
2079 return ip6_route_del(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080}
2081
Thomas Grafc127ea22007-03-22 11:58:32 -07002082static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083{
Thomas Graf86872cb2006-08-22 00:01:08 -07002084 struct fib6_config cfg;
2085 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086
Thomas Graf86872cb2006-08-22 00:01:08 -07002087 err = rtm_to_fib6_config(skb, nlh, &cfg);
2088 if (err < 0)
2089 return err;
2090
2091 return ip6_route_add(&cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092}
2093
Thomas Graf339bf982006-11-10 14:10:15 -08002094static inline size_t rt6_nlmsg_size(void)
2095{
2096 return NLMSG_ALIGN(sizeof(struct rtmsg))
2097 + nla_total_size(16) /* RTA_SRC */
2098 + nla_total_size(16) /* RTA_DST */
2099 + nla_total_size(16) /* RTA_GATEWAY */
2100 + nla_total_size(16) /* RTA_PREFSRC */
2101 + nla_total_size(4) /* RTA_TABLE */
2102 + nla_total_size(4) /* RTA_IIF */
2103 + nla_total_size(4) /* RTA_OIF */
2104 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08002105 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Thomas Graf339bf982006-11-10 14:10:15 -08002106 + nla_total_size(sizeof(struct rta_cacheinfo));
2107}
2108
Brian Haley191cd582008-08-14 15:33:21 -07002109static int rt6_fill_node(struct net *net,
2110 struct sk_buff *skb, struct rt6_info *rt,
Jamal Hadi Salim0d51aa82005-06-21 13:51:04 -07002111 struct in6_addr *dst, struct in6_addr *src,
2112 int iif, int type, u32 pid, u32 seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002113 int prefix, int nowait, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114{
2115 struct rtmsg *rtm;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002116 struct nlmsghdr *nlh;
Thomas Grafe3703b32006-11-27 09:27:07 -08002117 long expires;
Patrick McHardy9e762a42006-08-10 23:09:48 -07002118 u32 table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
2120 if (prefix) { /* user wants prefix routes only */
2121 if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
2122 /* success since this is not a prefix route */
2123 return 1;
2124 }
2125 }
2126
Thomas Graf2d7202b2006-08-22 00:01:27 -07002127 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
2128 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08002129 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002130
2131 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 rtm->rtm_family = AF_INET6;
2133 rtm->rtm_dst_len = rt->rt6i_dst.plen;
2134 rtm->rtm_src_len = rt->rt6i_src.plen;
2135 rtm->rtm_tos = 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07002136 if (rt->rt6i_table)
Patrick McHardy9e762a42006-08-10 23:09:48 -07002137 table = rt->rt6i_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07002138 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07002139 table = RT6_TABLE_UNSPEC;
2140 rtm->rtm_table = table;
Thomas Graf2d7202b2006-08-22 00:01:27 -07002141 NLA_PUT_U32(skb, RTA_TABLE, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 if (rt->rt6i_flags&RTF_REJECT)
2143 rtm->rtm_type = RTN_UNREACHABLE;
2144 else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK))
2145 rtm->rtm_type = RTN_LOCAL;
2146 else
2147 rtm->rtm_type = RTN_UNICAST;
2148 rtm->rtm_flags = 0;
2149 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2150 rtm->rtm_protocol = rt->rt6i_protocol;
2151 if (rt->rt6i_flags&RTF_DYNAMIC)
2152 rtm->rtm_protocol = RTPROT_REDIRECT;
2153 else if (rt->rt6i_flags & RTF_ADDRCONF)
2154 rtm->rtm_protocol = RTPROT_KERNEL;
2155 else if (rt->rt6i_flags&RTF_DEFAULT)
2156 rtm->rtm_protocol = RTPROT_RA;
2157
2158 if (rt->rt6i_flags&RTF_CACHE)
2159 rtm->rtm_flags |= RTM_F_CLONED;
2160
2161 if (dst) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002162 NLA_PUT(skb, RTA_DST, 16, dst);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002163 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 } else if (rtm->rtm_dst_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002165 NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166#ifdef CONFIG_IPV6_SUBTREES
2167 if (src) {
Thomas Graf2d7202b2006-08-22 00:01:27 -07002168 NLA_PUT(skb, RTA_SRC, 16, src);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002169 rtm->rtm_src_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 } else if (rtm->rtm_src_len)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002171 NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002173 if (iif) {
2174#ifdef CONFIG_IPV6_MROUTE
2175 if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
2176 int err = ip6mr_get_route(skb, rtm, nowait);
2177 if (err <= 0) {
2178 if (!nowait) {
2179 if (err == 0)
2180 return 0;
2181 goto nla_put_failure;
2182 } else {
2183 if (err == -EMSGSIZE)
2184 goto nla_put_failure;
2185 }
2186 }
2187 } else
2188#endif
2189 NLA_PUT_U32(skb, RTA_IIF, iif);
2190 } else if (dst) {
Brian Haley5e0115e2008-08-13 01:58:57 -07002191 struct inet6_dev *idev = ip6_dst_idev(&rt->u.dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 struct in6_addr saddr_buf;
Brian Haley191cd582008-08-14 15:33:21 -07002193 if (ipv6_dev_get_saddr(net, idev ? idev->dev : NULL,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +09002194 dst, 0, &saddr_buf) == 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002195 NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07002197
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 if (rtnetlink_put_metrics(skb, rt->u.dst.metrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002199 goto nla_put_failure;
2200
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 if (rt->u.dst.neighbour)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002202 NLA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
2203
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204 if (rt->u.dst.dev)
Thomas Graf2d7202b2006-08-22 00:01:27 -07002205 NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
2206
2207 NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
Thomas Grafe3703b32006-11-27 09:27:07 -08002208
YOSHIFUJI Hideaki36e3dea2008-05-13 02:52:55 +09002209 if (!(rt->rt6i_flags & RTF_EXPIRES))
2210 expires = 0;
2211 else if (rt->rt6i_expires - jiffies < INT_MAX)
2212 expires = rt->rt6i_expires - jiffies;
2213 else
2214 expires = INT_MAX;
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07002215
Thomas Grafe3703b32006-11-27 09:27:07 -08002216 if (rtnl_put_cacheinfo(skb, &rt->u.dst, 0, 0, 0,
2217 expires, rt->u.dst.error) < 0)
2218 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219
Thomas Graf2d7202b2006-08-22 00:01:27 -07002220 return nlmsg_end(skb, nlh);
2221
2222nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08002223 nlmsg_cancel(skb, nlh);
2224 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225}
2226
Patrick McHardy1b43af52006-08-10 23:11:17 -07002227int rt6_dump_route(struct rt6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228{
2229 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
2230 int prefix;
2231
Thomas Graf2d7202b2006-08-22 00:01:27 -07002232 if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
2233 struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
2235 } else
2236 prefix = 0;
2237
Brian Haley191cd582008-08-14 15:33:21 -07002238 return rt6_fill_node(arg->net,
2239 arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002241 prefix, 0, NLM_F_MULTI);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242}
2243
Thomas Grafc127ea22007-03-22 11:58:32 -07002244static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09002246 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07002247 struct nlattr *tb[RTA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07002249 struct sk_buff *skb;
2250 struct rtmsg *rtm;
2251 struct flowi fl;
2252 int err, iif = 0;
2253
2254 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
2255 if (err < 0)
2256 goto errout;
2257
2258 err = -EINVAL;
2259 memset(&fl, 0, sizeof(fl));
2260
2261 if (tb[RTA_SRC]) {
2262 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
2263 goto errout;
2264
2265 ipv6_addr_copy(&fl.fl6_src, nla_data(tb[RTA_SRC]));
2266 }
2267
2268 if (tb[RTA_DST]) {
2269 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
2270 goto errout;
2271
2272 ipv6_addr_copy(&fl.fl6_dst, nla_data(tb[RTA_DST]));
2273 }
2274
2275 if (tb[RTA_IIF])
2276 iif = nla_get_u32(tb[RTA_IIF]);
2277
2278 if (tb[RTA_OIF])
2279 fl.oif = nla_get_u32(tb[RTA_OIF]);
2280
2281 if (iif) {
2282 struct net_device *dev;
Daniel Lezcano55786892008-03-04 13:47:47 -08002283 dev = __dev_get_by_index(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07002284 if (!dev) {
2285 err = -ENODEV;
2286 goto errout;
2287 }
2288 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289
2290 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
Thomas Grafab364a62006-08-22 00:01:47 -07002291 if (skb == NULL) {
2292 err = -ENOBUFS;
2293 goto errout;
2294 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
2296 /* Reserve room for dummy headers, this skb can pass
2297 through good chunk of routing engine.
2298 */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07002299 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
2301
Daniel Lezcano8a3edd82008-03-07 11:14:16 -08002302 rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 skb->dst = &rt->u.dst;
2304
Brian Haley191cd582008-08-14 15:33:21 -07002305 err = rt6_fill_node(net, skb, rt, &fl.fl6_dst, &fl.fl6_src, iif,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002307 nlh->nlmsg_seq, 0, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07002309 kfree_skb(skb);
2310 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311 }
2312
Daniel Lezcano55786892008-03-04 13:47:47 -08002313 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid);
Thomas Grafab364a62006-08-22 00:01:47 -07002314errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316}
2317
Thomas Graf86872cb2006-08-22 00:01:08 -07002318void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319{
2320 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08002321 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002322 u32 seq;
2323 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08002325 err = -ENOBUFS;
2326 seq = info->nlh != NULL ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07002327
Thomas Graf339bf982006-11-10 14:10:15 -08002328 skb = nlmsg_new(rt6_nlmsg_size(), gfp_any());
Thomas Graf21713eb2006-08-15 00:35:24 -07002329 if (skb == NULL)
2330 goto errout;
2331
Brian Haley191cd582008-08-14 15:33:21 -07002332 err = rt6_fill_node(net, skb, rt, NULL, NULL, 0,
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09002333 event, info->pid, seq, 0, 0, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08002334 if (err < 0) {
2335 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
2336 WARN_ON(err == -EMSGSIZE);
2337 kfree_skb(skb);
2338 goto errout;
2339 }
Daniel Lezcano55786892008-03-04 13:47:47 -08002340 err = rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
2341 info->nlh, gfp_any());
Thomas Graf21713eb2006-08-15 00:35:24 -07002342errout:
2343 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08002344 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345}
2346
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002347static int ip6_route_dev_notify(struct notifier_block *this,
2348 unsigned long event, void *data)
2349{
2350 struct net_device *dev = (struct net_device *)data;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002351 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002352
2353 if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
2354 net->ipv6.ip6_null_entry->u.dst.dev = dev;
2355 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
2356#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2357 net->ipv6.ip6_prohibit_entry->u.dst.dev = dev;
2358 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
2359 net->ipv6.ip6_blk_hole_entry->u.dst.dev = dev;
2360 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
2361#endif
2362 }
2363
2364 return NOTIFY_OK;
2365}
2366
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367/*
2368 * /proc
2369 */
2370
2371#ifdef CONFIG_PROC_FS
2372
2373#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
2374
2375struct rt6_proc_arg
2376{
2377 char *buffer;
2378 int offset;
2379 int length;
2380 int skip;
2381 int len;
2382};
2383
2384static int rt6_info_route(struct rt6_info *rt, void *p_arg)
2385{
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002386 struct seq_file *m = p_arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002388 seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_dst.addr),
2389 rt->rt6i_dst.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390
2391#ifdef CONFIG_IPV6_SUBTREES
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002392 seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_src.addr),
2393 rt->rt6i_src.plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394#else
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002395 seq_puts(m, "00000000000000000000000000000000 00 ");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396#endif
2397
2398 if (rt->rt6i_nexthop) {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002399 seq_printf(m, NIP6_SEQFMT,
2400 NIP6(*((struct in6_addr *)rt->rt6i_nexthop->primary_key)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 } else {
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002402 seq_puts(m, "00000000000000000000000000000000");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403 }
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002404 seq_printf(m, " %08x %08x %08x %08x %8s\n",
2405 rt->rt6i_metric, atomic_read(&rt->u.dst.__refcnt),
2406 rt->u.dst.__use, rt->rt6i_flags,
2407 rt->rt6i_dev ? rt->rt6i_dev->name : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408 return 0;
2409}
2410
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002411static int ipv6_route_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412{
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002413 struct net *net = (struct net *)m->private;
2414 fib6_clean_all(net, rt6_info_route, 0, m);
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002415 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416}
2417
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002418static int ipv6_route_open(struct inode *inode, struct file *file)
2419{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002420 return single_open_net(inode, file, ipv6_route_show);
Daniel Lezcanof3db4852008-03-03 23:27:06 -08002421}
2422
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002423static const struct file_operations ipv6_route_proc_fops = {
2424 .owner = THIS_MODULE,
2425 .open = ipv6_route_open,
2426 .read = seq_read,
2427 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002428 .release = single_release_net,
Alexey Dobriyan33120b32007-11-06 05:27:11 -08002429};
2430
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431static int rt6_stats_seq_show(struct seq_file *seq, void *v)
2432{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002433 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002435 net->ipv6.rt6_stats->fib_nodes,
2436 net->ipv6.rt6_stats->fib_route_nodes,
2437 net->ipv6.rt6_stats->fib_rt_alloc,
2438 net->ipv6.rt6_stats->fib_rt_entries,
2439 net->ipv6.rt6_stats->fib_rt_cache,
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002440 atomic_read(&net->ipv6.ip6_dst_ops->entries),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002441 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442
2443 return 0;
2444}
2445
2446static int rt6_stats_seq_open(struct inode *inode, struct file *file)
2447{
Pavel Emelyanovde05c552008-07-18 04:07:21 -07002448 return single_open_net(inode, file, rt6_stats_seq_show);
Daniel Lezcano69ddb802008-03-04 13:46:23 -08002449}
2450
Arjan van de Ven9a321442007-02-12 00:55:35 -08002451static const struct file_operations rt6_stats_seq_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 .owner = THIS_MODULE,
2453 .open = rt6_stats_seq_open,
2454 .read = seq_read,
2455 .llseek = seq_lseek,
Pavel Emelyanovb6fcbdb2008-07-18 04:07:44 -07002456 .release = single_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457};
2458#endif /* CONFIG_PROC_FS */
2459
2460#ifdef CONFIG_SYSCTL
2461
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462static
2463int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
2464 void __user *buffer, size_t *lenp, loff_t *ppos)
2465{
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08002466 struct net *net = current->nsproxy->net_ns;
2467 int delay = net->ipv6.sysctl.flush_delay;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468 if (write) {
2469 proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08002470 fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471 return 0;
2472 } else
2473 return -EINVAL;
2474}
2475
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002476ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002477 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08002479 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07002481 .mode = 0200,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002482 .proc_handler = &ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483 },
2484 {
2485 .ctl_name = NET_IPV6_ROUTE_GC_THRESH,
2486 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002487 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 .maxlen = sizeof(int),
2489 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002490 .proc_handler = &proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 },
2492 {
2493 .ctl_name = NET_IPV6_ROUTE_MAX_SIZE,
2494 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08002495 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 .maxlen = sizeof(int),
2497 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002498 .proc_handler = &proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 },
2500 {
2501 .ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL,
2502 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002503 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 .maxlen = sizeof(int),
2505 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002506 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 .strategy = &sysctl_jiffies,
2508 },
2509 {
2510 .ctl_name = NET_IPV6_ROUTE_GC_TIMEOUT,
2511 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08002512 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
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_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516 .strategy = &sysctl_jiffies,
2517 },
2518 {
2519 .ctl_name = NET_IPV6_ROUTE_GC_INTERVAL,
2520 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08002521 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 .maxlen = sizeof(int),
2523 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002524 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 .strategy = &sysctl_jiffies,
2526 },
2527 {
2528 .ctl_name = NET_IPV6_ROUTE_GC_ELASTICITY,
2529 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08002530 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 .maxlen = sizeof(int),
2532 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002533 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 .strategy = &sysctl_jiffies,
2535 },
2536 {
2537 .ctl_name = NET_IPV6_ROUTE_MTU_EXPIRES,
2538 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08002539 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540 .maxlen = sizeof(int),
2541 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002542 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 .strategy = &sysctl_jiffies,
2544 },
2545 {
2546 .ctl_name = NET_IPV6_ROUTE_MIN_ADVMSS,
2547 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08002548 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 .maxlen = sizeof(int),
2550 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002551 .proc_handler = &proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 .strategy = &sysctl_jiffies,
2553 },
2554 {
2555 .ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
2556 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08002557 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 .maxlen = sizeof(int),
2559 .mode = 0644,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002560 .proc_handler = &proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561 .strategy = &sysctl_ms_jiffies,
2562 },
2563 { .ctl_name = 0 }
2564};
2565
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002566struct ctl_table *ipv6_route_sysctl_init(struct net *net)
2567{
2568 struct ctl_table *table;
2569
2570 table = kmemdup(ipv6_route_table_template,
2571 sizeof(ipv6_route_table_template),
2572 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002573
2574 if (table) {
2575 table[0].data = &net->ipv6.sysctl.flush_delay;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002576 table[1].data = &net->ipv6.ip6_dst_ops->gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09002577 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
2578 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
2579 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
2580 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
2581 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
2582 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
2583 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
2584 }
2585
Daniel Lezcano760f2d02008-01-10 02:53:43 -08002586 return table;
2587}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588#endif
2589
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002590static int ip6_route_net_init(struct net *net)
2591{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07002592 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002593
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002594 net->ipv6.ip6_dst_ops = kmemdup(&ip6_dst_ops_template,
2595 sizeof(*net->ipv6.ip6_dst_ops),
2596 GFP_KERNEL);
2597 if (!net->ipv6.ip6_dst_ops)
2598 goto out;
Denis V. Lunev48115be2008-04-16 02:01:34 -07002599 net->ipv6.ip6_dst_ops->dst_net = hold_net(net);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002600
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002601 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
2602 sizeof(*net->ipv6.ip6_null_entry),
2603 GFP_KERNEL);
2604 if (!net->ipv6.ip6_null_entry)
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002605 goto out_ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002606 net->ipv6.ip6_null_entry->u.dst.path =
2607 (struct dst_entry *)net->ipv6.ip6_null_entry;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002608 net->ipv6.ip6_null_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002609
2610#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2611 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
2612 sizeof(*net->ipv6.ip6_prohibit_entry),
2613 GFP_KERNEL);
2614 if (!net->ipv6.ip6_prohibit_entry) {
2615 kfree(net->ipv6.ip6_null_entry);
2616 goto out;
2617 }
2618 net->ipv6.ip6_prohibit_entry->u.dst.path =
2619 (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002620 net->ipv6.ip6_prohibit_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002621
2622 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
2623 sizeof(*net->ipv6.ip6_blk_hole_entry),
2624 GFP_KERNEL);
2625 if (!net->ipv6.ip6_blk_hole_entry) {
2626 kfree(net->ipv6.ip6_null_entry);
2627 kfree(net->ipv6.ip6_prohibit_entry);
2628 goto out;
2629 }
2630 net->ipv6.ip6_blk_hole_entry->u.dst.path =
2631 (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002632 net->ipv6.ip6_blk_hole_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002633#endif
2634
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002635#ifdef CONFIG_PROC_FS
2636 proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
2637 proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
2638#endif
Benjamin Thery6891a342008-03-04 13:49:47 -08002639 net->ipv6.ip6_rt_gc_expire = 30*HZ;
2640
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002641 ret = 0;
2642out:
2643 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002644
2645out_ip6_dst_ops:
Denis V. Lunev48115be2008-04-16 02:01:34 -07002646 release_net(net->ipv6.ip6_dst_ops->dst_net);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002647 kfree(net->ipv6.ip6_dst_ops);
2648 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002649}
2650
2651static void ip6_route_net_exit(struct net *net)
2652{
2653#ifdef CONFIG_PROC_FS
2654 proc_net_remove(net, "ipv6_route");
2655 proc_net_remove(net, "rt6_stats");
2656#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002657 kfree(net->ipv6.ip6_null_entry);
2658#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2659 kfree(net->ipv6.ip6_prohibit_entry);
2660 kfree(net->ipv6.ip6_blk_hole_entry);
2661#endif
Denis V. Lunev48115be2008-04-16 02:01:34 -07002662 release_net(net->ipv6.ip6_dst_ops->dst_net);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002663 kfree(net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002664}
2665
2666static struct pernet_operations ip6_route_net_ops = {
2667 .init = ip6_route_net_init,
2668 .exit = ip6_route_net_exit,
2669};
2670
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002671static struct notifier_block ip6_route_dev_notifier = {
2672 .notifier_call = ip6_route_dev_notify,
2673 .priority = 0,
2674};
2675
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002676int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002678 int ret;
2679
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08002680 ret = -ENOMEM;
2681 ip6_dst_ops_template.kmem_cachep =
2682 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
2683 SLAB_HWCACHE_ALIGN, NULL);
2684 if (!ip6_dst_ops_template.kmem_cachep)
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002685 goto out;;
David S. Miller14e50e52007-05-24 18:17:54 -07002686
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002687 ret = register_pernet_subsys(&ip6_route_net_ops);
2688 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002689 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08002690
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002691 /* Registering of the loopback is done before this portion of code,
2692 * the loopback reference in rt6_info will not be taken, do it
2693 * manually for init_net */
2694 init_net.ipv6.ip6_null_entry->u.dst.dev = init_net.loopback_dev;
2695 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2696 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
2697 init_net.ipv6.ip6_prohibit_entry->u.dst.dev = init_net.loopback_dev;
2698 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2699 init_net.ipv6.ip6_blk_hole_entry->u.dst.dev = init_net.loopback_dev;
2700 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
2701 #endif
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002702 ret = fib6_init();
2703 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002704 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002705
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002706 ret = xfrm6_init();
2707 if (ret)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002708 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08002709
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002710 ret = fib6_rules_init();
2711 if (ret)
2712 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08002713
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002714 ret = -ENOBUFS;
2715 if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL) ||
2716 __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL) ||
2717 __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL))
2718 goto fib6_rules_init;
2719
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002720 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08002721 if (ret)
2722 goto fib6_rules_init;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002723
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002724out:
2725 return ret;
2726
2727fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002728 fib6_rules_cleanup();
2729xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002730 xfrm6_fini();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002731out_fib6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002732 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002733out_register_subsys:
2734 unregister_pernet_subsys(&ip6_route_net_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002735out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002736 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08002737 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738}
2739
2740void ip6_route_cleanup(void)
2741{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002742 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Graf101367c2006-08-04 03:39:02 -07002743 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08002746 unregister_pernet_subsys(&ip6_route_net_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08002747 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748}