blob: 5dd6113c8f8ff968b43a49f205ef876a654ca056 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Wei Wang35732d02017-10-06 12:05:57 -070047#include <linux/jhash.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020048#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <net/snmp.h>
50#include <net/ipv6.h>
51#include <net/ip6_fib.h>
52#include <net/ip6_route.h>
53#include <net/ndisc.h>
54#include <net/addrconf.h>
55#include <net/tcp.h>
56#include <linux/rtnetlink.h>
57#include <net/dst.h>
Jiri Benc904af042015-08-20 13:56:31 +020058#include <net/dst_metadata.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070060#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070061#include <net/netlink.h>
Nicolas Dichtel51ebd312012-10-22 03:42:09 +000062#include <net/nexthop.h>
Roopa Prabhu19e42e42015-07-21 10:43:48 +020063#include <net/lwtunnel.h>
Jiri Benc904af042015-08-20 13:56:31 +020064#include <net/ip_tunnels.h>
David Ahernca254492015-10-12 11:47:10 -070065#include <net/l3mdev.h>
Roopa Prabhueacb9382018-05-22 14:03:28 -070066#include <net/ip.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080067#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69#ifdef CONFIG_SYSCTL
70#include <linux/sysctl.h>
71#endif
72
David Ahern30d444d2018-05-23 17:08:48 -070073static int ip6_rt_type_to_error(u8 fib6_type);
74
75#define CREATE_TRACE_POINTS
76#include <trace/events/fib6.h>
77EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
78#undef CREATE_TRACE_POINTS
79
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020080enum rt6_nud_state {
Jiri Benc7e980562013-12-11 13:48:20 +010081 RT6_NUD_FAIL_HARD = -3,
82 RT6_NUD_FAIL_PROBE = -2,
83 RT6_NUD_FAIL_DO_RR = -1,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020084 RT6_NUD_SUCCEED = 1
85};
86
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080088static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000089static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090static struct dst_entry *ip6_negative_advice(struct dst_entry *);
91static void ip6_dst_destroy(struct dst_entry *);
92static void ip6_dst_ifdown(struct dst_entry *,
93 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080094static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
96static int ip6_pkt_discard(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050097static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Kamala R7150aed2013-12-02 19:55:21 +053098static int ip6_pkt_prohibit(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050099static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100static void ip6_link_failure(struct sk_buff *skb);
David S. Miller6700c272012-07-17 03:29:28 -0700101static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
102 struct sk_buff *skb, u32 mtu);
103static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
104 struct sk_buff *skb);
David Ahern702cea52019-04-09 14:41:13 -0700105static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
106 int strict);
David Ahern8d1c8022018-04-17 17:33:26 -0700107static size_t rt6_nlmsg_size(struct fib6_info *rt);
David Ahernd4ead6b2018-04-17 17:33:16 -0700108static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -0700109 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -0700110 struct in6_addr *dest, struct in6_addr *src,
David Ahern16a16cd2017-02-02 12:37:11 -0800111 int iif, int type, u32 portid, u32 seq,
112 unsigned int flags);
David Ahern7e4b5122019-04-16 14:36:00 -0700113static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang35732d02017-10-06 12:05:57 -0700114 struct in6_addr *daddr,
115 struct in6_addr *saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800117#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -0700118static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000119 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700120 const struct in6_addr *gwaddr,
121 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +0000122 unsigned int pref);
David Ahern8d1c8022018-04-17 17:33:26 -0700123static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000124 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700125 const struct in6_addr *gwaddr,
126 struct net_device *dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800127#endif
128
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700129struct uncached_list {
130 spinlock_t lock;
131 struct list_head head;
132};
133
134static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
135
Xin Long510c3212018-02-14 19:06:02 +0800136void rt6_uncached_list_add(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700137{
138 struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
139
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700140 rt->rt6i_uncached_list = ul;
141
142 spin_lock_bh(&ul->lock);
143 list_add_tail(&rt->rt6i_uncached, &ul->head);
144 spin_unlock_bh(&ul->lock);
145}
146
Xin Long510c3212018-02-14 19:06:02 +0800147void rt6_uncached_list_del(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700148{
149 if (!list_empty(&rt->rt6i_uncached)) {
150 struct uncached_list *ul = rt->rt6i_uncached_list;
Wei Wang81eb8442017-10-06 12:06:11 -0700151 struct net *net = dev_net(rt->dst.dev);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700152
153 spin_lock_bh(&ul->lock);
154 list_del(&rt->rt6i_uncached);
Wei Wang81eb8442017-10-06 12:06:11 -0700155 atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700156 spin_unlock_bh(&ul->lock);
157 }
158}
159
160static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
161{
162 struct net_device *loopback_dev = net->loopback_dev;
163 int cpu;
164
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500165 if (dev == loopback_dev)
166 return;
167
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700168 for_each_possible_cpu(cpu) {
169 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
170 struct rt6_info *rt;
171
172 spin_lock_bh(&ul->lock);
173 list_for_each_entry(rt, &ul->head, rt6i_uncached) {
174 struct inet6_dev *rt_idev = rt->rt6i_idev;
175 struct net_device *rt_dev = rt->dst.dev;
176
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500177 if (rt_idev->dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700178 rt->rt6i_idev = in6_dev_get(loopback_dev);
179 in6_dev_put(rt_idev);
180 }
181
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500182 if (rt_dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700183 rt->dst.dev = loopback_dev;
184 dev_hold(rt->dst.dev);
185 dev_put(rt_dev);
186 }
187 }
188 spin_unlock_bh(&ul->lock);
189 }
190}
191
David Ahernf8a1b432018-04-17 17:33:21 -0700192static inline const void *choose_neigh_daddr(const struct in6_addr *p,
David S. Millerf894cbf2012-07-02 21:52:24 -0700193 struct sk_buff *skb,
194 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500195{
David S. Millera7563f32012-01-26 16:29:16 -0500196 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500197 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700198 else if (skb)
199 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500200 return daddr;
201}
202
David Ahernf8a1b432018-04-17 17:33:21 -0700203struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
204 struct net_device *dev,
205 struct sk_buff *skb,
206 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700207{
David S. Miller39232972012-01-26 15:22:32 -0500208 struct neighbour *n;
209
David Ahernf8a1b432018-04-17 17:33:21 -0700210 daddr = choose_neigh_daddr(gw, skb, daddr);
211 n = __ipv6_neigh_lookup(dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500212 if (n)
213 return n;
Stefano Brivio7adf3242019-01-02 13:29:27 +0100214
215 n = neigh_create(&nd_tbl, daddr, dev);
216 return IS_ERR(n) ? NULL : n;
David Ahernf8a1b432018-04-17 17:33:21 -0700217}
218
219static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
220 struct sk_buff *skb,
221 const void *daddr)
222{
223 const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
224
225 return ip6_neigh_lookup(&rt->rt6i_gateway, dst->dev, skb, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500226}
227
Julian Anastasov63fca652017-02-06 23:14:15 +0200228static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
229{
230 struct net_device *dev = dst->dev;
231 struct rt6_info *rt = (struct rt6_info *)dst;
232
David Ahernf8a1b432018-04-17 17:33:21 -0700233 daddr = choose_neigh_daddr(&rt->rt6i_gateway, NULL, daddr);
Julian Anastasov63fca652017-02-06 23:14:15 +0200234 if (!daddr)
235 return;
236 if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
237 return;
238 if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
239 return;
240 __ipv6_confirm_neigh(dev, daddr);
241}
242
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800243static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 .gc = ip6_dst_gc,
246 .gc_thresh = 1024,
247 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800248 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000249 .mtu = ip6_mtu,
David Ahernd4ead6b2018-04-17 17:33:16 -0700250 .cow_metrics = dst_cow_metrics_generic,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 .destroy = ip6_dst_destroy,
252 .ifdown = ip6_dst_ifdown,
253 .negative_advice = ip6_negative_advice,
254 .link_failure = ip6_link_failure,
255 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700256 .redirect = rt6_do_redirect,
Eric W. Biederman9f8955c2015-10-07 16:48:39 -0500257 .local_out = __ip6_local_out,
David Ahernf8a1b432018-04-17 17:33:21 -0700258 .neigh_lookup = ip6_dst_neigh_lookup,
Julian Anastasov63fca652017-02-06 23:14:15 +0200259 .confirm_neigh = ip6_confirm_neigh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260};
261
Steffen Klassertebb762f2011-11-23 02:12:51 +0000262static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800263{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000264 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
265
266 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800267}
268
David S. Miller6700c272012-07-17 03:29:28 -0700269static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
270 struct sk_buff *skb, u32 mtu)
David S. Miller14e50e52007-05-24 18:17:54 -0700271{
272}
273
David S. Miller6700c272012-07-17 03:29:28 -0700274static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
275 struct sk_buff *skb)
David S. Millerb587ee32012-07-12 00:39:24 -0700276{
277}
278
David S. Miller14e50e52007-05-24 18:17:54 -0700279static struct dst_ops ip6_dst_blackhole_ops = {
280 .family = AF_INET6,
David S. Miller14e50e52007-05-24 18:17:54 -0700281 .destroy = ip6_dst_destroy,
282 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000283 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800284 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700285 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700286 .redirect = ip6_rt_blackhole_redirect,
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -0700287 .cow_metrics = dst_cow_metrics_generic,
David Ahernf8a1b432018-04-17 17:33:21 -0700288 .neigh_lookup = ip6_dst_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700289};
290
David S. Miller62fa8a82011-01-26 20:51:05 -0800291static const u32 ip6_template_metrics[RTAX_MAX] = {
Li RongQing14edd872012-10-24 14:01:18 +0800292 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800293};
294
David Ahern8d1c8022018-04-17 17:33:26 -0700295static const struct fib6_info fib6_null_entry_template = {
David Ahern93c2fb22018-04-18 15:38:59 -0700296 .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP),
297 .fib6_protocol = RTPROT_KERNEL,
298 .fib6_metric = ~(u32)0,
299 .fib6_ref = ATOMIC_INIT(1),
David Ahern421842e2018-04-17 17:33:18 -0700300 .fib6_type = RTN_UNREACHABLE,
301 .fib6_metrics = (struct dst_metrics *)&dst_default_metrics,
302};
303
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000304static const struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700305 .dst = {
306 .__refcnt = ATOMIC_INIT(1),
307 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000308 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700309 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700310 .input = ip6_pkt_discard,
311 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 },
313 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314};
315
Thomas Graf101367c2006-08-04 03:39:02 -0700316#ifdef CONFIG_IPV6_MULTIPLE_TABLES
317
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000318static const struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700319 .dst = {
320 .__refcnt = ATOMIC_INIT(1),
321 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000322 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700323 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700324 .input = ip6_pkt_prohibit,
325 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700326 },
327 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700328};
329
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000330static const struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700331 .dst = {
332 .__refcnt = ATOMIC_INIT(1),
333 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000334 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700335 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700336 .input = dst_discard,
Eric W. Biedermanede20592015-10-07 16:48:47 -0500337 .output = dst_discard_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700338 },
339 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700340};
341
342#endif
343
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700344static void rt6_info_init(struct rt6_info *rt)
345{
346 struct dst_entry *dst = &rt->dst;
347
348 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700349 INIT_LIST_HEAD(&rt->rt6i_uncached);
350}
351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352/* allocate dst with ip6_dst_ops */
David Ahern93531c62018-04-17 17:33:25 -0700353struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
354 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
David S. Miller97bab732012-06-09 22:36:36 -0700356 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
Wei Wangb2a9c0e2017-06-17 10:42:41 -0700357 1, DST_OBSOLETE_FORCE_CHK, flags);
David S. Millercf911662011-04-28 14:31:47 -0700358
Wei Wang81eb8442017-10-06 12:06:11 -0700359 if (rt) {
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700360 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -0700361 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
362 }
Steffen Klassert81048912012-07-05 23:37:09 +0000363
David S. Millercf911662011-04-28 14:31:47 -0700364 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365}
David Ahern9ab179d2016-04-07 11:10:06 -0700366EXPORT_SYMBOL(ip6_dst_alloc);
Martin KaFai Laud52d3992015-05-22 20:56:06 -0700367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368static void ip6_dst_destroy(struct dst_entry *dst)
369{
370 struct rt6_info *rt = (struct rt6_info *)dst;
David Aherna68886a2018-04-20 15:38:02 -0700371 struct fib6_info *from;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700372 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
David Ahern1620a332018-10-04 20:07:54 -0700374 ip_dst_metrics_put(dst);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700375 rt6_uncached_list_del(rt);
376
377 idev = rt->rt6i_idev;
David S. Miller38308472011-12-03 18:02:47 -0500378 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 rt->rt6i_idev = NULL;
380 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900381 }
Gao feng1716a962012-04-06 00:13:10 +0000382
David Aherna68886a2018-04-20 15:38:02 -0700383 rcu_read_lock();
384 from = rcu_dereference(rt->from);
385 rcu_assign_pointer(rt->from, NULL);
David Ahern93531c62018-04-17 17:33:25 -0700386 fib6_info_release(from);
David Aherna68886a2018-04-20 15:38:02 -0700387 rcu_read_unlock();
David S. Millerb3419362010-11-30 12:27:11 -0800388}
389
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
391 int how)
392{
393 struct rt6_info *rt = (struct rt6_info *)dst;
394 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800395 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900396 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Wei Wange5645f52017-08-14 10:44:59 -0700398 if (idev && idev->dev != loopback_dev) {
399 struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev);
400 if (loopback_idev) {
401 rt->rt6i_idev = loopback_idev;
402 in6_dev_put(idev);
David S. Miller97cac082012-07-02 22:43:47 -0700403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
405}
406
Martin KaFai Lau5973fb12015-11-11 11:51:07 -0800407static bool __rt6_check_expired(const struct rt6_info *rt)
408{
409 if (rt->rt6i_flags & RTF_EXPIRES)
410 return time_after(jiffies, rt->dst.expires);
411 else
412 return false;
413}
414
Eric Dumazeta50feda2012-05-18 18:57:34 +0000415static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416{
David Aherna68886a2018-04-20 15:38:02 -0700417 struct fib6_info *from;
418
419 from = rcu_dereference(rt->from);
420
Gao feng1716a962012-04-06 00:13:10 +0000421 if (rt->rt6i_flags & RTF_EXPIRES) {
422 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000423 return true;
David Aherna68886a2018-04-20 15:38:02 -0700424 } else if (from) {
Xin Long1e2ea8a2017-08-26 20:10:10 +0800425 return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
David Aherna68886a2018-04-20 15:38:02 -0700426 fib6_check_expired(from);
Gao feng1716a962012-04-06 00:13:10 +0000427 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000428 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430
David Ahernb1d40992019-04-16 14:35:59 -0700431void fib6_select_path(const struct net *net, struct fib6_result *res,
432 struct flowi6 *fl6, int oif, bool have_oif_match,
433 const struct sk_buff *skb, int strict)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000434{
David Ahern8d1c8022018-04-17 17:33:26 -0700435 struct fib6_info *sibling, *next_sibling;
David Ahernb1d40992019-04-16 14:35:59 -0700436 struct fib6_info *match = res->f6i;
437
438 if (!match->fib6_nsiblings || have_oif_match)
439 goto out;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000440
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200441 /* We might have already computed the hash for ICMPv6 errors. In such
442 * case it will always be non-zero. Otherwise now is the time to do it.
443 */
444 if (!fl6->mp_hash)
David Ahernb4bac172018-03-02 08:32:18 -0800445 fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200446
David Ahernad1601a2019-03-27 20:53:56 -0700447 if (fl6->mp_hash <= atomic_read(&match->fib6_nh.fib_nh_upper_bound))
David Ahernb1d40992019-04-16 14:35:59 -0700448 goto out;
Ido Schimmelbbfcd772017-11-21 09:50:12 +0200449
David Ahern93c2fb22018-04-18 15:38:59 -0700450 list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
451 fib6_siblings) {
David Ahern702cea52019-04-09 14:41:13 -0700452 const struct fib6_nh *nh = &sibling->fib6_nh;
David Ahern5e670d82018-04-17 17:33:14 -0700453 int nh_upper_bound;
454
David Ahern702cea52019-04-09 14:41:13 -0700455 nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
David Ahern5e670d82018-04-17 17:33:14 -0700456 if (fl6->mp_hash > nh_upper_bound)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200457 continue;
David Ahern702cea52019-04-09 14:41:13 -0700458 if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200459 break;
460 match = sibling;
461 break;
462 }
463
David Ahernb1d40992019-04-16 14:35:59 -0700464out:
465 res->f6i = match;
466 res->nh = &match->fib6_nh;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000467}
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469/*
Wei Wang66f5d6c2017-10-06 12:06:10 -0700470 * Route lookup. rcu_read_lock() should be held.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 */
472
David Ahern0c59d002019-04-09 14:41:18 -0700473static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
474 const struct in6_addr *saddr, int oif, int flags)
475{
476 const struct net_device *dev;
477
478 if (nh->fib_nh_flags & RTNH_F_DEAD)
479 return false;
480
481 dev = nh->fib_nh_dev;
482 if (oif) {
483 if (dev->ifindex == oif)
484 return true;
485 } else {
486 if (ipv6_chk_addr(net, saddr, dev,
487 flags & RT6_LOOKUP_F_IFACE))
488 return true;
489 }
490
491 return false;
492}
493
David Ahern8d1c8022018-04-17 17:33:26 -0700494static inline struct fib6_info *rt6_device_match(struct net *net,
495 struct fib6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000496 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700498 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499{
David Ahern0c59d002019-04-09 14:41:18 -0700500 const struct fib6_nh *nh;
David Ahern8d1c8022018-04-17 17:33:26 -0700501 struct fib6_info *sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
David Ahern5e670d82018-04-17 17:33:14 -0700503 if (!oif && ipv6_addr_any(saddr) &&
David Ahernad1601a2019-03-27 20:53:56 -0700504 !(rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD))
Ido Schimmel8067bb82018-01-07 12:45:09 +0200505 return rt;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900506
David Ahern8fb11a92018-05-04 13:54:24 -0700507 for (sprt = rt; sprt; sprt = rcu_dereference(sprt->fib6_next)) {
David Ahern0c59d002019-04-09 14:41:18 -0700508 nh = &sprt->fib6_nh;
509 if (__rt6_device_match(net, nh, saddr, oif, flags))
510 return sprt;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900511 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
David Aherneea68cd2018-04-18 15:39:02 -0700513 if (oif && flags & RT6_LOOKUP_F_IFACE)
514 return net->ipv6.fib6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
David Ahernad1601a2019-03-27 20:53:56 -0700516 return rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ? net->ipv6.fib6_null_entry : rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517}
518
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800519#ifdef CONFIG_IPV6_ROUTER_PREF
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200520struct __rt6_probe_work {
521 struct work_struct work;
522 struct in6_addr target;
523 struct net_device *dev;
524};
525
526static void rt6_probe_deferred(struct work_struct *w)
527{
528 struct in6_addr mcaddr;
529 struct __rt6_probe_work *work =
530 container_of(w, struct __rt6_probe_work, work);
531
532 addrconf_addr_solict_mult(&work->target, &mcaddr);
Erik Nordmarkadc176c2016-12-02 14:00:08 -0800533 ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200534 dev_put(work->dev);
Michael Büsch662f5532015-02-08 10:14:07 +0100535 kfree(work);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200536}
537
David Aherncc3a86c2019-04-09 14:41:12 -0700538static void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800539{
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200540 struct __rt6_probe_work *work = NULL;
David Ahern5e670d82018-04-17 17:33:14 -0700541 const struct in6_addr *nh_gw;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000542 struct neighbour *neigh;
David Ahern5e670d82018-04-17 17:33:14 -0700543 struct net_device *dev;
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200544 struct inet6_dev *idev;
David Ahern5e670d82018-04-17 17:33:14 -0700545
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800546 /*
547 * Okay, this does not seem to be appropriate
548 * for now, however, we need to check if it
549 * is really so; aka Router Reachability Probing.
550 *
551 * Router Reachability Probe MUST be rate-limited
552 * to no more than one per minute.
553 */
David Aherncc3a86c2019-04-09 14:41:12 -0700554 if (fib6_nh->fib_nh_gw_family)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000555 return;
David Ahern5e670d82018-04-17 17:33:14 -0700556
David Aherncc3a86c2019-04-09 14:41:12 -0700557 nh_gw = &fib6_nh->fib_nh_gw6;
558 dev = fib6_nh->fib_nh_dev;
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000559 rcu_read_lock_bh();
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200560 idev = __in6_dev_get(dev);
David Ahern5e670d82018-04-17 17:33:14 -0700561 neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000562 if (neigh) {
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700563 if (neigh->nud_state & NUD_VALID)
564 goto out;
565
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000566 write_lock(&neigh->lock);
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700567 if (!(neigh->nud_state & NUD_VALID) &&
568 time_after(jiffies,
David Aherndcd1f572018-04-18 15:39:05 -0700569 neigh->updated + idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700570 work = kmalloc(sizeof(*work), GFP_ATOMIC);
571 if (work)
572 __neigh_set_probe_once(neigh);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200573 }
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000574 write_unlock(&neigh->lock);
David Aherncc3a86c2019-04-09 14:41:12 -0700575 } else if (time_after(jiffies, fib6_nh->last_probe +
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200576 idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700577 work = kmalloc(sizeof(*work), GFP_ATOMIC);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000578 }
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700579
580 if (work) {
David Aherncc3a86c2019-04-09 14:41:12 -0700581 fib6_nh->last_probe = jiffies;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700582 INIT_WORK(&work->work, rt6_probe_deferred);
David Ahern5e670d82018-04-17 17:33:14 -0700583 work->target = *nh_gw;
584 dev_hold(dev);
585 work->dev = dev;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700586 schedule_work(&work->work);
587 }
588
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700589out:
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000590 rcu_read_unlock_bh();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800591}
592#else
David Aherncc3a86c2019-04-09 14:41:12 -0700593static inline void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800594{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800595}
596#endif
597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800599 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 */
David Ahern1ba9a892019-04-09 14:41:10 -0700601static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200603 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
David Ahern5e670d82018-04-17 17:33:14 -0700604 struct neighbour *neigh;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000605
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000606 rcu_read_lock_bh();
David Ahern1ba9a892019-04-09 14:41:10 -0700607 neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
608 &fib6_nh->fib_nh_gw6);
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000609 if (neigh) {
610 read_lock(&neigh->lock);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800611 if (neigh->nud_state & NUD_VALID)
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200612 ret = RT6_NUD_SUCCEED;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800613#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000614 else if (!(neigh->nud_state & NUD_FAILED))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200615 ret = RT6_NUD_SUCCEED;
Jiri Benc7e980562013-12-11 13:48:20 +0100616 else
617 ret = RT6_NUD_FAIL_PROBE;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800618#endif
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000619 read_unlock(&neigh->lock);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200620 } else {
621 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
Jiri Benc7e980562013-12-11 13:48:20 +0100622 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
Paul Marksa5a81f02012-12-03 10:26:54 +0000623 }
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000624 rcu_read_unlock_bh();
625
Paul Marksa5a81f02012-12-03 10:26:54 +0000626 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800627}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
David Ahern702cea52019-04-09 14:41:13 -0700629static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
630 int strict)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800631{
David Ahern6e1809a2019-04-09 14:41:11 -0700632 int m = 0;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900633
David Ahern6e1809a2019-04-09 14:41:11 -0700634 if (!oif || nh->fib_nh_dev->ifindex == oif)
635 m = 2;
636
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700637 if (!m && (strict & RT6_LOOKUP_F_IFACE))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200638 return RT6_NUD_FAIL_HARD;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800639#ifdef CONFIG_IPV6_ROUTER_PREF
David Ahern702cea52019-04-09 14:41:13 -0700640 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800641#endif
David Ahern1ba9a892019-04-09 14:41:10 -0700642 if ((strict & RT6_LOOKUP_F_REACHABLE) &&
David Ahern702cea52019-04-09 14:41:13 -0700643 !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
David Ahern1ba9a892019-04-09 14:41:10 -0700644 int n = rt6_check_neigh(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200645 if (n < 0)
646 return n;
647 }
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800648 return m;
649}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
David Ahern28679ed2019-04-09 14:41:14 -0700651static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
652 int oif, int strict, int *mpri, bool *do_rr)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800653{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200654 bool match_do_rr = false;
David Ahern28679ed2019-04-09 14:41:14 -0700655 bool rc = false;
656 int m;
Andy Gospodarek35103d12015-08-13 10:39:01 -0400657
David Ahern28679ed2019-04-09 14:41:14 -0700658 if (nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel8067bb82018-01-07 12:45:09 +0200659 goto out;
660
David Ahern28679ed2019-04-09 14:41:14 -0700661 if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
662 nh->fib_nh_flags & RTNH_F_LINKDOWN &&
David Ahernd5d32e42016-10-24 12:27:23 -0700663 !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
Andy Gospodarek35103d12015-08-13 10:39:01 -0400664 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700665
David Ahern28679ed2019-04-09 14:41:14 -0700666 m = rt6_score_route(nh, fib6_flags, oif, strict);
Jiri Benc7e980562013-12-11 13:48:20 +0100667 if (m == RT6_NUD_FAIL_DO_RR) {
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200668 match_do_rr = true;
669 m = 0; /* lowest valid score */
Jiri Benc7e980562013-12-11 13:48:20 +0100670 } else if (m == RT6_NUD_FAIL_HARD) {
David S. Millerf11e6652007-03-24 20:36:25 -0700671 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700672 }
673
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200674 if (strict & RT6_LOOKUP_F_REACHABLE)
David Ahern28679ed2019-04-09 14:41:14 -0700675 rt6_probe(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200676
Jiri Benc7e980562013-12-11 13:48:20 +0100677 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200678 if (m > *mpri) {
679 *do_rr = match_do_rr;
680 *mpri = m;
David Ahern28679ed2019-04-09 14:41:14 -0700681 rc = true;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200682 }
David S. Millerf11e6652007-03-24 20:36:25 -0700683out:
David Ahern28679ed2019-04-09 14:41:14 -0700684 return rc;
David S. Millerf11e6652007-03-24 20:36:25 -0700685}
686
David Ahern30c15f02019-04-09 14:41:15 -0700687static void __find_rr_leaf(struct fib6_info *rt_start,
688 struct fib6_info *nomatch, u32 metric,
689 struct fib6_info **match, struct fib6_info **cont,
690 int oif, int strict, bool *do_rr, int *mpri)
David S. Millerf11e6652007-03-24 20:36:25 -0700691{
David Ahern30c15f02019-04-09 14:41:15 -0700692 struct fib6_info *rt;
693
694 for (rt = rt_start;
695 rt && rt != nomatch;
696 rt = rcu_dereference(rt->fib6_next)) {
697 struct fib6_nh *nh;
698
699 if (cont && rt->fib6_metric != metric) {
700 *cont = rt;
701 return;
702 }
703
704 if (fib6_check_expired(rt))
705 continue;
706
707 nh = &rt->fib6_nh;
708 if (find_match(nh, rt->fib6_flags, oif, strict, mpri, do_rr))
709 *match = rt;
710 }
711}
712
713static struct fib6_info *find_rr_leaf(struct fib6_node *fn,
714 struct fib6_info *leaf,
715 struct fib6_info *rr_head,
716 u32 metric, int oif, int strict,
717 bool *do_rr)
718{
719 struct fib6_info *match = NULL, *cont = NULL;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800720 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
David Ahern30c15f02019-04-09 14:41:15 -0700722 __find_rr_leaf(rr_head, NULL, metric, &match, &cont,
723 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700724
David Ahern30c15f02019-04-09 14:41:15 -0700725 __find_rr_leaf(leaf, rr_head, metric, &match, &cont,
726 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700727
728 if (match || !cont)
729 return match;
730
David Ahern30c15f02019-04-09 14:41:15 -0700731 __find_rr_leaf(cont, NULL, metric, &match, NULL,
732 oif, strict, do_rr, &mpri);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800733
David S. Millerf11e6652007-03-24 20:36:25 -0700734 return match;
735}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800736
David Ahern8d1c8022018-04-17 17:33:26 -0700737static struct fib6_info *rt6_select(struct net *net, struct fib6_node *fn,
Wei Wang8d1040e2017-10-06 12:06:08 -0700738 int oif, int strict)
David S. Millerf11e6652007-03-24 20:36:25 -0700739{
David Ahern8d1c8022018-04-17 17:33:26 -0700740 struct fib6_info *leaf = rcu_dereference(fn->leaf);
741 struct fib6_info *match, *rt0;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200742 bool do_rr = false;
Wei Wang17ecf592017-10-06 12:06:09 -0700743 int key_plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
David Ahern421842e2018-04-17 17:33:18 -0700745 if (!leaf || leaf == net->ipv6.fib6_null_entry)
746 return net->ipv6.fib6_null_entry;
Wei Wang8d1040e2017-10-06 12:06:08 -0700747
Wei Wang66f5d6c2017-10-06 12:06:10 -0700748 rt0 = rcu_dereference(fn->rr_ptr);
David S. Millerf11e6652007-03-24 20:36:25 -0700749 if (!rt0)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700750 rt0 = leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Wei Wang17ecf592017-10-06 12:06:09 -0700752 /* Double check to make sure fn is not an intermediate node
753 * and fn->leaf does not points to its child's leaf
754 * (This might happen if all routes under fn are deleted from
755 * the tree and fib6_repair_tree() is called on the node.)
756 */
David Ahern93c2fb22018-04-18 15:38:59 -0700757 key_plen = rt0->fib6_dst.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700758#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -0700759 if (rt0->fib6_src.plen)
760 key_plen = rt0->fib6_src.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700761#endif
762 if (fn->fn_bit != key_plen)
David Ahern421842e2018-04-17 17:33:18 -0700763 return net->ipv6.fib6_null_entry;
Wei Wang17ecf592017-10-06 12:06:09 -0700764
David Ahern93c2fb22018-04-18 15:38:59 -0700765 match = find_rr_leaf(fn, leaf, rt0, rt0->fib6_metric, oif, strict,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200766 &do_rr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200768 if (do_rr) {
David Ahern8fb11a92018-05-04 13:54:24 -0700769 struct fib6_info *next = rcu_dereference(rt0->fib6_next);
David S. Millerf11e6652007-03-24 20:36:25 -0700770
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800771 /* no entries matched; do round-robin */
David Ahern93c2fb22018-04-18 15:38:59 -0700772 if (!next || next->fib6_metric != rt0->fib6_metric)
Wei Wang8d1040e2017-10-06 12:06:08 -0700773 next = leaf;
David S. Millerf11e6652007-03-24 20:36:25 -0700774
Wei Wang66f5d6c2017-10-06 12:06:10 -0700775 if (next != rt0) {
David Ahern93c2fb22018-04-18 15:38:59 -0700776 spin_lock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700777 /* make sure next is not being deleted from the tree */
David Ahern93c2fb22018-04-18 15:38:59 -0700778 if (next->fib6_node)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700779 rcu_assign_pointer(fn->rr_ptr, next);
David Ahern93c2fb22018-04-18 15:38:59 -0700780 spin_unlock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700781 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 }
783
David Ahern421842e2018-04-17 17:33:18 -0700784 return match ? match : net->ipv6.fib6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785}
786
David Ahern85bd05d2019-04-16 14:36:01 -0700787static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700788{
David Ahern85bd05d2019-04-16 14:36:01 -0700789 return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
790 res->nh->fib_nh_gw_family;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700791}
792
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800793#ifdef CONFIG_IPV6_ROUTE_INFO
794int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000795 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800796{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900797 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800798 struct route_info *rinfo = (struct route_info *) opt;
799 struct in6_addr prefix_buf, *prefix;
800 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900801 unsigned long lifetime;
David Ahern8d1c8022018-04-17 17:33:26 -0700802 struct fib6_info *rt;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800803
804 if (len < sizeof(struct route_info)) {
805 return -EINVAL;
806 }
807
808 /* Sanity check for prefix_len and length */
809 if (rinfo->length > 3) {
810 return -EINVAL;
811 } else if (rinfo->prefix_len > 128) {
812 return -EINVAL;
813 } else if (rinfo->prefix_len > 64) {
814 if (rinfo->length < 2) {
815 return -EINVAL;
816 }
817 } else if (rinfo->prefix_len > 0) {
818 if (rinfo->length < 1) {
819 return -EINVAL;
820 }
821 }
822
823 pref = rinfo->route_pref;
824 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000825 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800826
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900827 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800828
829 if (rinfo->length == 3)
830 prefix = (struct in6_addr *)rinfo->prefix;
831 else {
832 /* this function is safe */
833 ipv6_addr_prefix(&prefix_buf,
834 (struct in6_addr *)rinfo->prefix,
835 rinfo->prefix_len);
836 prefix = &prefix_buf;
837 }
838
Duan Jiongf104a562013-11-08 09:56:53 +0800839 if (rinfo->prefix_len == 0)
David Ahernafb1d4b52018-04-17 17:33:11 -0700840 rt = rt6_get_dflt_router(net, gwaddr, dev);
Duan Jiongf104a562013-11-08 09:56:53 +0800841 else
842 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
David Ahern830218c2016-10-24 10:52:35 -0700843 gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800844
845 if (rt && !lifetime) {
David Ahernafb1d4b52018-04-17 17:33:11 -0700846 ip6_del_rt(net, rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800847 rt = NULL;
848 }
849
850 if (!rt && lifetime)
David Ahern830218c2016-10-24 10:52:35 -0700851 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
852 dev, pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800853 else if (rt)
David Ahern93c2fb22018-04-18 15:38:59 -0700854 rt->fib6_flags = RTF_ROUTEINFO |
855 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800856
857 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000858 if (!addrconf_finite_timeout(lifetime))
David Ahern14895682018-04-17 17:33:17 -0700859 fib6_clean_expires(rt);
Gao feng1716a962012-04-06 00:13:10 +0000860 else
David Ahern14895682018-04-17 17:33:17 -0700861 fib6_set_expires(rt, jiffies + HZ * lifetime);
Gao feng1716a962012-04-06 00:13:10 +0000862
David Ahern93531c62018-04-17 17:33:25 -0700863 fib6_info_release(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800864 }
865 return 0;
866}
867#endif
868
David Ahernae90d862018-04-17 17:33:12 -0700869/*
870 * Misc support functions
871 */
872
873/* called with rcu_lock held */
David Ahern8d1c8022018-04-17 17:33:26 -0700874static struct net_device *ip6_rt_get_dev_rcu(struct fib6_info *rt)
David Ahernae90d862018-04-17 17:33:12 -0700875{
David Ahernad1601a2019-03-27 20:53:56 -0700876 struct net_device *dev = rt->fib6_nh.fib_nh_dev;
David Ahernae90d862018-04-17 17:33:12 -0700877
David Ahern93c2fb22018-04-18 15:38:59 -0700878 if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
David Ahernae90d862018-04-17 17:33:12 -0700879 /* for copies of local routes, dst->dev needs to be the
880 * device if it is a master device, the master device if
881 * device is enslaved, and the loopback as the default
882 */
883 if (netif_is_l3_slave(dev) &&
David Ahern93c2fb22018-04-18 15:38:59 -0700884 !rt6_need_strict(&rt->fib6_dst.addr))
David Ahernae90d862018-04-17 17:33:12 -0700885 dev = l3mdev_master_dev_rcu(dev);
886 else if (!netif_is_l3_master(dev))
887 dev = dev_net(dev)->loopback_dev;
888 /* last case is netif_is_l3_master(dev) is true in which
889 * case we want dev returned to be dev
890 */
891 }
892
893 return dev;
894}
895
David Ahern6edb3c92018-04-17 17:33:15 -0700896static const int fib6_prop[RTN_MAX + 1] = {
897 [RTN_UNSPEC] = 0,
898 [RTN_UNICAST] = 0,
899 [RTN_LOCAL] = 0,
900 [RTN_BROADCAST] = 0,
901 [RTN_ANYCAST] = 0,
902 [RTN_MULTICAST] = 0,
903 [RTN_BLACKHOLE] = -EINVAL,
904 [RTN_UNREACHABLE] = -EHOSTUNREACH,
905 [RTN_PROHIBIT] = -EACCES,
906 [RTN_THROW] = -EAGAIN,
907 [RTN_NAT] = -EINVAL,
908 [RTN_XRESOLVE] = -EINVAL,
909};
910
911static int ip6_rt_type_to_error(u8 fib6_type)
912{
913 return fib6_prop[fib6_type];
914}
915
David Ahern8d1c8022018-04-17 17:33:26 -0700916static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
David Ahern3b6761d2018-04-17 17:33:20 -0700917{
918 unsigned short flags = 0;
919
920 if (rt->dst_nocount)
921 flags |= DST_NOCOUNT;
922 if (rt->dst_nopolicy)
923 flags |= DST_NOPOLICY;
924 if (rt->dst_host)
925 flags |= DST_HOST;
926
927 return flags;
928}
929
David Ahern8d1c8022018-04-17 17:33:26 -0700930static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct fib6_info *ort)
David Ahern6edb3c92018-04-17 17:33:15 -0700931{
932 rt->dst.error = ip6_rt_type_to_error(ort->fib6_type);
933
934 switch (ort->fib6_type) {
935 case RTN_BLACKHOLE:
936 rt->dst.output = dst_discard_out;
937 rt->dst.input = dst_discard;
938 break;
939 case RTN_PROHIBIT:
940 rt->dst.output = ip6_pkt_prohibit_out;
941 rt->dst.input = ip6_pkt_prohibit;
942 break;
943 case RTN_THROW:
944 case RTN_UNREACHABLE:
945 default:
946 rt->dst.output = ip6_pkt_discard_out;
947 rt->dst.input = ip6_pkt_discard;
948 break;
949 }
950}
951
David Ahern8d1c8022018-04-17 17:33:26 -0700952static void ip6_rt_init_dst(struct rt6_info *rt, struct fib6_info *ort)
David Ahern6edb3c92018-04-17 17:33:15 -0700953{
David Ahern93c2fb22018-04-18 15:38:59 -0700954 if (ort->fib6_flags & RTF_REJECT) {
David Ahern6edb3c92018-04-17 17:33:15 -0700955 ip6_rt_init_dst_reject(rt, ort);
956 return;
957 }
958
959 rt->dst.error = 0;
960 rt->dst.output = ip6_output;
961
Hangbin Liud23c4b62018-08-23 11:31:37 +0800962 if (ort->fib6_type == RTN_LOCAL || ort->fib6_type == RTN_ANYCAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700963 rt->dst.input = ip6_input;
David Ahern93c2fb22018-04-18 15:38:59 -0700964 } else if (ipv6_addr_type(&ort->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700965 rt->dst.input = ip6_mc_input;
966 } else {
967 rt->dst.input = ip6_forward;
968 }
969
David Ahernad1601a2019-03-27 20:53:56 -0700970 if (ort->fib6_nh.fib_nh_lws) {
971 rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.fib_nh_lws);
David Ahern6edb3c92018-04-17 17:33:15 -0700972 lwtunnel_set_redirect(&rt->dst);
973 }
974
975 rt->dst.lastuse = jiffies;
976}
977
Wei Wange873e4b2018-07-21 20:56:32 -0700978/* Caller must already hold reference to @from */
David Ahern8d1c8022018-04-17 17:33:26 -0700979static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
David Ahernae90d862018-04-17 17:33:12 -0700980{
David Ahernae90d862018-04-17 17:33:12 -0700981 rt->rt6i_flags &= ~RTF_EXPIRES;
David Aherna68886a2018-04-20 15:38:02 -0700982 rcu_assign_pointer(rt->from, from);
David Aherne1255ed2018-10-04 20:07:53 -0700983 ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
David Ahernae90d862018-04-17 17:33:12 -0700984}
985
Wei Wange873e4b2018-07-21 20:56:32 -0700986/* Caller must already hold reference to @ort */
David Ahern8d1c8022018-04-17 17:33:26 -0700987static void ip6_rt_copy_init(struct rt6_info *rt, struct fib6_info *ort)
David Ahernae90d862018-04-17 17:33:12 -0700988{
David Aherndcd1f572018-04-18 15:39:05 -0700989 struct net_device *dev = fib6_info_nh_dev(ort);
990
David Ahern6edb3c92018-04-17 17:33:15 -0700991 ip6_rt_init_dst(rt, ort);
992
David Ahern93c2fb22018-04-18 15:38:59 -0700993 rt->rt6i_dst = ort->fib6_dst;
David Aherndcd1f572018-04-18 15:39:05 -0700994 rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
David Ahern93c2fb22018-04-18 15:38:59 -0700995 rt->rt6i_flags = ort->fib6_flags;
David Ahernbdf00462019-04-05 16:30:26 -0700996 if (ort->fib6_nh.fib_nh_gw_family) {
David Ahernad1601a2019-03-27 20:53:56 -0700997 rt->rt6i_gateway = ort->fib6_nh.fib_nh_gw6;
David Ahern2b2450c2019-03-27 20:53:52 -0700998 rt->rt6i_flags |= RTF_GATEWAY;
999 }
David Ahernae90d862018-04-17 17:33:12 -07001000 rt6_set_from(rt, ort);
David Ahernae90d862018-04-17 17:33:12 -07001001#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07001002 rt->rt6i_src = ort->fib6_src;
David Ahernae90d862018-04-17 17:33:12 -07001003#endif
David Ahernae90d862018-04-17 17:33:12 -07001004}
1005
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001006static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1007 struct in6_addr *saddr)
1008{
Wei Wang66f5d6c2017-10-06 12:06:10 -07001009 struct fib6_node *pn, *sn;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001010 while (1) {
1011 if (fn->fn_flags & RTN_TL_ROOT)
1012 return NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001013 pn = rcu_dereference(fn->parent);
1014 sn = FIB6_SUBTREE(pn);
1015 if (sn && sn != fn)
David Ahern64547432018-05-09 20:34:19 -07001016 fn = fib6_node_lookup(sn, NULL, saddr);
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001017 else
1018 fn = pn;
1019 if (fn->fn_flags & RTN_RTINFO)
1020 return fn;
1021 }
1022}
Thomas Grafc71099a2006-08-04 23:20:06 -07001023
David Ahern10585b42019-03-20 09:24:50 -07001024static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
Wei Wangd3843fe2017-10-06 12:06:06 -07001025{
1026 struct rt6_info *rt = *prt;
1027
1028 if (dst_hold_safe(&rt->dst))
1029 return true;
David Ahern10585b42019-03-20 09:24:50 -07001030 if (net) {
Wei Wangd3843fe2017-10-06 12:06:06 -07001031 rt = net->ipv6.ip6_null_entry;
1032 dst_hold(&rt->dst);
1033 } else {
1034 rt = NULL;
1035 }
1036 *prt = rt;
1037 return false;
1038}
1039
David Aherndec9b0e2018-04-17 17:33:19 -07001040/* called with rcu_lock held */
David Ahern8d1c8022018-04-17 17:33:26 -07001041static struct rt6_info *ip6_create_rt_rcu(struct fib6_info *rt)
David Aherndec9b0e2018-04-17 17:33:19 -07001042{
David Ahern3b6761d2018-04-17 17:33:20 -07001043 unsigned short flags = fib6_info_dst_flags(rt);
David Ahernad1601a2019-03-27 20:53:56 -07001044 struct net_device *dev = rt->fib6_nh.fib_nh_dev;
David Aherndec9b0e2018-04-17 17:33:19 -07001045 struct rt6_info *nrt;
1046
Wei Wange873e4b2018-07-21 20:56:32 -07001047 if (!fib6_info_hold_safe(rt))
Xin Long1c87e792019-03-20 14:45:48 +08001048 goto fallback;
Wei Wange873e4b2018-07-21 20:56:32 -07001049
David Ahern93531c62018-04-17 17:33:25 -07001050 nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
Xin Long1c87e792019-03-20 14:45:48 +08001051 if (!nrt) {
Wei Wange873e4b2018-07-21 20:56:32 -07001052 fib6_info_release(rt);
Xin Long1c87e792019-03-20 14:45:48 +08001053 goto fallback;
1054 }
David Aherndec9b0e2018-04-17 17:33:19 -07001055
Xin Long1c87e792019-03-20 14:45:48 +08001056 ip6_rt_copy_init(nrt, rt);
1057 return nrt;
1058
1059fallback:
1060 nrt = dev_net(dev)->ipv6.ip6_null_entry;
1061 dst_hold(&nrt->dst);
David Aherndec9b0e2018-04-17 17:33:19 -07001062 return nrt;
1063}
1064
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001065static struct rt6_info *ip6_pol_route_lookup(struct net *net,
1066 struct fib6_table *table,
David Ahernb75cc8f2018-03-02 08:32:17 -08001067 struct flowi6 *fl6,
1068 const struct sk_buff *skb,
1069 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
David Ahernb1d40992019-04-16 14:35:59 -07001071 struct fib6_result res = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 struct fib6_node *fn;
David Ahern23fb93a2018-04-17 17:33:23 -07001073 struct rt6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
David Ahernb6cdbc82018-03-29 17:44:57 -07001075 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1076 flags &= ~RT6_LOOKUP_F_IFACE;
1077
Wei Wang66f5d6c2017-10-06 12:06:10 -07001078 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07001079 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -07001080restart:
David Ahernb1d40992019-04-16 14:35:59 -07001081 res.f6i = rcu_dereference(fn->leaf);
1082 if (!res.f6i)
1083 res.f6i = net->ipv6.fib6_null_entry;
David Ahernaf52a522019-04-09 14:41:16 -07001084 else
David Ahernb1d40992019-04-16 14:35:59 -07001085 res.f6i = rt6_device_match(net, res.f6i, &fl6->saddr,
1086 fl6->flowi6_oif, flags);
David Ahernaf52a522019-04-09 14:41:16 -07001087
David Ahernb1d40992019-04-16 14:35:59 -07001088 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001089 fn = fib6_backtrack(fn, &fl6->saddr);
1090 if (fn)
1091 goto restart;
David Ahernaf52a522019-04-09 14:41:16 -07001092
1093 rt = net->ipv6.ip6_null_entry;
1094 dst_hold(&rt->dst);
1095 goto out;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001096 }
Wei Wang2b760fc2017-10-06 12:06:03 -07001097
David Ahernb1d40992019-04-16 14:35:59 -07001098 fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1099 fl6->flowi6_oif != 0, skb, flags);
1100
David S. Miller4c9483b2011-03-12 16:22:43 -05001101 /* Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001102 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001103 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001104 if (ip6_hold_safe(net, &rt))
David Aherndec9b0e2018-04-17 17:33:19 -07001105 dst_use_noref(&rt->dst, jiffies);
David Ahern23fb93a2018-04-17 17:33:23 -07001106 } else {
David Ahernb1d40992019-04-16 14:35:59 -07001107 rt = ip6_create_rt_rcu(res.f6i);
David Aherndec9b0e2018-04-17 17:33:19 -07001108 }
Wei Wangd3843fe2017-10-06 12:06:06 -07001109
David Ahernaf52a522019-04-09 14:41:16 -07001110out:
David Ahernb1d40992019-04-16 14:35:59 -07001111 trace_fib6_table_lookup(net, res.f6i, table, fl6);
David Ahernaf52a522019-04-09 14:41:16 -07001112
Wei Wang66f5d6c2017-10-06 12:06:10 -07001113 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08001114
Thomas Grafc71099a2006-08-04 23:20:06 -07001115 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001116}
1117
Ian Morris67ba4152014-08-24 21:53:10 +01001118struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08001119 const struct sk_buff *skb, int flags)
Florian Westphalea6e5742011-09-05 16:05:44 +02001120{
David Ahernb75cc8f2018-03-02 08:32:17 -08001121 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
Florian Westphalea6e5742011-09-05 16:05:44 +02001122}
1123EXPORT_SYMBOL_GPL(ip6_route_lookup);
1124
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001125struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
David Ahernb75cc8f2018-03-02 08:32:17 -08001126 const struct in6_addr *saddr, int oif,
1127 const struct sk_buff *skb, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -07001128{
David S. Miller4c9483b2011-03-12 16:22:43 -05001129 struct flowi6 fl6 = {
1130 .flowi6_oif = oif,
1131 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -07001132 };
1133 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001134 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07001135
Thomas Grafadaa70b2006-10-13 15:01:03 -07001136 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -05001137 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -07001138 flags |= RT6_LOOKUP_F_HAS_SADDR;
1139 }
1140
David Ahernb75cc8f2018-03-02 08:32:17 -08001141 dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -07001142 if (dst->error == 0)
1143 return (struct rt6_info *) dst;
1144
1145 dst_release(dst);
1146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 return NULL;
1148}
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001149EXPORT_SYMBOL(rt6_lookup);
1150
Thomas Grafc71099a2006-08-04 23:20:06 -07001151/* ip6_ins_rt is called with FREE table->tb6_lock.
Wei Wang1cfb71e2017-06-17 10:42:33 -07001152 * It takes new route entry, the addition fails by any reason the
1153 * route is released.
1154 * Caller must hold dst before calling it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 */
1156
David Ahern8d1c8022018-04-17 17:33:26 -07001157static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
David Ahern333c4302017-05-21 10:12:04 -06001158 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159{
1160 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001161 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
David Ahern93c2fb22018-04-18 15:38:59 -07001163 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001164 spin_lock_bh(&table->tb6_lock);
David Ahernd4ead6b2018-04-17 17:33:16 -07001165 err = fib6_add(&table->tb6_root, rt, info, extack);
Wei Wang66f5d6c2017-10-06 12:06:10 -07001166 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167
1168 return err;
1169}
1170
David Ahern8d1c8022018-04-17 17:33:26 -07001171int ip6_ins_rt(struct net *net, struct fib6_info *rt)
Thomas Graf40e22e82006-08-22 00:00:45 -07001172{
David Ahernafb1d4b52018-04-17 17:33:11 -07001173 struct nl_info info = { .nl_net = net, };
Florian Westphale715b6d2015-01-05 23:57:44 +01001174
David Ahernd4ead6b2018-04-17 17:33:16 -07001175 return __ip6_ins_rt(rt, &info, NULL);
Thomas Graf40e22e82006-08-22 00:00:45 -07001176}
1177
David Ahern85bd05d2019-04-16 14:36:01 -07001178static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001179 const struct in6_addr *daddr,
1180 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181{
David Ahern85bd05d2019-04-16 14:36:01 -07001182 struct fib6_info *f6i = res->f6i;
David Ahern4832c302017-08-17 12:17:20 -07001183 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 struct rt6_info *rt;
1185
1186 /*
1187 * Clone the route.
1188 */
1189
David Ahern85bd05d2019-04-16 14:36:01 -07001190 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001191 return NULL;
1192
David Ahern85bd05d2019-04-16 14:36:01 -07001193 dev = ip6_rt_get_dev_rcu(f6i);
David Ahern93531c62018-04-17 17:33:25 -07001194 rt = ip6_dst_alloc(dev_net(dev), dev, 0);
Wei Wange873e4b2018-07-21 20:56:32 -07001195 if (!rt) {
David Ahern85bd05d2019-04-16 14:36:01 -07001196 fib6_info_release(f6i);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001197 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001198 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001199
David Ahern85bd05d2019-04-16 14:36:01 -07001200 ip6_rt_copy_init(rt, res->f6i);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001201 rt->rt6i_flags |= RTF_CACHE;
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001202 rt->dst.flags |= DST_HOST;
1203 rt->rt6i_dst.addr = *daddr;
1204 rt->rt6i_dst.plen = 128;
1205
David Ahern85bd05d2019-04-16 14:36:01 -07001206 if (!rt6_is_gw_or_nonexthop(res)) {
1207 if (f6i->fib6_dst.plen != 128 &&
1208 ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001209 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210#ifdef CONFIG_IPV6_SUBTREES
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001211 if (rt->rt6i_src.plen && saddr) {
1212 rt->rt6i_src.addr = *saddr;
1213 rt->rt6i_src.plen = 128;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001214 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001215#endif
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001216 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001218 return rt;
1219}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220
David Ahern8d1c8022018-04-17 17:33:26 -07001221static struct rt6_info *ip6_rt_pcpu_alloc(struct fib6_info *rt)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001222{
David Ahern3b6761d2018-04-17 17:33:20 -07001223 unsigned short flags = fib6_info_dst_flags(rt);
David Ahern4832c302017-08-17 12:17:20 -07001224 struct net_device *dev;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001225 struct rt6_info *pcpu_rt;
1226
Wei Wange873e4b2018-07-21 20:56:32 -07001227 if (!fib6_info_hold_safe(rt))
1228 return NULL;
1229
David Ahern4832c302017-08-17 12:17:20 -07001230 rcu_read_lock();
1231 dev = ip6_rt_get_dev_rcu(rt);
David Ahern93531c62018-04-17 17:33:25 -07001232 pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags);
David Ahern4832c302017-08-17 12:17:20 -07001233 rcu_read_unlock();
Wei Wange873e4b2018-07-21 20:56:32 -07001234 if (!pcpu_rt) {
1235 fib6_info_release(rt);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001236 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001237 }
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001238 ip6_rt_copy_init(pcpu_rt, rt);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001239 pcpu_rt->rt6i_flags |= RTF_PCPU;
1240 return pcpu_rt;
1241}
1242
Wei Wang66f5d6c2017-10-06 12:06:10 -07001243/* It should be called with rcu_read_lock() acquired */
David Ahern8d1c8022018-04-17 17:33:26 -07001244static struct rt6_info *rt6_get_pcpu_route(struct fib6_info *rt)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001245{
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001246 struct rt6_info *pcpu_rt, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001247
1248 p = this_cpu_ptr(rt->rt6i_pcpu);
1249 pcpu_rt = *p;
1250
David Ahernd4ead6b2018-04-17 17:33:16 -07001251 if (pcpu_rt)
David Ahern10585b42019-03-20 09:24:50 -07001252 ip6_hold_safe(NULL, &pcpu_rt);
Wei Wangd3843fe2017-10-06 12:06:06 -07001253
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001254 return pcpu_rt;
1255}
1256
David Ahernafb1d4b52018-04-17 17:33:11 -07001257static struct rt6_info *rt6_make_pcpu_route(struct net *net,
David Ahern8d1c8022018-04-17 17:33:26 -07001258 struct fib6_info *rt)
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001259{
1260 struct rt6_info *pcpu_rt, *prev, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001261
1262 pcpu_rt = ip6_rt_pcpu_alloc(rt);
1263 if (!pcpu_rt) {
Martin KaFai Lau9c7370a2015-08-14 11:05:54 -07001264 dst_hold(&net->ipv6.ip6_null_entry->dst);
1265 return net->ipv6.ip6_null_entry;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001266 }
1267
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001268 dst_hold(&pcpu_rt->dst);
Wei Wanga94b9362017-10-06 12:06:04 -07001269 p = this_cpu_ptr(rt->rt6i_pcpu);
1270 prev = cmpxchg(p, NULL, pcpu_rt);
Eric Dumazet951f7882017-10-08 21:07:18 -07001271 BUG_ON(prev);
Wei Wanga94b9362017-10-06 12:06:04 -07001272
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001273 return pcpu_rt;
1274}
1275
Wei Wang35732d02017-10-06 12:05:57 -07001276/* exception hash table implementation
1277 */
1278static DEFINE_SPINLOCK(rt6_exception_lock);
1279
1280/* Remove rt6_ex from hash table and free the memory
1281 * Caller must hold rt6_exception_lock
1282 */
1283static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1284 struct rt6_exception *rt6_ex)
1285{
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001286 struct fib6_info *from;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001287 struct net *net;
Wei Wang81eb8442017-10-06 12:06:11 -07001288
Wei Wang35732d02017-10-06 12:05:57 -07001289 if (!bucket || !rt6_ex)
1290 return;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001291
1292 net = dev_net(rt6_ex->rt6i->dst.dev);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001293 net->ipv6.rt6_stats->fib_rt_cache--;
1294
1295 /* purge completely the exception to allow releasing the held resources:
1296 * some [sk] cache may keep the dst around for unlimited time
1297 */
1298 from = rcu_dereference_protected(rt6_ex->rt6i->from,
1299 lockdep_is_held(&rt6_exception_lock));
1300 rcu_assign_pointer(rt6_ex->rt6i->from, NULL);
1301 fib6_info_release(from);
1302 dst_dev_put(&rt6_ex->rt6i->dst);
1303
Wei Wang35732d02017-10-06 12:05:57 -07001304 hlist_del_rcu(&rt6_ex->hlist);
David Ahern77634cc2018-04-17 17:33:27 -07001305 dst_release(&rt6_ex->rt6i->dst);
Wei Wang35732d02017-10-06 12:05:57 -07001306 kfree_rcu(rt6_ex, rcu);
1307 WARN_ON_ONCE(!bucket->depth);
1308 bucket->depth--;
1309}
1310
1311/* Remove oldest rt6_ex in bucket and free the memory
1312 * Caller must hold rt6_exception_lock
1313 */
1314static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
1315{
1316 struct rt6_exception *rt6_ex, *oldest = NULL;
1317
1318 if (!bucket)
1319 return;
1320
1321 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1322 if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
1323 oldest = rt6_ex;
1324 }
1325 rt6_remove_exception(bucket, oldest);
1326}
1327
1328static u32 rt6_exception_hash(const struct in6_addr *dst,
1329 const struct in6_addr *src)
1330{
1331 static u32 seed __read_mostly;
1332 u32 val;
1333
1334 net_get_random_once(&seed, sizeof(seed));
1335 val = jhash(dst, sizeof(*dst), seed);
1336
1337#ifdef CONFIG_IPV6_SUBTREES
1338 if (src)
1339 val = jhash(src, sizeof(*src), val);
1340#endif
1341 return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
1342}
1343
1344/* Helper function to find the cached rt in the hash table
1345 * and update bucket pointer to point to the bucket for this
1346 * (daddr, saddr) pair
1347 * Caller must hold rt6_exception_lock
1348 */
1349static struct rt6_exception *
1350__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
1351 const struct in6_addr *daddr,
1352 const struct in6_addr *saddr)
1353{
1354 struct rt6_exception *rt6_ex;
1355 u32 hval;
1356
1357 if (!(*bucket) || !daddr)
1358 return NULL;
1359
1360 hval = rt6_exception_hash(daddr, saddr);
1361 *bucket += hval;
1362
1363 hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
1364 struct rt6_info *rt6 = rt6_ex->rt6i;
1365 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1366
1367#ifdef CONFIG_IPV6_SUBTREES
1368 if (matched && saddr)
1369 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1370#endif
1371 if (matched)
1372 return rt6_ex;
1373 }
1374 return NULL;
1375}
1376
1377/* Helper function to find the cached rt in the hash table
1378 * and update bucket pointer to point to the bucket for this
1379 * (daddr, saddr) pair
1380 * Caller must hold rcu_read_lock()
1381 */
1382static struct rt6_exception *
1383__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
1384 const struct in6_addr *daddr,
1385 const struct in6_addr *saddr)
1386{
1387 struct rt6_exception *rt6_ex;
1388 u32 hval;
1389
1390 WARN_ON_ONCE(!rcu_read_lock_held());
1391
1392 if (!(*bucket) || !daddr)
1393 return NULL;
1394
1395 hval = rt6_exception_hash(daddr, saddr);
1396 *bucket += hval;
1397
1398 hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
1399 struct rt6_info *rt6 = rt6_ex->rt6i;
1400 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1401
1402#ifdef CONFIG_IPV6_SUBTREES
1403 if (matched && saddr)
1404 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1405#endif
1406 if (matched)
1407 return rt6_ex;
1408 }
1409 return NULL;
1410}
1411
David Ahern8d1c8022018-04-17 17:33:26 -07001412static unsigned int fib6_mtu(const struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001413{
David Ahernd4ead6b2018-04-17 17:33:16 -07001414 unsigned int mtu;
1415
David Aherndcd1f572018-04-18 15:39:05 -07001416 if (rt->fib6_pmtu) {
1417 mtu = rt->fib6_pmtu;
1418 } else {
1419 struct net_device *dev = fib6_info_nh_dev(rt);
1420 struct inet6_dev *idev;
1421
1422 rcu_read_lock();
1423 idev = __in6_dev_get(dev);
1424 mtu = idev->cnf.mtu6;
1425 rcu_read_unlock();
1426 }
1427
David Ahernd4ead6b2018-04-17 17:33:16 -07001428 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1429
David Ahernad1601a2019-03-27 20:53:56 -07001430 return mtu - lwtunnel_headroom(rt->fib6_nh.fib_nh_lws, mtu);
David Ahernd4ead6b2018-04-17 17:33:16 -07001431}
1432
Wei Wang35732d02017-10-06 12:05:57 -07001433static int rt6_insert_exception(struct rt6_info *nrt,
David Ahern8d1c8022018-04-17 17:33:26 -07001434 struct fib6_info *ort)
Wei Wang35732d02017-10-06 12:05:57 -07001435{
David Ahern5e670d82018-04-17 17:33:14 -07001436 struct net *net = dev_net(nrt->dst.dev);
Wei Wang35732d02017-10-06 12:05:57 -07001437 struct rt6_exception_bucket *bucket;
1438 struct in6_addr *src_key = NULL;
1439 struct rt6_exception *rt6_ex;
1440 int err = 0;
1441
Wei Wang35732d02017-10-06 12:05:57 -07001442 spin_lock_bh(&rt6_exception_lock);
1443
1444 if (ort->exception_bucket_flushed) {
1445 err = -EINVAL;
1446 goto out;
1447 }
1448
1449 bucket = rcu_dereference_protected(ort->rt6i_exception_bucket,
1450 lockdep_is_held(&rt6_exception_lock));
1451 if (!bucket) {
1452 bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
1453 GFP_ATOMIC);
1454 if (!bucket) {
1455 err = -ENOMEM;
1456 goto out;
1457 }
1458 rcu_assign_pointer(ort->rt6i_exception_bucket, bucket);
1459 }
1460
1461#ifdef CONFIG_IPV6_SUBTREES
1462 /* rt6i_src.plen != 0 indicates ort is in subtree
1463 * and exception table is indexed by a hash of
1464 * both rt6i_dst and rt6i_src.
1465 * Otherwise, the exception table is indexed by
1466 * a hash of only rt6i_dst.
1467 */
David Ahern93c2fb22018-04-18 15:38:59 -07001468 if (ort->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001469 src_key = &nrt->rt6i_src.addr;
1470#endif
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001471 /* rt6_mtu_change() might lower mtu on ort.
1472 * Only insert this exception route if its mtu
1473 * is less than ort's mtu value.
1474 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001475 if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(ort)) {
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001476 err = -EINVAL;
1477 goto out;
1478 }
Wei Wang60006a42017-10-06 12:05:58 -07001479
Wei Wang35732d02017-10-06 12:05:57 -07001480 rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
1481 src_key);
1482 if (rt6_ex)
1483 rt6_remove_exception(bucket, rt6_ex);
1484
1485 rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
1486 if (!rt6_ex) {
1487 err = -ENOMEM;
1488 goto out;
1489 }
1490 rt6_ex->rt6i = nrt;
1491 rt6_ex->stamp = jiffies;
Wei Wang35732d02017-10-06 12:05:57 -07001492 hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
1493 bucket->depth++;
Wei Wang81eb8442017-10-06 12:06:11 -07001494 net->ipv6.rt6_stats->fib_rt_cache++;
Wei Wang35732d02017-10-06 12:05:57 -07001495
1496 if (bucket->depth > FIB6_MAX_DEPTH)
1497 rt6_exception_remove_oldest(bucket);
1498
1499out:
1500 spin_unlock_bh(&rt6_exception_lock);
1501
1502 /* Update fn->fn_sernum to invalidate all cached dst */
Paolo Abenib886d5f2017-10-19 16:07:10 +02001503 if (!err) {
David Ahern93c2fb22018-04-18 15:38:59 -07001504 spin_lock_bh(&ort->fib6_table->tb6_lock);
David Ahern7aef6852018-04-17 17:33:10 -07001505 fib6_update_sernum(net, ort);
David Ahern93c2fb22018-04-18 15:38:59 -07001506 spin_unlock_bh(&ort->fib6_table->tb6_lock);
Paolo Abenib886d5f2017-10-19 16:07:10 +02001507 fib6_force_start_gc(net);
1508 }
Wei Wang35732d02017-10-06 12:05:57 -07001509
1510 return err;
1511}
1512
David Ahern8d1c8022018-04-17 17:33:26 -07001513void rt6_flush_exceptions(struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001514{
1515 struct rt6_exception_bucket *bucket;
1516 struct rt6_exception *rt6_ex;
1517 struct hlist_node *tmp;
1518 int i;
1519
1520 spin_lock_bh(&rt6_exception_lock);
1521 /* Prevent rt6_insert_exception() to recreate the bucket list */
1522 rt->exception_bucket_flushed = 1;
1523
1524 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1525 lockdep_is_held(&rt6_exception_lock));
1526 if (!bucket)
1527 goto out;
1528
1529 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1530 hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist)
1531 rt6_remove_exception(bucket, rt6_ex);
1532 WARN_ON_ONCE(bucket->depth);
1533 bucket++;
1534 }
1535
1536out:
1537 spin_unlock_bh(&rt6_exception_lock);
1538}
1539
1540/* Find cached rt in the hash table inside passed in rt
1541 * Caller has to hold rcu_read_lock()
1542 */
David Ahern7e4b5122019-04-16 14:36:00 -07001543static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang35732d02017-10-06 12:05:57 -07001544 struct in6_addr *daddr,
1545 struct in6_addr *saddr)
1546{
1547 struct rt6_exception_bucket *bucket;
1548 struct in6_addr *src_key = NULL;
1549 struct rt6_exception *rt6_ex;
David Ahern7e4b5122019-04-16 14:36:00 -07001550 struct rt6_info *ret = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001551
David Ahern7e4b5122019-04-16 14:36:00 -07001552 bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001553
1554#ifdef CONFIG_IPV6_SUBTREES
David Ahern7e4b5122019-04-16 14:36:00 -07001555 /* fib6i_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001556 * and exception table is indexed by a hash of
David Ahern7e4b5122019-04-16 14:36:00 -07001557 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001558 * Otherwise, the exception table is indexed by
David Ahern7e4b5122019-04-16 14:36:00 -07001559 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001560 */
David Ahern7e4b5122019-04-16 14:36:00 -07001561 if (res->f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001562 src_key = saddr;
1563#endif
1564 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1565
1566 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
David Ahern7e4b5122019-04-16 14:36:00 -07001567 ret = rt6_ex->rt6i;
Wei Wang35732d02017-10-06 12:05:57 -07001568
David Ahern7e4b5122019-04-16 14:36:00 -07001569 return ret;
Wei Wang35732d02017-10-06 12:05:57 -07001570}
1571
1572/* Remove the passed in cached rt from the hash table that contains it */
David Ahern23fb93a2018-04-17 17:33:23 -07001573static int rt6_remove_exception_rt(struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001574{
Wei Wang35732d02017-10-06 12:05:57 -07001575 struct rt6_exception_bucket *bucket;
1576 struct in6_addr *src_key = NULL;
1577 struct rt6_exception *rt6_ex;
David Ahern8a14e462018-04-23 11:32:07 -07001578 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001579 int err;
1580
Eric Dumazet091311d2018-04-24 09:22:49 -07001581 from = rcu_dereference(rt->from);
Wei Wang35732d02017-10-06 12:05:57 -07001582 if (!from ||
Colin Ian King442d7132017-10-10 19:10:30 +01001583 !(rt->rt6i_flags & RTF_CACHE))
Wei Wang35732d02017-10-06 12:05:57 -07001584 return -EINVAL;
1585
1586 if (!rcu_access_pointer(from->rt6i_exception_bucket))
1587 return -ENOENT;
1588
1589 spin_lock_bh(&rt6_exception_lock);
1590 bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
1591 lockdep_is_held(&rt6_exception_lock));
1592#ifdef CONFIG_IPV6_SUBTREES
1593 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1594 * and exception table is indexed by a hash of
1595 * both rt6i_dst and rt6i_src.
1596 * Otherwise, the exception table is indexed by
1597 * a hash of only rt6i_dst.
1598 */
David Ahern93c2fb22018-04-18 15:38:59 -07001599 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001600 src_key = &rt->rt6i_src.addr;
1601#endif
1602 rt6_ex = __rt6_find_exception_spinlock(&bucket,
1603 &rt->rt6i_dst.addr,
1604 src_key);
1605 if (rt6_ex) {
1606 rt6_remove_exception(bucket, rt6_ex);
1607 err = 0;
1608 } else {
1609 err = -ENOENT;
1610 }
1611
1612 spin_unlock_bh(&rt6_exception_lock);
1613 return err;
1614}
1615
1616/* Find rt6_ex which contains the passed in rt cache and
1617 * refresh its stamp
1618 */
1619static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1620{
Wei Wang35732d02017-10-06 12:05:57 -07001621 struct rt6_exception_bucket *bucket;
1622 struct in6_addr *src_key = NULL;
1623 struct rt6_exception *rt6_ex;
Paolo Abeni193f3682019-02-21 11:19:41 +01001624 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001625
1626 rcu_read_lock();
Paolo Abeni193f3682019-02-21 11:19:41 +01001627 from = rcu_dereference(rt->from);
1628 if (!from || !(rt->rt6i_flags & RTF_CACHE))
1629 goto unlock;
1630
Wei Wang35732d02017-10-06 12:05:57 -07001631 bucket = rcu_dereference(from->rt6i_exception_bucket);
1632
1633#ifdef CONFIG_IPV6_SUBTREES
1634 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1635 * and exception table is indexed by a hash of
1636 * both rt6i_dst and rt6i_src.
1637 * Otherwise, the exception table is indexed by
1638 * a hash of only rt6i_dst.
1639 */
David Ahern93c2fb22018-04-18 15:38:59 -07001640 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001641 src_key = &rt->rt6i_src.addr;
1642#endif
1643 rt6_ex = __rt6_find_exception_rcu(&bucket,
1644 &rt->rt6i_dst.addr,
1645 src_key);
1646 if (rt6_ex)
1647 rt6_ex->stamp = jiffies;
1648
Paolo Abeni193f3682019-02-21 11:19:41 +01001649unlock:
Wei Wang35732d02017-10-06 12:05:57 -07001650 rcu_read_unlock();
1651}
1652
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001653static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1654 struct rt6_info *rt, int mtu)
1655{
1656 /* If the new MTU is lower than the route PMTU, this new MTU will be the
1657 * lowest MTU in the path: always allow updating the route PMTU to
1658 * reflect PMTU decreases.
1659 *
1660 * If the new MTU is higher, and the route PMTU is equal to the local
1661 * MTU, this means the old MTU is the lowest in the path, so allow
1662 * updating it: if other nodes now have lower MTUs, PMTU discovery will
1663 * handle this.
1664 */
1665
1666 if (dst_mtu(&rt->dst) >= mtu)
1667 return true;
1668
1669 if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
1670 return true;
1671
1672 return false;
1673}
1674
1675static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
David Ahern8d1c8022018-04-17 17:33:26 -07001676 struct fib6_info *rt, int mtu)
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001677{
1678 struct rt6_exception_bucket *bucket;
1679 struct rt6_exception *rt6_ex;
1680 int i;
1681
1682 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1683 lockdep_is_held(&rt6_exception_lock));
1684
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001685 if (!bucket)
1686 return;
1687
1688 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1689 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1690 struct rt6_info *entry = rt6_ex->rt6i;
1691
1692 /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
David Ahernd4ead6b2018-04-17 17:33:16 -07001693 * route), the metrics of its rt->from have already
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001694 * been updated.
1695 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001696 if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001697 rt6_mtu_change_route_allowed(idev, entry, mtu))
David Ahernd4ead6b2018-04-17 17:33:16 -07001698 dst_metric_set(&entry->dst, RTAX_MTU, mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001699 }
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001700 bucket++;
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001701 }
1702}
1703
Wei Wangb16cb452017-10-06 12:06:00 -07001704#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE)
1705
David Ahern8d1c8022018-04-17 17:33:26 -07001706static void rt6_exceptions_clean_tohost(struct fib6_info *rt,
Wei Wangb16cb452017-10-06 12:06:00 -07001707 struct in6_addr *gateway)
1708{
1709 struct rt6_exception_bucket *bucket;
1710 struct rt6_exception *rt6_ex;
1711 struct hlist_node *tmp;
1712 int i;
1713
1714 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1715 return;
1716
1717 spin_lock_bh(&rt6_exception_lock);
1718 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1719 lockdep_is_held(&rt6_exception_lock));
1720
1721 if (bucket) {
1722 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1723 hlist_for_each_entry_safe(rt6_ex, tmp,
1724 &bucket->chain, hlist) {
1725 struct rt6_info *entry = rt6_ex->rt6i;
1726
1727 if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
1728 RTF_CACHE_GATEWAY &&
1729 ipv6_addr_equal(gateway,
1730 &entry->rt6i_gateway)) {
1731 rt6_remove_exception(bucket, rt6_ex);
1732 }
1733 }
1734 bucket++;
1735 }
1736 }
1737
1738 spin_unlock_bh(&rt6_exception_lock);
1739}
1740
Wei Wangc757faa2017-10-06 12:06:01 -07001741static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
1742 struct rt6_exception *rt6_ex,
1743 struct fib6_gc_args *gc_args,
1744 unsigned long now)
1745{
1746 struct rt6_info *rt = rt6_ex->rt6i;
1747
Paolo Abeni1859bac2017-10-19 16:07:11 +02001748 /* we are pruning and obsoleting aged-out and non gateway exceptions
1749 * even if others have still references to them, so that on next
1750 * dst_check() such references can be dropped.
1751 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
1752 * expired, independently from their aging, as per RFC 8201 section 4
1753 */
Wei Wang31afeb42018-01-26 11:40:17 -08001754 if (!(rt->rt6i_flags & RTF_EXPIRES)) {
1755 if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
1756 RT6_TRACE("aging clone %p\n", rt);
1757 rt6_remove_exception(bucket, rt6_ex);
1758 return;
1759 }
1760 } else if (time_after(jiffies, rt->dst.expires)) {
1761 RT6_TRACE("purging expired route %p\n", rt);
Wei Wangc757faa2017-10-06 12:06:01 -07001762 rt6_remove_exception(bucket, rt6_ex);
1763 return;
Wei Wang31afeb42018-01-26 11:40:17 -08001764 }
1765
1766 if (rt->rt6i_flags & RTF_GATEWAY) {
Wei Wangc757faa2017-10-06 12:06:01 -07001767 struct neighbour *neigh;
1768 __u8 neigh_flags = 0;
1769
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001770 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
1771 if (neigh)
Wei Wangc757faa2017-10-06 12:06:01 -07001772 neigh_flags = neigh->flags;
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001773
Wei Wangc757faa2017-10-06 12:06:01 -07001774 if (!(neigh_flags & NTF_ROUTER)) {
1775 RT6_TRACE("purging route %p via non-router but gateway\n",
1776 rt);
1777 rt6_remove_exception(bucket, rt6_ex);
1778 return;
1779 }
1780 }
Wei Wang31afeb42018-01-26 11:40:17 -08001781
Wei Wangc757faa2017-10-06 12:06:01 -07001782 gc_args->more++;
1783}
1784
David Ahern8d1c8022018-04-17 17:33:26 -07001785void rt6_age_exceptions(struct fib6_info *rt,
Wei Wangc757faa2017-10-06 12:06:01 -07001786 struct fib6_gc_args *gc_args,
1787 unsigned long now)
1788{
1789 struct rt6_exception_bucket *bucket;
1790 struct rt6_exception *rt6_ex;
1791 struct hlist_node *tmp;
1792 int i;
1793
1794 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1795 return;
1796
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001797 rcu_read_lock_bh();
1798 spin_lock(&rt6_exception_lock);
Wei Wangc757faa2017-10-06 12:06:01 -07001799 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1800 lockdep_is_held(&rt6_exception_lock));
1801
1802 if (bucket) {
1803 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1804 hlist_for_each_entry_safe(rt6_ex, tmp,
1805 &bucket->chain, hlist) {
1806 rt6_age_examine_exception(bucket, rt6_ex,
1807 gc_args, now);
1808 }
1809 bucket++;
1810 }
1811 }
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001812 spin_unlock(&rt6_exception_lock);
1813 rcu_read_unlock_bh();
Wei Wangc757faa2017-10-06 12:06:01 -07001814}
1815
David Ahern1d053da2018-05-09 20:34:21 -07001816/* must be called with rcu lock held */
1817struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
1818 int oif, struct flowi6 *fl6, int strict)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819{
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001820 struct fib6_node *fn, *saved_fn;
David Ahern8d1c8022018-04-17 17:33:26 -07001821 struct fib6_info *f6i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822
David Ahern64547432018-05-09 20:34:19 -07001823 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001824 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
David Ahernca254492015-10-12 11:47:10 -07001826 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1827 oif = 0;
1828
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001829redo_rt6_select:
David Ahern23fb93a2018-04-17 17:33:23 -07001830 f6i = rt6_select(net, fn, oif, strict);
David Ahern23fb93a2018-04-17 17:33:23 -07001831 if (f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001832 fn = fib6_backtrack(fn, &fl6->saddr);
1833 if (fn)
1834 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001835 else if (strict & RT6_LOOKUP_F_REACHABLE) {
1836 /* also consider unreachable route */
1837 strict &= ~RT6_LOOKUP_F_REACHABLE;
1838 fn = saved_fn;
1839 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001840 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001841 }
1842
David Ahernd4bea422018-05-09 20:34:24 -07001843 trace_fib6_table_lookup(net, f6i, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08001844
David Ahern1d053da2018-05-09 20:34:21 -07001845 return f6i;
1846}
1847
1848struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1849 int oif, struct flowi6 *fl6,
1850 const struct sk_buff *skb, int flags)
1851{
David Ahernb1d40992019-04-16 14:35:59 -07001852 struct fib6_result res = {};
David Ahern1d053da2018-05-09 20:34:21 -07001853 struct rt6_info *rt;
1854 int strict = 0;
1855
1856 strict |= flags & RT6_LOOKUP_F_IFACE;
1857 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1858 if (net->ipv6.devconf_all->forwarding == 0)
1859 strict |= RT6_LOOKUP_F_REACHABLE;
1860
1861 rcu_read_lock();
1862
David Ahernb1d40992019-04-16 14:35:59 -07001863 res.f6i = fib6_table_lookup(net, table, oif, fl6, strict);
1864 if (res.f6i == net->ipv6.fib6_null_entry) {
David Ahern421842e2018-04-17 17:33:18 -07001865 rt = net->ipv6.ip6_null_entry;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001866 rcu_read_unlock();
Wei Wangd3843fe2017-10-06 12:06:06 -07001867 dst_hold(&rt->dst);
Wei Wangd3843fe2017-10-06 12:06:06 -07001868 return rt;
David Ahern23fb93a2018-04-17 17:33:23 -07001869 }
1870
David Ahernb1d40992019-04-16 14:35:59 -07001871 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07001872
David Ahern23fb93a2018-04-17 17:33:23 -07001873 /*Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001874 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001875 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001876 if (ip6_hold_safe(net, &rt))
Wei Wangd3843fe2017-10-06 12:06:06 -07001877 dst_use_noref(&rt->dst, jiffies);
David Ahernd4ead6b2018-04-17 17:33:16 -07001878
Wei Wang66f5d6c2017-10-06 12:06:10 -07001879 rcu_read_unlock();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001880 return rt;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001881 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07001882 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001883 /* Create a RTF_CACHE clone which will not be
1884 * owned by the fib6 tree. It is for the special case where
1885 * the daddr in the skb during the neighbor look-up is different
1886 * from the fl6->daddr used to look-up route here.
1887 */
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001888 struct rt6_info *uncached_rt;
1889
David Ahern85bd05d2019-04-16 14:36:01 -07001890 uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001891
David Ahern4d85cd02018-04-20 15:37:59 -07001892 rcu_read_unlock();
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001893
Wei Wang1cfb71e2017-06-17 10:42:33 -07001894 if (uncached_rt) {
1895 /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
1896 * No need for another dst_hold()
1897 */
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07001898 rt6_uncached_list_add(uncached_rt);
Wei Wang81eb8442017-10-06 12:06:11 -07001899 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang1cfb71e2017-06-17 10:42:33 -07001900 } else {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001901 uncached_rt = net->ipv6.ip6_null_entry;
Wei Wang1cfb71e2017-06-17 10:42:33 -07001902 dst_hold(&uncached_rt->dst);
1903 }
David Ahernb8115802015-11-19 12:24:22 -08001904
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001905 return uncached_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001906 } else {
1907 /* Get a percpu copy */
1908
1909 struct rt6_info *pcpu_rt;
1910
Eric Dumazet951f7882017-10-08 21:07:18 -07001911 local_bh_disable();
David Ahernb1d40992019-04-16 14:35:59 -07001912 pcpu_rt = rt6_get_pcpu_route(res.f6i);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001913
David Ahern93531c62018-04-17 17:33:25 -07001914 if (!pcpu_rt)
David Ahernb1d40992019-04-16 14:35:59 -07001915 pcpu_rt = rt6_make_pcpu_route(net, res.f6i);
David Ahern93531c62018-04-17 17:33:25 -07001916
Eric Dumazet951f7882017-10-08 21:07:18 -07001917 local_bh_enable();
1918 rcu_read_unlock();
David Ahernd4bea422018-05-09 20:34:24 -07001919
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001920 return pcpu_rt;
1921 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001922}
David Ahern9ff74382016-06-13 13:44:19 -07001923EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07001924
David Ahernb75cc8f2018-03-02 08:32:17 -08001925static struct rt6_info *ip6_pol_route_input(struct net *net,
1926 struct fib6_table *table,
1927 struct flowi6 *fl6,
1928 const struct sk_buff *skb,
1929 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001930{
David Ahernb75cc8f2018-03-02 08:32:17 -08001931 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001932}
1933
Mahesh Bandeward409b842016-09-16 12:59:08 -07001934struct dst_entry *ip6_route_input_lookup(struct net *net,
1935 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08001936 struct flowi6 *fl6,
1937 const struct sk_buff *skb,
1938 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001939{
1940 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
1941 flags |= RT6_LOOKUP_F_IFACE;
1942
David Ahernb75cc8f2018-03-02 08:32:17 -08001943 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001944}
Mahesh Bandeward409b842016-09-16 12:59:08 -07001945EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001946
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001947static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001948 struct flow_keys *keys,
1949 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001950{
1951 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1952 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001953 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001954 const struct ipv6hdr *inner_iph;
1955 const struct icmp6hdr *icmph;
1956 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07001957 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001958
1959 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
1960 goto out;
1961
Eric Dumazetcea67a22018-04-29 09:54:59 -07001962 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
1963 sizeof(_icmph), &_icmph);
1964 if (!icmph)
1965 goto out;
1966
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001967 if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
1968 icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
1969 icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
1970 icmph->icmp6_type != ICMPV6_PARAMPROB)
1971 goto out;
1972
1973 inner_iph = skb_header_pointer(skb,
1974 skb_transport_offset(skb) + sizeof(*icmph),
1975 sizeof(_inner_iph), &_inner_iph);
1976 if (!inner_iph)
1977 goto out;
1978
1979 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001980 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001981out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001982 if (_flkeys) {
1983 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
1984 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
1985 keys->tags.flow_label = _flkeys->tags.flow_label;
1986 keys->basic.ip_proto = _flkeys->basic.ip_proto;
1987 } else {
1988 keys->addrs.v6addrs.src = key_iph->saddr;
1989 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02001990 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001991 keys->basic.ip_proto = key_iph->nexthdr;
1992 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001993}
1994
1995/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08001996u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
1997 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001998{
1999 struct flow_keys hash_keys;
David Ahern9a2a5372018-03-02 08:32:15 -08002000 u32 mhash;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002001
David S. Millerbbfa0472018-03-12 11:09:33 -04002002 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002003 case 0:
2004 memset(&hash_keys, 0, sizeof(hash_keys));
2005 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2006 if (skb) {
2007 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2008 } else {
2009 hash_keys.addrs.v6addrs.src = fl6->saddr;
2010 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002011 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002012 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2013 }
2014 break;
2015 case 1:
2016 if (skb) {
2017 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2018 struct flow_keys keys;
2019
2020 /* short-circuit if we already have L4 hash present */
2021 if (skb->l4_hash)
2022 return skb_get_hash_raw(skb) >> 1;
2023
2024 memset(&hash_keys, 0, sizeof(hash_keys));
2025
2026 if (!flkeys) {
2027 skb_flow_dissect_flow_keys(skb, &keys, flag);
2028 flkeys = &keys;
2029 }
2030 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2031 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2032 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2033 hash_keys.ports.src = flkeys->ports.src;
2034 hash_keys.ports.dst = flkeys->ports.dst;
2035 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2036 } else {
2037 memset(&hash_keys, 0, sizeof(hash_keys));
2038 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2039 hash_keys.addrs.v6addrs.src = fl6->saddr;
2040 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2041 hash_keys.ports.src = fl6->fl6_sport;
2042 hash_keys.ports.dst = fl6->fl6_dport;
2043 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2044 }
2045 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002046 }
David Ahern9a2a5372018-03-02 08:32:15 -08002047 mhash = flow_hash_from_keys(&hash_keys);
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002048
David Ahern9a2a5372018-03-02 08:32:15 -08002049 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002050}
2051
Thomas Grafc71099a2006-08-04 23:20:06 -07002052void ip6_route_input(struct sk_buff *skb)
2053{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002054 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002055 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002056 int flags = RT6_LOOKUP_F_HAS_SADDR;
Jiri Benc904af042015-08-20 13:56:31 +02002057 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002058 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002059 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002060 .daddr = iph->daddr,
2061 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002062 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002063 .flowi6_mark = skb->mark,
2064 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002065 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002066 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002067
Jiri Benc904af042015-08-20 13:56:31 +02002068 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002069 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002070 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002071
2072 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2073 flkeys = &_flkeys;
2074
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002075 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002076 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002077 skb_dst_drop(skb);
David Ahernb75cc8f2018-03-02 08:32:17 -08002078 skb_dst_set(skb,
2079 ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002080}
2081
David Ahernb75cc8f2018-03-02 08:32:17 -08002082static struct rt6_info *ip6_pol_route_output(struct net *net,
2083 struct fib6_table *table,
2084 struct flowi6 *fl6,
2085 const struct sk_buff *skb,
2086 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002087{
David Ahernb75cc8f2018-03-02 08:32:17 -08002088 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002089}
2090
Paolo Abeni6f21c962016-01-29 12:30:19 +01002091struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
2092 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002093{
David Ahernd46a9d62015-10-21 08:42:22 -07002094 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002095
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002096 if (ipv6_addr_type(&fl6->daddr) &
2097 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002098 struct dst_entry *dst;
2099
2100 dst = l3mdev_link_scope_lookup(net, fl6);
2101 if (dst)
2102 return dst;
2103 }
David Ahernca254492015-10-12 11:47:10 -07002104
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002105 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002106
David Ahernd46a9d62015-10-21 08:42:22 -07002107 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002108 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002109 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002110 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002111
David Ahernd46a9d62015-10-21 08:42:22 -07002112 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002113 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002114 else if (sk)
2115 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002116
David Ahernb75cc8f2018-03-02 08:32:17 -08002117 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002119EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120
David S. Miller2774c132011-03-01 14:59:04 -08002121struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002122{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002123 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002124 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002125 struct dst_entry *new = NULL;
2126
Wei Wang1dbe32522017-06-17 10:42:26 -07002127 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002128 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002129 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002130 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002131 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002132
Changli Gaod8d1f302010-06-10 23:31:35 -07002133 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002134 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002135 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002136 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002137
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002138 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002139
Wei Wang1dbe32522017-06-17 10:42:26 -07002140 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002141 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002142 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002143
2144 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2145#ifdef CONFIG_IPV6_SUBTREES
2146 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2147#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002148 }
2149
David S. Miller69ead7a2011-03-01 14:45:33 -08002150 dst_release(dst_orig);
2151 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002152}
David S. Miller14e50e52007-05-24 18:17:54 -07002153
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154/*
2155 * Destination cache support functions
2156 */
2157
David Ahern8d1c8022018-04-17 17:33:26 -07002158static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002159{
Steffen Klassert36143642017-08-25 09:05:42 +02002160 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002161
David Ahern8ae86972018-04-20 15:38:03 -07002162 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002163 return false;
2164
2165 if (fib6_check_expired(f6i))
2166 return false;
2167
2168 return true;
2169}
2170
David Aherna68886a2018-04-20 15:38:02 -07002171static struct dst_entry *rt6_check(struct rt6_info *rt,
2172 struct fib6_info *from,
2173 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002174{
Wei Wangc5cff852017-08-21 09:47:10 -07002175 u32 rt_cookie = 0;
2176
David Aherna68886a2018-04-20 15:38:02 -07002177 if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
David Ahern93531c62018-04-17 17:33:25 -07002178 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002179 return NULL;
2180
2181 if (rt6_check_expired(rt))
2182 return NULL;
2183
2184 return &rt->dst;
2185}
2186
David Aherna68886a2018-04-20 15:38:02 -07002187static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2188 struct fib6_info *from,
2189 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002190{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002191 if (!__rt6_check_expired(rt) &&
2192 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002193 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002194 return &rt->dst;
2195 else
2196 return NULL;
2197}
2198
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
2200{
David Aherna87b7dc2018-04-20 15:38:00 -07002201 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002202 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 struct rt6_info *rt;
2204
David Aherna87b7dc2018-04-20 15:38:00 -07002205 rt = container_of(dst, struct rt6_info, dst);
2206
2207 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002209 /* All IPV6 dsts are created with ->obsolete set to the value
2210 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2211 * into this function always.
2212 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002213
David Aherna68886a2018-04-20 15:38:02 -07002214 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002215
David Aherna68886a2018-04-20 15:38:02 -07002216 if (from && (rt->rt6i_flags & RTF_PCPU ||
2217 unlikely(!list_empty(&rt->rt6i_uncached))))
2218 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002219 else
David Aherna68886a2018-04-20 15:38:02 -07002220 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002221
2222 rcu_read_unlock();
2223
2224 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225}
2226
2227static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2228{
2229 struct rt6_info *rt = (struct rt6_info *) dst;
2230
2231 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002232 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002233 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002234 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002235 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002236 dst = NULL;
2237 }
David Ahernc3c14da2018-04-23 11:32:06 -07002238 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002239 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002241 dst = NULL;
2242 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002244 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245}
2246
2247static void ip6_link_failure(struct sk_buff *skb)
2248{
2249 struct rt6_info *rt;
2250
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002251 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252
Eric Dumazetadf30902009-06-02 05:19:30 +00002253 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002255 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002256 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002257 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002258 } else {
David Aherna68886a2018-04-20 15:38:02 -07002259 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002260 struct fib6_node *fn;
2261
David Aherna68886a2018-04-20 15:38:02 -07002262 from = rcu_dereference(rt->from);
2263 if (from) {
2264 fn = rcu_dereference(from->fib6_node);
2265 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2266 fn->fn_sernum = -1;
2267 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002268 }
David Ahern8a14e462018-04-23 11:32:07 -07002269 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 }
2271}
2272
David Ahern6a3e0302018-04-20 15:37:57 -07002273static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2274{
David Aherna68886a2018-04-20 15:38:02 -07002275 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2276 struct fib6_info *from;
2277
2278 rcu_read_lock();
2279 from = rcu_dereference(rt0->from);
2280 if (from)
2281 rt0->dst.expires = from->expires;
2282 rcu_read_unlock();
2283 }
David Ahern6a3e0302018-04-20 15:37:57 -07002284
2285 dst_set_expires(&rt0->dst, timeout);
2286 rt0->rt6i_flags |= RTF_EXPIRES;
2287}
2288
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002289static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2290{
2291 struct net *net = dev_net(rt->dst.dev);
2292
David Ahernd4ead6b2018-04-17 17:33:16 -07002293 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002294 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002295 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2296}
2297
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002298static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2299{
2300 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002301 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002302}
2303
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002304static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2305 const struct ipv6hdr *iph, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002307 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002308 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309
Xin Long19bda362016-10-28 18:18:01 +08002310 if (dst_metric_locked(dst, RTAX_MTU))
2311 return;
2312
Julian Anastasov0dec8792017-02-06 23:14:16 +02002313 if (iph) {
2314 daddr = &iph->daddr;
2315 saddr = &iph->saddr;
2316 } else if (sk) {
2317 daddr = &sk->sk_v6_daddr;
2318 saddr = &inet6_sk(sk)->saddr;
2319 } else {
2320 daddr = NULL;
2321 saddr = NULL;
2322 }
2323 dst_confirm_neigh(dst, daddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002324 mtu = max_t(u32, mtu, IPV6_MIN_MTU);
2325 if (mtu >= dst_mtu(dst))
2326 return;
David S. Miller81aded22012-06-15 14:54:11 -07002327
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002328 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002329 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002330 /* update rt6_ex->stamp for cache */
2331 if (rt6->rt6i_flags & RTF_CACHE)
2332 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002333 } else if (daddr) {
David Ahern85bd05d2019-04-16 14:36:01 -07002334 struct fib6_result res = {};
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002335 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002336
David Ahern4d85cd02018-04-20 15:37:59 -07002337 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07002338 res.f6i = rcu_dereference(rt6->from);
2339 if (!res.f6i) {
Jonathan Lemon9c69a132019-04-14 14:21:29 -07002340 rcu_read_unlock();
2341 return;
2342 }
David Ahern85bd05d2019-04-16 14:36:01 -07002343 res.nh = &res.f6i->fib6_nh;
2344 nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002345 if (nrt6) {
2346 rt6_do_update_pmtu(nrt6, mtu);
David Ahern85bd05d2019-04-16 14:36:01 -07002347 if (rt6_insert_exception(nrt6, res.f6i))
Wei Wang2b760fc2017-10-06 12:06:03 -07002348 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002349 }
David Aherna68886a2018-04-20 15:38:02 -07002350 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 }
2352}
2353
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002354static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2355 struct sk_buff *skb, u32 mtu)
2356{
2357 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
2358}
2359
David S. Miller42ae66c2012-06-15 20:01:57 -07002360void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002361 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002362{
2363 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2364 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002365 struct flowi6 fl6 = {
2366 .flowi6_oif = oif,
2367 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2368 .daddr = iph->daddr,
2369 .saddr = iph->saddr,
2370 .flowlabel = ip6_flowinfo(iph),
2371 .flowi6_uid = uid,
2372 };
David S. Miller81aded22012-06-15 14:54:11 -07002373
2374 dst = ip6_route_output(net, NULL, &fl6);
2375 if (!dst->error)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002376 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07002377 dst_release(dst);
2378}
2379EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2380
2381void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2382{
David Ahern7ddacfa2018-11-18 10:45:30 -08002383 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002384 struct dst_entry *dst;
2385
David Ahern7ddacfa2018-11-18 10:45:30 -08002386 if (!oif && skb->dev)
2387 oif = l3mdev_master_ifindex(skb->dev);
2388
2389 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002390
2391 dst = __sk_dst_get(sk);
2392 if (!dst || !dst->obsolete ||
2393 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2394 return;
2395
2396 bh_lock_sock(sk);
2397 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2398 ip6_datagram_dst_update(sk, false);
2399 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002400}
2401EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2402
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002403void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2404 const struct flowi6 *fl6)
2405{
2406#ifdef CONFIG_IPV6_SUBTREES
2407 struct ipv6_pinfo *np = inet6_sk(sk);
2408#endif
2409
2410 ip6_dst_store(sk, dst,
2411 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2412 &sk->sk_v6_daddr : NULL,
2413#ifdef CONFIG_IPV6_SUBTREES
2414 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2415 &np->saddr :
2416#endif
2417 NULL);
2418}
2419
David Ahern0b34eb02019-04-09 14:41:19 -07002420static bool ip6_redirect_nh_match(struct fib6_info *f6i,
2421 struct fib6_nh *nh,
2422 struct flowi6 *fl6,
2423 const struct in6_addr *gw,
2424 struct rt6_info **ret)
2425{
2426 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2427 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2428 return false;
2429
2430 /* rt_cache's gateway might be different from its 'parent'
2431 * in the case of an ip redirect.
2432 * So we keep searching in the exception table if the gateway
2433 * is different.
2434 */
2435 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
David Ahern7e4b5122019-04-16 14:36:00 -07002436 struct fib6_result res = {
2437 .f6i = f6i,
2438 };
David Ahern0b34eb02019-04-09 14:41:19 -07002439 struct rt6_info *rt_cache;
2440
David Ahern7e4b5122019-04-16 14:36:00 -07002441 rt_cache = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern0b34eb02019-04-09 14:41:19 -07002442 if (rt_cache &&
2443 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2444 *ret = rt_cache;
2445 return true;
2446 }
2447 return false;
2448 }
2449 return true;
2450}
2451
Duan Jiongb55b76b2013-09-04 19:44:21 +08002452/* Handle redirects */
2453struct ip6rd_flowi {
2454 struct flowi6 fl6;
2455 struct in6_addr gateway;
2456};
2457
2458static struct rt6_info *__ip6_route_redirect(struct net *net,
2459 struct fib6_table *table,
2460 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002461 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002462 int flags)
2463{
2464 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002465 struct rt6_info *ret = NULL;
David Ahern8d1c8022018-04-17 17:33:26 -07002466 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002467 struct fib6_node *fn;
2468
2469 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002470 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002471 *
2472 * RFC 4861 specifies that redirects should only be
2473 * accepted if they come from the nexthop to the target.
2474 * Due to the way the routes are chosen, this notion
2475 * is a bit fuzzy and one might need to check all possible
2476 * routes.
2477 */
2478
Wei Wang66f5d6c2017-10-06 12:06:10 -07002479 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002480 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002481restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002482 for_each_fib6_node_rt_rcu(fn) {
David Ahern14895682018-04-17 17:33:17 -07002483 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002484 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002485 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002486 break;
David Ahern0b34eb02019-04-09 14:41:19 -07002487 if (ip6_redirect_nh_match(rt, &rt->fib6_nh, fl6,
2488 &rdfl->gateway, &ret))
2489 goto out;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002490 }
2491
2492 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002493 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002494 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002495 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002496 goto out;
2497 }
2498
David Ahern421842e2018-04-17 17:33:18 -07002499 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002500 fn = fib6_backtrack(fn, &fl6->saddr);
2501 if (fn)
2502 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002503 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002504
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002505out:
David Ahern23fb93a2018-04-17 17:33:23 -07002506 if (ret)
David Ahern10585b42019-03-20 09:24:50 -07002507 ip6_hold_safe(net, &ret);
David Ahern23fb93a2018-04-17 17:33:23 -07002508 else
2509 ret = ip6_create_rt_rcu(rt);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002510
Wei Wang66f5d6c2017-10-06 12:06:10 -07002511 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002512
Paolo Abenib65f1642017-10-19 09:31:43 +02002513 trace_fib6_table_lookup(net, rt, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002514 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002515};
2516
2517static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002518 const struct flowi6 *fl6,
2519 const struct sk_buff *skb,
2520 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002521{
2522 int flags = RT6_LOOKUP_F_HAS_SADDR;
2523 struct ip6rd_flowi rdfl;
2524
2525 rdfl.fl6 = *fl6;
2526 rdfl.gateway = *gateway;
2527
David Ahernb75cc8f2018-03-02 08:32:17 -08002528 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002529 flags, __ip6_route_redirect);
2530}
2531
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002532void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
2533 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002534{
2535 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2536 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07002537 struct flowi6 fl6 = {
2538 .flowi6_iif = LOOPBACK_IFINDEX,
2539 .flowi6_oif = oif,
2540 .flowi6_mark = mark,
2541 .daddr = iph->daddr,
2542 .saddr = iph->saddr,
2543 .flowlabel = ip6_flowinfo(iph),
2544 .flowi6_uid = uid,
2545 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002546
David Ahernb75cc8f2018-03-02 08:32:17 -08002547 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002548 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002549 dst_release(dst);
2550}
2551EXPORT_SYMBOL_GPL(ip6_redirect);
2552
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07002553void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08002554{
2555 const struct ipv6hdr *iph = ipv6_hdr(skb);
2556 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
2557 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002558 struct flowi6 fl6 = {
2559 .flowi6_iif = LOOPBACK_IFINDEX,
2560 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002561 .daddr = msg->dest,
2562 .saddr = iph->daddr,
2563 .flowi6_uid = sock_net_uid(net, NULL),
2564 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08002565
David Ahernb75cc8f2018-03-02 08:32:17 -08002566 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002567 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08002568 dst_release(dst);
2569}
2570
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002571void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
2572{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002573 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
2574 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002575}
2576EXPORT_SYMBOL_GPL(ip6_sk_redirect);
2577
David S. Miller0dbaee32010-12-13 12:52:14 -08002578static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579{
David S. Miller0dbaee32010-12-13 12:52:14 -08002580 struct net_device *dev = dst->dev;
2581 unsigned int mtu = dst_mtu(dst);
2582 struct net *net = dev_net(dev);
2583
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
2585
Daniel Lezcano55786892008-03-04 13:47:47 -08002586 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
2587 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588
2589 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002590 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
2591 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
2592 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 * rely only on pmtu discovery"
2594 */
2595 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
2596 mtu = IPV6_MAXPLEN;
2597 return mtu;
2598}
2599
Steffen Klassertebb762f2011-11-23 02:12:51 +00002600static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08002601{
David S. Millerd33e4552010-12-14 13:01:14 -08002602 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07002603 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002604
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002605 mtu = dst_metric_raw(dst, RTAX_MTU);
2606 if (mtu)
2607 goto out;
2608
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002609 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08002610
2611 rcu_read_lock();
2612 idev = __in6_dev_get(dst->dev);
2613 if (idev)
2614 mtu = idev->cnf.mtu6;
2615 rcu_read_unlock();
2616
Eric Dumazet30f78d82014-04-10 21:23:36 -07002617out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07002618 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2619
2620 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08002621}
2622
David Ahern901731b2018-05-21 09:08:14 -07002623/* MTU selection:
2624 * 1. mtu on route is locked - use it
2625 * 2. mtu from nexthop exception
2626 * 3. mtu from egress device
2627 *
2628 * based on ip6_dst_mtu_forward and exception logic of
2629 * rt6_find_cached_rt; called with rcu_read_lock
2630 */
2631u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
2632 struct in6_addr *saddr)
2633{
2634 struct rt6_exception_bucket *bucket;
2635 struct rt6_exception *rt6_ex;
2636 struct in6_addr *src_key;
2637 struct inet6_dev *idev;
2638 u32 mtu = 0;
2639
2640 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
2641 mtu = f6i->fib6_pmtu;
2642 if (mtu)
2643 goto out;
2644 }
2645
2646 src_key = NULL;
2647#ifdef CONFIG_IPV6_SUBTREES
2648 if (f6i->fib6_src.plen)
2649 src_key = saddr;
2650#endif
2651
2652 bucket = rcu_dereference(f6i->rt6i_exception_bucket);
2653 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
2654 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
2655 mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
2656
2657 if (likely(!mtu)) {
2658 struct net_device *dev = fib6_info_nh_dev(f6i);
2659
2660 mtu = IPV6_MIN_MTU;
2661 idev = __in6_dev_get(dev);
2662 if (idev && idev->cnf.mtu6 > mtu)
2663 mtu = idev->cnf.mtu6;
2664 }
2665
2666 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2667out:
2668 return mtu - lwtunnel_headroom(fib6_info_nh_lwt(f6i), mtu);
2669}
2670
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08002671struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05002672 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673{
David S. Miller87a11572011-12-06 17:04:13 -05002674 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 struct rt6_info *rt;
2676 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002677 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678
David S. Miller38308472011-12-03 18:02:47 -05002679 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00002680 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681
Martin KaFai Lauad706862015-08-14 11:05:52 -07002682 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05002683 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05002685 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 goto out;
2687 }
2688
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002689 rt->dst.flags |= DST_HOST;
Brendan McGrath588753f2017-12-13 22:14:57 +11002690 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002691 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03002692 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05002693 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002694 rt->rt6i_dst.plen = 128;
2695 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08002696 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697
Ido Schimmel4c981e22018-01-07 12:45:04 +02002698 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07002699 * do proper release of the net_device
2700 */
2701 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002702 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703
David S. Miller87a11572011-12-06 17:04:13 -05002704 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
2705
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706out:
David S. Miller87a11572011-12-06 17:04:13 -05002707 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708}
2709
Daniel Lezcano569d3642008-01-18 03:56:57 -08002710static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002712 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08002713 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
2714 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
2715 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
2716 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
2717 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002718 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719
Eric Dumazetfc66f952010-10-08 06:37:34 +00002720 entries = dst_entries_get_fast(ops);
Michal Kubeček49a18d82013-08-01 10:04:24 +02002721 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00002722 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 goto out;
2724
Benjamin Thery6891a342008-03-04 13:49:47 -08002725 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08002726 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002727 entries = dst_entries_get_slow(ops);
2728 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08002729 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08002731 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002732 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733}
2734
David Ahern8c145862016-04-24 21:26:04 -07002735static struct rt6_info *ip6_nh_lookup_table(struct net *net,
2736 struct fib6_config *cfg,
David Ahernf4797b32018-01-25 16:55:08 -08002737 const struct in6_addr *gw_addr,
2738 u32 tbid, int flags)
David Ahern8c145862016-04-24 21:26:04 -07002739{
2740 struct flowi6 fl6 = {
2741 .flowi6_oif = cfg->fc_ifindex,
2742 .daddr = *gw_addr,
2743 .saddr = cfg->fc_prefsrc,
2744 };
2745 struct fib6_table *table;
2746 struct rt6_info *rt;
David Ahern8c145862016-04-24 21:26:04 -07002747
David Ahernf4797b32018-01-25 16:55:08 -08002748 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07002749 if (!table)
2750 return NULL;
2751
2752 if (!ipv6_addr_any(&cfg->fc_prefsrc))
2753 flags |= RT6_LOOKUP_F_HAS_SADDR;
2754
David Ahernf4797b32018-01-25 16:55:08 -08002755 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahernb75cc8f2018-03-02 08:32:17 -08002756 rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07002757
2758 /* if table lookup failed, fall back to full lookup */
2759 if (rt == net->ipv6.ip6_null_entry) {
2760 ip6_rt_put(rt);
2761 rt = NULL;
2762 }
2763
2764 return rt;
2765}
2766
David Ahernfc1e64e2018-01-25 16:55:09 -08002767static int ip6_route_check_nh_onlink(struct net *net,
2768 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07002769 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08002770 struct netlink_ext_ack *extack)
2771{
David Ahern44750f82018-02-06 13:17:06 -08002772 u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08002773 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2774 u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002775 struct fib6_info *from;
David Ahernfc1e64e2018-01-25 16:55:09 -08002776 struct rt6_info *grt;
2777 int err;
2778
2779 err = 0;
2780 grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
2781 if (grt) {
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002782 rcu_read_lock();
2783 from = rcu_dereference(grt->from);
David Ahern58e354c2018-02-06 12:14:12 -08002784 if (!grt->dst.error &&
David Ahern4ed591c2018-10-24 13:58:39 -07002785 /* ignore match if it is the default route */
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002786 from && !ipv6_addr_any(&from->fib6_dst.addr) &&
David Ahern58e354c2018-02-06 12:14:12 -08002787 (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
David Ahern44750f82018-02-06 13:17:06 -08002788 NL_SET_ERR_MSG(extack,
2789 "Nexthop has invalid gateway or device mismatch");
David Ahernfc1e64e2018-01-25 16:55:09 -08002790 err = -EINVAL;
2791 }
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002792 rcu_read_unlock();
David Ahernfc1e64e2018-01-25 16:55:09 -08002793
2794 ip6_rt_put(grt);
2795 }
2796
2797 return err;
2798}
2799
David Ahern1edce992018-01-25 16:55:07 -08002800static int ip6_route_check_nh(struct net *net,
2801 struct fib6_config *cfg,
2802 struct net_device **_dev,
2803 struct inet6_dev **idev)
2804{
2805 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2806 struct net_device *dev = _dev ? *_dev : NULL;
2807 struct rt6_info *grt = NULL;
2808 int err = -EHOSTUNREACH;
2809
2810 if (cfg->fc_table) {
David Ahernf4797b32018-01-25 16:55:08 -08002811 int flags = RT6_LOOKUP_F_IFACE;
2812
2813 grt = ip6_nh_lookup_table(net, cfg, gw_addr,
2814 cfg->fc_table, flags);
David Ahern1edce992018-01-25 16:55:07 -08002815 if (grt) {
2816 if (grt->rt6i_flags & RTF_GATEWAY ||
2817 (dev && dev != grt->dst.dev)) {
2818 ip6_rt_put(grt);
2819 grt = NULL;
2820 }
2821 }
2822 }
2823
2824 if (!grt)
David Ahernb75cc8f2018-03-02 08:32:17 -08002825 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1);
David Ahern1edce992018-01-25 16:55:07 -08002826
2827 if (!grt)
2828 goto out;
2829
2830 if (dev) {
2831 if (dev != grt->dst.dev) {
2832 ip6_rt_put(grt);
2833 goto out;
2834 }
2835 } else {
2836 *_dev = dev = grt->dst.dev;
2837 *idev = grt->rt6i_idev;
2838 dev_hold(dev);
2839 in6_dev_hold(grt->rt6i_idev);
2840 }
2841
2842 if (!(grt->rt6i_flags & RTF_GATEWAY))
2843 err = 0;
2844
2845 ip6_rt_put(grt);
2846
2847out:
2848 return err;
2849}
2850
David Ahern9fbb7042018-03-13 08:29:36 -07002851static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
2852 struct net_device **_dev, struct inet6_dev **idev,
2853 struct netlink_ext_ack *extack)
2854{
2855 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2856 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07002857 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07002858 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07002859 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07002860 int err = -EINVAL;
2861
2862 /* if gw_addr is local we will fail to detect this in case
2863 * address is still TENTATIVE (DAD in progress). rt6_lookup()
2864 * will return already-added prefix route via interface that
2865 * prefix route was assigned to, which might be non-loopback.
2866 */
David Ahern232378e2018-03-13 08:29:37 -07002867 if (dev &&
2868 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2869 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07002870 goto out;
2871 }
2872
2873 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
2874 /* IPv6 strictly inhibits using not link-local
2875 * addresses as nexthop address.
2876 * Otherwise, router will not able to send redirects.
2877 * It is very good, but in some (rare!) circumstances
2878 * (SIT, PtP, NBMA NOARP links) it is handy to allow
2879 * some exceptions. --ANK
2880 * We allow IPv4-mapped nexthops to support RFC4798-type
2881 * addressing
2882 */
2883 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
2884 NL_SET_ERR_MSG(extack, "Invalid gateway address");
2885 goto out;
2886 }
2887
2888 if (cfg->fc_flags & RTNH_F_ONLINK)
2889 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
2890 else
2891 err = ip6_route_check_nh(net, cfg, _dev, idev);
2892
2893 if (err)
2894 goto out;
2895 }
2896
2897 /* reload in case device was changed */
2898 dev = *_dev;
2899
2900 err = -EINVAL;
2901 if (!dev) {
2902 NL_SET_ERR_MSG(extack, "Egress device not specified");
2903 goto out;
2904 } else if (dev->flags & IFF_LOOPBACK) {
2905 NL_SET_ERR_MSG(extack,
2906 "Egress device can not be loopback device for this route");
2907 goto out;
2908 }
David Ahern232378e2018-03-13 08:29:37 -07002909
2910 /* if we did not check gw_addr above, do so now that the
2911 * egress device has been resolved.
2912 */
2913 if (need_addr_check &&
2914 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2915 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2916 goto out;
2917 }
2918
David Ahern9fbb7042018-03-13 08:29:36 -07002919 err = 0;
2920out:
2921 return err;
2922}
2923
David Ahern83c442512019-03-27 20:53:50 -07002924static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
2925{
2926 if ((flags & RTF_REJECT) ||
2927 (dev && (dev->flags & IFF_LOOPBACK) &&
2928 !(addr_type & IPV6_ADDR_LOOPBACK) &&
2929 !(flags & RTF_LOCAL)))
2930 return true;
2931
2932 return false;
2933}
2934
2935int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
2936 struct fib6_config *cfg, gfp_t gfp_flags,
2937 struct netlink_ext_ack *extack)
2938{
2939 struct net_device *dev = NULL;
2940 struct inet6_dev *idev = NULL;
2941 int addr_type;
2942 int err;
2943
David Ahernf1741732019-03-27 20:53:57 -07002944 fib6_nh->fib_nh_family = AF_INET6;
2945
David Ahern83c442512019-03-27 20:53:50 -07002946 err = -ENODEV;
2947 if (cfg->fc_ifindex) {
2948 dev = dev_get_by_index(net, cfg->fc_ifindex);
2949 if (!dev)
2950 goto out;
2951 idev = in6_dev_get(dev);
2952 if (!idev)
2953 goto out;
2954 }
2955
2956 if (cfg->fc_flags & RTNH_F_ONLINK) {
2957 if (!dev) {
2958 NL_SET_ERR_MSG(extack,
2959 "Nexthop device required for onlink");
2960 goto out;
2961 }
2962
2963 if (!(dev->flags & IFF_UP)) {
2964 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
2965 err = -ENETDOWN;
2966 goto out;
2967 }
2968
David Ahernad1601a2019-03-27 20:53:56 -07002969 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07002970 }
2971
David Ahernad1601a2019-03-27 20:53:56 -07002972 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07002973
2974 /* We cannot add true routes via loopback here,
2975 * they would result in kernel looping; promote them to reject routes
2976 */
2977 addr_type = ipv6_addr_type(&cfg->fc_dst);
2978 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
2979 /* hold loopback dev/idev if we haven't done so. */
2980 if (dev != net->loopback_dev) {
2981 if (dev) {
2982 dev_put(dev);
2983 in6_dev_put(idev);
2984 }
2985 dev = net->loopback_dev;
2986 dev_hold(dev);
2987 idev = in6_dev_get(dev);
2988 if (!idev) {
2989 err = -ENODEV;
2990 goto out;
2991 }
2992 }
2993 goto set_dev;
2994 }
2995
2996 if (cfg->fc_flags & RTF_GATEWAY) {
2997 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
2998 if (err)
2999 goto out;
3000
David Ahernad1601a2019-03-27 20:53:56 -07003001 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07003002 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07003003 }
3004
3005 err = -ENODEV;
3006 if (!dev)
3007 goto out;
3008
3009 if (idev->cnf.disable_ipv6) {
3010 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3011 err = -EACCES;
3012 goto out;
3013 }
3014
3015 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3016 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3017 err = -ENETDOWN;
3018 goto out;
3019 }
3020
3021 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3022 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003023 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003024
David Ahern979e2762019-03-27 20:53:58 -07003025 err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
3026 cfg->fc_encap_type, cfg, gfp_flags, extack);
3027 if (err)
3028 goto out;
David Ahern83c442512019-03-27 20:53:50 -07003029set_dev:
David Ahernad1601a2019-03-27 20:53:56 -07003030 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003031 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003032 err = 0;
3033out:
3034 if (idev)
3035 in6_dev_put(idev);
3036
3037 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003038 lwtstate_put(fib6_nh->fib_nh_lws);
3039 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003040 if (dev)
3041 dev_put(dev);
3042 }
3043
3044 return err;
3045}
3046
David Aherndac7d0f2019-03-27 20:53:51 -07003047void fib6_nh_release(struct fib6_nh *fib6_nh)
3048{
David Ahern979e2762019-03-27 20:53:58 -07003049 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003050}
3051
David Ahern8d1c8022018-04-17 17:33:26 -07003052static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003053 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003054 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003055{
Daniel Lezcano55786892008-03-04 13:47:47 -08003056 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003057 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003058 struct fib6_table *table;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003059 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003060 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061
David Ahern557c44b2017-04-19 14:19:43 -07003062 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003063 if (cfg->fc_flags & RTF_PCPU) {
3064 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003065 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003066 }
David Ahern557c44b2017-04-19 14:19:43 -07003067
Wei Wang2ea23522017-10-27 17:30:12 -07003068 /* RTF_CACHE is an internal flag; can not be set by userspace */
3069 if (cfg->fc_flags & RTF_CACHE) {
3070 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3071 goto out;
3072 }
3073
David Aherne8478e82018-04-17 17:33:13 -07003074 if (cfg->fc_type > RTN_MAX) {
3075 NL_SET_ERR_MSG(extack, "Invalid route type");
3076 goto out;
3077 }
3078
David Ahernd5d531c2017-05-21 10:12:05 -06003079 if (cfg->fc_dst_len > 128) {
3080 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003081 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003082 }
3083 if (cfg->fc_src_len > 128) {
3084 NL_SET_ERR_MSG(extack, "Invalid source address length");
3085 goto out;
3086 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003088 if (cfg->fc_src_len) {
3089 NL_SET_ERR_MSG(extack,
3090 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003091 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003092 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093#endif
David Ahernfc1e64e2018-01-25 16:55:09 -08003094
Matti Vaittinend71314b2011-11-14 00:14:49 +00003095 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003096 if (cfg->fc_nlinfo.nlh &&
3097 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003098 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003099 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003100 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003101 table = fib6_new_table(net, cfg->fc_table);
3102 }
3103 } else {
3104 table = fib6_new_table(net, cfg->fc_table);
3105 }
David S. Miller38308472011-12-03 18:02:47 -05003106
3107 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003108 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003109
David Ahern93531c62018-04-17 17:33:25 -07003110 err = -ENOMEM;
3111 rt = fib6_info_alloc(gfp_flags);
3112 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003113 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003114
David Ahernd7e774f2018-11-06 12:51:15 -08003115 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3116 extack);
David Ahern767a2212018-10-04 20:07:51 -07003117 if (IS_ERR(rt->fib6_metrics)) {
3118 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003119 /* Do not leave garbage there. */
3120 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003121 goto out;
3122 }
3123
David Ahern93531c62018-04-17 17:33:25 -07003124 if (cfg->fc_flags & RTF_ADDRCONF)
3125 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126
Gao feng1716a962012-04-06 00:13:10 +00003127 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003128 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003129 clock_t_to_jiffies(cfg->fc_expires));
3130 else
David Ahern14895682018-04-17 17:33:17 -07003131 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003132
Thomas Graf86872cb2006-08-22 00:01:08 -07003133 if (cfg->fc_protocol == RTPROT_UNSPEC)
3134 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003135 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003136
David Ahern83c442512019-03-27 20:53:50 -07003137 rt->fib6_table = table;
3138 rt->fib6_metric = cfg->fc_metric;
3139 rt->fib6_type = cfg->fc_type;
David Ahern2b2450c2019-03-27 20:53:52 -07003140 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003141
David Ahern93c2fb22018-04-18 15:38:59 -07003142 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3143 rt->fib6_dst.plen = cfg->fc_dst_len;
3144 if (rt->fib6_dst.plen == 128)
David Ahern3b6761d2018-04-17 17:33:20 -07003145 rt->dst_host = true;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003146
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003148 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3149 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150#endif
David Ahern83c442512019-03-27 20:53:50 -07003151 err = fib6_nh_init(net, &rt->fib6_nh, cfg, gfp_flags, extack);
3152 if (err)
3153 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154
3155 /* We cannot add true routes via loopback here,
David Ahern83c442512019-03-27 20:53:50 -07003156 * they would result in kernel looping; promote them to reject routes
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157 */
David Ahern83c442512019-03-27 20:53:50 -07003158 addr_type = ipv6_addr_type(&cfg->fc_dst);
David Ahernad1601a2019-03-27 20:53:56 -07003159 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh.fib_nh_dev, addr_type))
David Ahern83c442512019-03-27 20:53:50 -07003160 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
David Ahern955ec4c2018-01-24 19:45:29 -08003161
Daniel Walterc3968a82011-04-13 21:10:57 +00003162 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahern83c442512019-03-27 20:53:50 -07003163 struct net_device *dev = fib6_info_nh_dev(rt);
3164
Daniel Walterc3968a82011-04-13 21:10:57 +00003165 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003166 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003167 err = -EINVAL;
3168 goto out;
3169 }
David Ahern93c2fb22018-04-18 15:38:59 -07003170 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3171 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003172 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003173 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003174
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003175 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003176out:
David Ahern93531c62018-04-17 17:33:25 -07003177 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003178 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003179}
3180
David Ahernacb54e32018-04-17 17:33:22 -07003181int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003182 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003183{
David Ahern8d1c8022018-04-17 17:33:26 -07003184 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003185 int err;
3186
David Ahernacb54e32018-04-17 17:33:22 -07003187 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003188 if (IS_ERR(rt))
3189 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003190
David Ahernd4ead6b2018-04-17 17:33:16 -07003191 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003192 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003193
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194 return err;
3195}
3196
David Ahern8d1c8022018-04-17 17:33:26 -07003197static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198{
David Ahernafb1d4b52018-04-17 17:33:11 -07003199 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003200 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003201 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202
David Ahern421842e2018-04-17 17:33:18 -07003203 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003204 err = -ENOENT;
3205 goto out;
3206 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003207
David Ahern93c2fb22018-04-18 15:38:59 -07003208 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003209 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003210 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003211 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003212
Gao feng6825a262012-09-19 19:25:34 +00003213out:
David Ahern93531c62018-04-17 17:33:25 -07003214 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003215 return err;
3216}
3217
David Ahern8d1c8022018-04-17 17:33:26 -07003218int ip6_del_rt(struct net *net, struct fib6_info *rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003219{
David Ahernafb1d4b52018-04-17 17:33:11 -07003220 struct nl_info info = { .nl_net = net };
3221
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003222 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003223}
3224
David Ahern8d1c8022018-04-17 17:33:26 -07003225static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003226{
3227 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003228 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003229 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003230 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003231 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003232
David Ahern421842e2018-04-17 17:33:18 -07003233 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003234 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003235 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003236 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003237
David Ahern93c2fb22018-04-18 15:38:59 -07003238 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003239 struct fib6_info *sibling, *next_sibling;
David Ahern0ae81332017-02-02 12:37:08 -08003240
David Ahern16a16cd2017-02-02 12:37:11 -08003241 /* prefer to send a single notification with all hops */
3242 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3243 if (skb) {
3244 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3245
David Ahernd4ead6b2018-04-17 17:33:16 -07003246 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003247 NULL, NULL, 0, RTM_DELROUTE,
3248 info->portid, seq, 0) < 0) {
3249 kfree_skb(skb);
3250 skb = NULL;
3251 } else
3252 info->skip_notify = 1;
3253 }
3254
David Ahern0ae81332017-02-02 12:37:08 -08003255 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003256 &rt->fib6_siblings,
3257 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003258 err = fib6_del(sibling, info);
3259 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003260 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003261 }
3262 }
3263
3264 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003265out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003266 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003267out_put:
David Ahern93531c62018-04-17 17:33:25 -07003268 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003269
3270 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003271 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003272 info->nlh, gfp_any());
3273 }
David Ahern0ae81332017-02-02 12:37:08 -08003274 return err;
3275}
3276
David Ahern23fb93a2018-04-17 17:33:23 -07003277static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
3278{
3279 int rc = -ESRCH;
3280
3281 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3282 goto out;
3283
3284 if (cfg->fc_flags & RTF_GATEWAY &&
3285 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3286 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003287
3288 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003289out:
3290 return rc;
3291}
3292
David Ahern333c4302017-05-21 10:12:04 -06003293static int ip6_route_del(struct fib6_config *cfg,
3294 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003295{
David Ahern8d1c8022018-04-17 17:33:26 -07003296 struct rt6_info *rt_cache;
Thomas Grafc71099a2006-08-04 23:20:06 -07003297 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003298 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003299 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003300 int err = -ESRCH;
3301
Daniel Lezcano55786892008-03-04 13:47:47 -08003302 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003303 if (!table) {
3304 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003305 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003306 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003307
Wei Wang66f5d6c2017-10-06 12:06:10 -07003308 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003309
3310 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003311 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003312 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003313 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003314
Linus Torvalds1da177e2005-04-16 15:20:36 -07003315 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003316 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003317 struct fib6_nh *nh;
3318
Wei Wang2b760fc2017-10-06 12:06:03 -07003319 if (cfg->fc_flags & RTF_CACHE) {
David Ahern7e4b5122019-04-16 14:36:00 -07003320 struct fib6_result res = {
3321 .f6i = rt,
3322 };
David Ahern23fb93a2018-04-17 17:33:23 -07003323 int rc;
3324
David Ahern7e4b5122019-04-16 14:36:00 -07003325 rt_cache = rt6_find_cached_rt(&res,
3326 &cfg->fc_dst,
Wei Wang2b760fc2017-10-06 12:06:03 -07003327 &cfg->fc_src);
David Ahern23fb93a2018-04-17 17:33:23 -07003328 if (rt_cache) {
3329 rc = ip6_del_cached_rt(rt_cache, cfg);
Eric Dumazet9e575012018-05-09 10:05:46 -07003330 if (rc != -ESRCH) {
3331 rcu_read_unlock();
David Ahern23fb93a2018-04-17 17:33:23 -07003332 return rc;
Eric Dumazet9e575012018-05-09 10:05:46 -07003333 }
David Ahern23fb93a2018-04-17 17:33:23 -07003334 }
3335 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003336 }
David Ahernad1601a2019-03-27 20:53:56 -07003337
3338 nh = &rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003339 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003340 (!nh->fib_nh_dev ||
3341 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003342 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003343 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003344 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003346 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003347 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003348 if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
Mantas Mc2ed1882016-12-16 10:30:59 +02003349 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003350 if (!fib6_info_hold_safe(rt))
3351 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003352 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003353
David Ahern0ae81332017-02-02 12:37:08 -08003354 /* if gateway was specified only delete the one hop */
3355 if (cfg->fc_flags & RTF_GATEWAY)
3356 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3357
3358 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359 }
3360 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003361 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003362
3363 return err;
3364}
3365
David S. Miller6700c272012-07-17 03:29:28 -07003366static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003367{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003368 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003369 struct rt6_info *rt, *nrt = NULL;
David Ahern85bd05d2019-04-16 14:36:01 -07003370 struct fib6_result res = {};
David S. Millere8599ff2012-07-11 23:43:53 -07003371 struct ndisc_options ndopts;
3372 struct inet6_dev *in6_dev;
3373 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003374 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003375 int optlen, on_link;
3376 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07003377
Simon Horman29a3cad2013-05-28 20:34:26 +00003378 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003379 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07003380
3381 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07003382 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003383 return;
3384 }
3385
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003386 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07003387
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003388 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003389 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003390 return;
3391 }
3392
David S. Miller6e157b62012-07-12 00:05:02 -07003393 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003394 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003395 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003396 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07003397 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003398 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003399 return;
3400 }
3401
3402 in6_dev = __in6_dev_get(skb->dev);
3403 if (!in6_dev)
3404 return;
3405 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
3406 return;
3407
3408 /* RFC2461 8.1:
3409 * The IP source address of the Redirect MUST be the same as the current
3410 * first-hop router for the specified ICMP Destination Address.
3411 */
3412
Alexander Aringf997c552016-06-15 21:20:23 +02003413 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003414 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
3415 return;
3416 }
David S. Miller6e157b62012-07-12 00:05:02 -07003417
3418 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003419 if (ndopts.nd_opts_tgt_lladdr) {
3420 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
3421 skb->dev);
3422 if (!lladdr) {
3423 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
3424 return;
3425 }
3426 }
3427
David S. Miller6e157b62012-07-12 00:05:02 -07003428 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01003429 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07003430 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
3431 return;
3432 }
3433
3434 /* Redirect received -> path was valid.
3435 * Look, redirects are sent only in response to data packets,
3436 * so that this nexthop apparently is reachable. --ANK
3437 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02003438 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07003439
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003440 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07003441 if (!neigh)
3442 return;
3443
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444 /*
3445 * We have finally decided to accept it.
3446 */
3447
Alexander Aringf997c552016-06-15 21:20:23 +02003448 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003449 NEIGH_UPDATE_F_WEAK_OVERRIDE|
3450 NEIGH_UPDATE_F_OVERRIDE|
3451 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02003452 NEIGH_UPDATE_F_ISROUTER)),
3453 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003454
David Ahern4d85cd02018-04-20 15:37:59 -07003455 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07003456 res.f6i = rcu_dereference(rt->from);
Wei Wange873e4b2018-07-21 20:56:32 -07003457 /* This fib6_info_hold() is safe here because we hold reference to rt
3458 * and rt already holds reference to fib6_info.
3459 */
David Ahern85bd05d2019-04-16 14:36:01 -07003460 fib6_info_hold(res.f6i);
David Ahern4d85cd02018-04-20 15:37:59 -07003461 rcu_read_unlock();
David Ahern8a14e462018-04-23 11:32:07 -07003462
David Ahern85bd05d2019-04-16 14:36:01 -07003463 res.nh = &res.f6i->fib6_nh;
3464 nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05003465 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466 goto out;
3467
3468 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
3469 if (on_link)
3470 nrt->rt6i_flags &= ~RTF_GATEWAY;
3471
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003472 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003473
Wei Wang2b760fc2017-10-06 12:06:03 -07003474 /* No need to remove rt from the exception table if rt is
3475 * a cached route because rt6_insert_exception() will
3476 * takes care of it
3477 */
David Ahern85bd05d2019-04-16 14:36:01 -07003478 if (rt6_insert_exception(nrt, res.f6i)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07003479 dst_release_immediate(&nrt->dst);
3480 goto out;
3481 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003482
Changli Gaod8d1f302010-06-10 23:31:35 -07003483 netevent.old = &rt->dst;
3484 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003485 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00003486 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07003487 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
3488
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489out:
David Ahern85bd05d2019-04-16 14:36:01 -07003490 fib6_info_release(res.f6i);
David S. Millere8599ff2012-07-11 23:43:53 -07003491 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07003492}
3493
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003494#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07003495static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003496 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003497 const struct in6_addr *gwaddr,
3498 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003499{
David Ahern830218c2016-10-24 10:52:35 -07003500 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
3501 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003502 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07003503 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003504 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003505
David Ahern830218c2016-10-24 10:52:35 -07003506 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003507 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003508 return NULL;
3509
Wei Wang66f5d6c2017-10-06 12:06:10 -07003510 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07003511 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003512 if (!fn)
3513 goto out;
3514
Wei Wang66f5d6c2017-10-06 12:06:10 -07003515 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003516 if (rt->fib6_nh.fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003517 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07003518 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahernbdf00462019-04-05 16:30:26 -07003519 !rt->fib6_nh.fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003520 continue;
David Ahernad1601a2019-03-27 20:53:56 -07003521 if (!ipv6_addr_equal(&rt->fib6_nh.fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003522 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003523 if (!fib6_info_hold_safe(rt))
3524 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003525 break;
3526 }
3527out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003528 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003529 return rt;
3530}
3531
David Ahern8d1c8022018-04-17 17:33:26 -07003532static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003533 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003534 const struct in6_addr *gwaddr,
3535 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00003536 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003537{
Thomas Graf86872cb2006-08-22 00:01:08 -07003538 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08003539 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07003540 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07003541 .fc_dst_len = prefixlen,
3542 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
3543 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003544 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003545 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003546 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08003547 .fc_nlinfo.nlh = NULL,
3548 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003549 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003550
David Ahern830218c2016-10-24 10:52:35 -07003551 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003552 cfg.fc_dst = *prefix;
3553 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07003554
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08003555 /* We should treat it as a default route if prefix length is 0. */
3556 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07003557 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003558
David Ahernacb54e32018-04-17 17:33:22 -07003559 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003560
David Ahern830218c2016-10-24 10:52:35 -07003561 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003562}
3563#endif
3564
David Ahern8d1c8022018-04-17 17:33:26 -07003565struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003566 const struct in6_addr *addr,
3567 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003568{
David Ahern830218c2016-10-24 10:52:35 -07003569 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07003570 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07003571 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003572
David Ahernafb1d4b52018-04-17 17:33:11 -07003573 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003574 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003575 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003576
Wei Wang66f5d6c2017-10-06 12:06:10 -07003577 rcu_read_lock();
3578 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernad1601a2019-03-27 20:53:56 -07003579 struct fib6_nh *nh = &rt->fib6_nh;
3580
3581 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07003582 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07003583 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003584 break;
3585 }
Wei Wange873e4b2018-07-21 20:56:32 -07003586 if (rt && !fib6_info_hold_safe(rt))
3587 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003588 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003589 return rt;
3590}
3591
David Ahern8d1c8022018-04-17 17:33:26 -07003592struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003593 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08003594 struct net_device *dev,
3595 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003596{
Thomas Graf86872cb2006-08-22 00:01:08 -07003597 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07003598 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08003599 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07003600 .fc_ifindex = dev->ifindex,
3601 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
3602 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003603 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003604 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003605 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08003606 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07003607 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003608 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003609
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003610 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003611
David Ahernacb54e32018-04-17 17:33:22 -07003612 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07003613 struct fib6_table *table;
3614
3615 table = fib6_get_table(dev_net(dev), cfg.fc_table);
3616 if (table)
3617 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
3618 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003619
David Ahernafb1d4b52018-04-17 17:33:11 -07003620 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003621}
3622
David Ahernafb1d4b52018-04-17 17:33:11 -07003623static void __rt6_purge_dflt_routers(struct net *net,
3624 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625{
David Ahern8d1c8022018-04-17 17:33:26 -07003626 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003627
3628restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003629 rcu_read_lock();
3630 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07003631 struct net_device *dev = fib6_info_nh_dev(rt);
3632 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
3633
David Ahern93c2fb22018-04-18 15:38:59 -07003634 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07003635 (!idev || idev->cnf.accept_ra != 2) &&
3636 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07003637 rcu_read_unlock();
3638 ip6_del_rt(net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639 goto restart;
3640 }
3641 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003642 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07003643
3644 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
3645}
3646
3647void rt6_purge_dflt_routers(struct net *net)
3648{
3649 struct fib6_table *table;
3650 struct hlist_head *head;
3651 unsigned int h;
3652
3653 rcu_read_lock();
3654
3655 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3656 head = &net->ipv6.fib_table_hash[h];
3657 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
3658 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07003659 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07003660 }
3661 }
3662
3663 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003664}
3665
Daniel Lezcano55786892008-03-04 13:47:47 -08003666static void rtmsg_to_fib6_config(struct net *net,
3667 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07003668 struct fib6_config *cfg)
3669{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003670 *cfg = (struct fib6_config){
3671 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
3672 : RT6_TABLE_MAIN,
3673 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07003674 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003675 .fc_expires = rtmsg->rtmsg_info,
3676 .fc_dst_len = rtmsg->rtmsg_dst_len,
3677 .fc_src_len = rtmsg->rtmsg_src_len,
3678 .fc_flags = rtmsg->rtmsg_flags,
3679 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07003680
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003681 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003682
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003683 .fc_dst = rtmsg->rtmsg_dst,
3684 .fc_src = rtmsg->rtmsg_src,
3685 .fc_gateway = rtmsg->rtmsg_gateway,
3686 };
Thomas Graf86872cb2006-08-22 00:01:08 -07003687}
3688
Daniel Lezcano55786892008-03-04 13:47:47 -08003689int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003690{
Thomas Graf86872cb2006-08-22 00:01:08 -07003691 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003692 struct in6_rtmsg rtmsg;
3693 int err;
3694
Ian Morris67ba4152014-08-24 21:53:10 +01003695 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003696 case SIOCADDRT: /* Add a route */
3697 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00003698 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003699 return -EPERM;
3700 err = copy_from_user(&rtmsg, arg,
3701 sizeof(struct in6_rtmsg));
3702 if (err)
3703 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07003704
Daniel Lezcano55786892008-03-04 13:47:47 -08003705 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07003706
Linus Torvalds1da177e2005-04-16 15:20:36 -07003707 rtnl_lock();
3708 switch (cmd) {
3709 case SIOCADDRT:
David Ahernacb54e32018-04-17 17:33:22 -07003710 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711 break;
3712 case SIOCDELRT:
David Ahern333c4302017-05-21 10:12:04 -06003713 err = ip6_route_del(&cfg, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714 break;
3715 default:
3716 err = -EINVAL;
3717 }
3718 rtnl_unlock();
3719
3720 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07003721 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722
3723 return -EINVAL;
3724}
3725
3726/*
3727 * Drop the packet on the floor
3728 */
3729
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07003730static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003731{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003732 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00003733 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003734 switch (ipstats_mib_noroutes) {
3735 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07003736 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00003737 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputrabdb7cc62018-04-16 13:42:16 -04003738 IP6_INC_STATS(dev_net(dst->dev),
3739 __in6_dev_get_safely(skb->dev),
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003740 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003741 break;
3742 }
3743 /* FALLTHROUGH */
3744 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003745 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
3746 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003747 break;
3748 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00003749 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 kfree_skb(skb);
3751 return 0;
3752}
3753
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003754static int ip6_pkt_discard(struct sk_buff *skb)
3755{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003756 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003757}
3758
Eric W. Biedermanede20592015-10-07 16:48:47 -05003759static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760{
Eric Dumazetadf30902009-06-02 05:19:30 +00003761 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003762 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003763}
3764
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003765static int ip6_pkt_prohibit(struct sk_buff *skb)
3766{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003767 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003768}
3769
Eric W. Biedermanede20592015-10-07 16:48:47 -05003770static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003771{
Eric Dumazetadf30902009-06-02 05:19:30 +00003772 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003773 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003774}
3775
Linus Torvalds1da177e2005-04-16 15:20:36 -07003776/*
3777 * Allocate a dst for local (unicast / anycast) address.
3778 */
3779
David Ahern360a9882018-04-18 15:39:00 -07003780struct fib6_info *addrconf_f6i_alloc(struct net *net,
3781 struct inet6_dev *idev,
3782 const struct in6_addr *addr,
3783 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003784{
David Ahernc7a1ce32019-03-21 05:21:35 -07003785 struct fib6_config cfg = {
3786 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
3787 .fc_ifindex = idev->dev->ifindex,
3788 .fc_flags = RTF_UP | RTF_ADDRCONF | RTF_NONEXTHOP,
3789 .fc_dst = *addr,
3790 .fc_dst_len = 128,
3791 .fc_protocol = RTPROT_KERNEL,
3792 .fc_nlinfo.nl_net = net,
3793 .fc_ignore_dev_down = true,
3794 };
David Ahern5f02ce242016-09-10 12:09:54 -07003795
David Aherne8478e82018-04-17 17:33:13 -07003796 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07003797 cfg.fc_type = RTN_ANYCAST;
3798 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07003799 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07003800 cfg.fc_type = RTN_LOCAL;
3801 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07003802 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803
David Ahernc7a1ce32019-03-21 05:21:35 -07003804 return ip6_route_info_create(&cfg, gfp_flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805}
3806
Daniel Walterc3968a82011-04-13 21:10:57 +00003807/* remove deleted ip from prefsrc entries */
3808struct arg_dev_net_ip {
3809 struct net_device *dev;
3810 struct net *net;
3811 struct in6_addr *addr;
3812};
3813
David Ahern8d1c8022018-04-17 17:33:26 -07003814static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00003815{
3816 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
3817 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
3818 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
3819
David Ahernad1601a2019-03-27 20:53:56 -07003820 if (((void *)rt->fib6_nh.fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07003821 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07003822 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07003823 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003824 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07003825 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07003826 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003827 }
3828 return 0;
3829}
3830
3831void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
3832{
3833 struct net *net = dev_net(ifp->idev->dev);
3834 struct arg_dev_net_ip adni = {
3835 .dev = ifp->idev->dev,
3836 .net = net,
3837 .addr = &ifp->addr,
3838 };
Li RongQing0c3584d2013-12-27 16:32:38 +08003839 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00003840}
3841
David Ahern2b2450c2019-03-27 20:53:52 -07003842#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003843
3844/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07003845static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003846{
3847 struct in6_addr *gateway = (struct in6_addr *)arg;
3848
David Ahern93c2fb22018-04-18 15:38:59 -07003849 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Ahernbdf00462019-04-05 16:30:26 -07003850 rt->fib6_nh.fib_nh_gw_family &&
David Ahernad1601a2019-03-27 20:53:56 -07003851 ipv6_addr_equal(gateway, &rt->fib6_nh.fib_nh_gw6)) {
Duan Jiongbe7a0102014-05-15 15:56:14 +08003852 return -1;
3853 }
Wei Wangb16cb452017-10-06 12:06:00 -07003854
3855 /* Further clean up cached routes in exception table.
3856 * This is needed because cached route may have a different
3857 * gateway than its 'parent' in the case of an ip redirect.
3858 */
3859 rt6_exceptions_clean_tohost(rt, gateway);
3860
Duan Jiongbe7a0102014-05-15 15:56:14 +08003861 return 0;
3862}
3863
3864void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
3865{
3866 fib6_clean_all(net, fib6_clean_tohost, gateway);
3867}
3868
Ido Schimmel2127d952018-01-07 12:45:03 +02003869struct arg_netdev_event {
3870 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003871 union {
3872 unsigned int nh_flags;
3873 unsigned long event;
3874 };
Ido Schimmel2127d952018-01-07 12:45:03 +02003875};
3876
David Ahern8d1c8022018-04-17 17:33:26 -07003877static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003878{
David Ahern8d1c8022018-04-17 17:33:26 -07003879 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003880 struct fib6_node *fn;
3881
David Ahern93c2fb22018-04-18 15:38:59 -07003882 fn = rcu_dereference_protected(rt->fib6_node,
3883 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003884 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07003885 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003886 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07003887 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07003888 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003889 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07003890 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07003891 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003892 }
3893
3894 return NULL;
3895}
3896
David Ahern8d1c8022018-04-17 17:33:26 -07003897static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003898{
David Ahernad1601a2019-03-27 20:53:56 -07003899 if (rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ||
3900 (rt->fib6_nh.fib_nh_flags & RTNH_F_LINKDOWN &&
3901 ip6_ignore_linkdown(rt->fib6_nh.fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003902 return true;
3903
3904 return false;
3905}
3906
David Ahern8d1c8022018-04-17 17:33:26 -07003907static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003908{
David Ahern8d1c8022018-04-17 17:33:26 -07003909 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003910 int total = 0;
3911
3912 if (!rt6_is_dead(rt))
David Ahernad1601a2019-03-27 20:53:56 -07003913 total += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003914
David Ahern93c2fb22018-04-18 15:38:59 -07003915 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003916 if (!rt6_is_dead(iter))
David Ahernad1601a2019-03-27 20:53:56 -07003917 total += iter->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003918 }
3919
3920 return total;
3921}
3922
David Ahern8d1c8022018-04-17 17:33:26 -07003923static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003924{
3925 int upper_bound = -1;
3926
3927 if (!rt6_is_dead(rt)) {
David Ahernad1601a2019-03-27 20:53:56 -07003928 *weight += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003929 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
3930 total) - 1;
3931 }
David Ahernad1601a2019-03-27 20:53:56 -07003932 atomic_set(&rt->fib6_nh.fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003933}
3934
David Ahern8d1c8022018-04-17 17:33:26 -07003935static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003936{
David Ahern8d1c8022018-04-17 17:33:26 -07003937 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003938 int weight = 0;
3939
3940 rt6_upper_bound_set(rt, &weight, total);
3941
David Ahern93c2fb22018-04-18 15:38:59 -07003942 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003943 rt6_upper_bound_set(iter, &weight, total);
3944}
3945
David Ahern8d1c8022018-04-17 17:33:26 -07003946void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003947{
David Ahern8d1c8022018-04-17 17:33:26 -07003948 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003949 int total;
3950
3951 /* In case the entire multipath route was marked for flushing,
3952 * then there is no need to rebalance upon the removal of every
3953 * sibling route.
3954 */
David Ahern93c2fb22018-04-18 15:38:59 -07003955 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003956 return;
3957
3958 /* During lookup routes are evaluated in order, so we need to
3959 * make sure upper bounds are assigned from the first sibling
3960 * onwards.
3961 */
3962 first = rt6_multipath_first_sibling(rt);
3963 if (WARN_ON_ONCE(!first))
3964 return;
3965
3966 total = rt6_multipath_total_weight(first);
3967 rt6_multipath_upper_bound_set(first, total);
3968}
3969
David Ahern8d1c8022018-04-17 17:33:26 -07003970static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02003971{
3972 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07003973 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02003974
David Ahernad1601a2019-03-27 20:53:56 -07003975 if (rt != net->ipv6.fib6_null_entry &&
3976 rt->fib6_nh.fib_nh_dev == arg->dev) {
3977 rt->fib6_nh.fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07003978 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003979 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02003980 }
Ido Schimmel2127d952018-01-07 12:45:03 +02003981
3982 return 0;
3983}
3984
3985void rt6_sync_up(struct net_device *dev, unsigned int nh_flags)
3986{
3987 struct arg_netdev_event arg = {
3988 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02003989 {
3990 .nh_flags = nh_flags,
3991 },
Ido Schimmel2127d952018-01-07 12:45:03 +02003992 };
3993
3994 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
3995 arg.nh_flags |= RTNH_F_LINKDOWN;
3996
3997 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
3998}
3999
David Ahern8d1c8022018-04-17 17:33:26 -07004000static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004001 const struct net_device *dev)
4002{
David Ahern8d1c8022018-04-17 17:33:26 -07004003 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004004
David Ahernad1601a2019-03-27 20:53:56 -07004005 if (rt->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004006 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07004007 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004008 if (iter->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004009 return true;
4010
4011 return false;
4012}
4013
David Ahern8d1c8022018-04-17 17:33:26 -07004014static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004015{
David Ahern8d1c8022018-04-17 17:33:26 -07004016 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004017
4018 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004019 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004020 iter->should_flush = 1;
4021}
4022
David Ahern8d1c8022018-04-17 17:33:26 -07004023static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004024 const struct net_device *down_dev)
4025{
David Ahern8d1c8022018-04-17 17:33:26 -07004026 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004027 unsigned int dead = 0;
4028
David Ahernad1601a2019-03-27 20:53:56 -07004029 if (rt->fib6_nh.fib_nh_dev == down_dev ||
4030 rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004031 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004032 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004033 if (iter->fib6_nh.fib_nh_dev == down_dev ||
4034 iter->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004035 dead++;
4036
4037 return dead;
4038}
4039
David Ahern8d1c8022018-04-17 17:33:26 -07004040static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004041 const struct net_device *dev,
4042 unsigned int nh_flags)
4043{
David Ahern8d1c8022018-04-17 17:33:26 -07004044 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004045
David Ahernad1601a2019-03-27 20:53:56 -07004046 if (rt->fib6_nh.fib_nh_dev == dev)
4047 rt->fib6_nh.fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004048 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004049 if (iter->fib6_nh.fib_nh_dev == dev)
4050 iter->fib6_nh.fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004051}
4052
David Aherna1a22c12017-01-18 07:40:36 -08004053/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004054static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004055{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004056 const struct arg_netdev_event *arg = p_arg;
4057 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004058 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004059
David Ahern421842e2018-04-17 17:33:18 -07004060 if (rt == net->ipv6.fib6_null_entry)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004061 return 0;
4062
4063 switch (arg->event) {
4064 case NETDEV_UNREGISTER:
David Ahernad1601a2019-03-27 20:53:56 -07004065 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004066 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004067 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004068 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004069 if (!rt->fib6_nsiblings)
David Ahernad1601a2019-03-27 20:53:56 -07004070 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004071 if (rt6_multipath_uses_dev(rt, dev)) {
4072 unsigned int count;
4073
4074 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004075 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004076 rt6_multipath_flush(rt);
4077 return -1;
4078 }
4079 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4080 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004081 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004082 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004083 }
4084 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004085 case NETDEV_CHANGE:
David Ahernad1601a2019-03-27 20:53:56 -07004086 if (rt->fib6_nh.fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004087 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004088 break;
David Ahernad1601a2019-03-27 20:53:56 -07004089 rt->fib6_nh.fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004090 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004091 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004092 }
David S. Millerc159d302011-12-26 15:24:36 -05004093
Linus Torvalds1da177e2005-04-16 15:20:36 -07004094 return 0;
4095}
4096
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004097void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004098{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004099 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004100 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004101 {
4102 .event = event,
4103 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004104 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004105 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004106
David Ahern7c6bb7d2018-10-11 20:17:21 -07004107 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4108 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4109 else
4110 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004111}
4112
4113void rt6_disable_ip(struct net_device *dev, unsigned long event)
4114{
4115 rt6_sync_down_dev(dev, event);
4116 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4117 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004118}
4119
Eric Dumazet95c96172012-04-15 05:58:06 +00004120struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004121 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004122 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004123};
4124
David Ahern8d1c8022018-04-17 17:33:26 -07004125static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004126{
4127 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4128 struct inet6_dev *idev;
4129
4130 /* In IPv6 pmtu discovery is not optional,
4131 so that RTAX_MTU lock cannot disable it.
4132 We still use this lock to block changes
4133 caused by addrconf/ndisc.
4134 */
4135
4136 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004137 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004138 return 0;
4139
4140 /* For administrative MTU increase, there is no way to discover
4141 IPv6 PMTU increase, so PMTU increase should be updated here.
4142 Since RFC 1981 doesn't include administrative MTU increase
4143 update PMTU increase is a MUST. (i.e. jumbo frame)
4144 */
David Ahernad1601a2019-03-27 20:53:56 -07004145 if (rt->fib6_nh.fib_nh_dev == arg->dev &&
David Ahernd4ead6b2018-04-17 17:33:16 -07004146 !fib6_metric_locked(rt, RTAX_MTU)) {
4147 u32 mtu = rt->fib6_pmtu;
4148
4149 if (mtu >= arg->mtu ||
4150 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4151 fib6_metric_set(rt, RTAX_MTU, arg->mtu);
4152
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004153 spin_lock_bh(&rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01004154 rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004155 spin_unlock_bh(&rt6_exception_lock);
Simon Arlott566cfd82007-07-26 00:09:55 -07004156 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004157 return 0;
4158}
4159
Eric Dumazet95c96172012-04-15 05:58:06 +00004160void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004161{
Thomas Grafc71099a2006-08-04 23:20:06 -07004162 struct rt6_mtu_change_arg arg = {
4163 .dev = dev,
4164 .mtu = mtu,
4165 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004166
Li RongQing0c3584d2013-12-27 16:32:38 +08004167 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004168}
4169
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004170static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07004171 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004172 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004173 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004174 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004175 [RTA_PRIORITY] = { .type = NLA_U32 },
4176 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004177 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004178 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004179 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4180 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004181 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004182 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004183 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004184 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004185 [RTA_IP_PROTO] = { .type = NLA_U8 },
4186 [RTA_SPORT] = { .type = NLA_U16 },
4187 [RTA_DPORT] = { .type = NLA_U16 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004188};
4189
4190static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004191 struct fib6_config *cfg,
4192 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004193{
Thomas Graf86872cb2006-08-22 00:01:08 -07004194 struct rtmsg *rtm;
4195 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004196 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004197 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004198
Johannes Bergfceb6432017-04-12 14:34:07 +02004199 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy,
David Aherndac9c972018-10-07 20:16:24 -07004200 extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004201 if (err < 0)
4202 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004203
Thomas Graf86872cb2006-08-22 00:01:08 -07004204 err = -EINVAL;
4205 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004206
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004207 *cfg = (struct fib6_config){
4208 .fc_table = rtm->rtm_table,
4209 .fc_dst_len = rtm->rtm_dst_len,
4210 .fc_src_len = rtm->rtm_src_len,
4211 .fc_flags = RTF_UP,
4212 .fc_protocol = rtm->rtm_protocol,
4213 .fc_type = rtm->rtm_type,
4214
4215 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4216 .fc_nlinfo.nlh = nlh,
4217 .fc_nlinfo.nl_net = sock_net(skb->sk),
4218 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004219
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004220 if (rtm->rtm_type == RTN_UNREACHABLE ||
4221 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004222 rtm->rtm_type == RTN_PROHIBIT ||
4223 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004224 cfg->fc_flags |= RTF_REJECT;
4225
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004226 if (rtm->rtm_type == RTN_LOCAL)
4227 cfg->fc_flags |= RTF_LOCAL;
4228
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004229 if (rtm->rtm_flags & RTM_F_CLONED)
4230 cfg->fc_flags |= RTF_CACHE;
4231
David Ahernfc1e64e2018-01-25 16:55:09 -08004232 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4233
Thomas Graf86872cb2006-08-22 00:01:08 -07004234 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004235 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004236 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004237 }
David Aherne3818542019-02-26 09:00:03 -08004238 if (tb[RTA_VIA]) {
4239 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4240 goto errout;
4241 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004242
4243 if (tb[RTA_DST]) {
4244 int plen = (rtm->rtm_dst_len + 7) >> 3;
4245
4246 if (nla_len(tb[RTA_DST]) < plen)
4247 goto errout;
4248
4249 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004250 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004251
4252 if (tb[RTA_SRC]) {
4253 int plen = (rtm->rtm_src_len + 7) >> 3;
4254
4255 if (nla_len(tb[RTA_SRC]) < plen)
4256 goto errout;
4257
4258 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004259 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004260
Daniel Walterc3968a82011-04-13 21:10:57 +00004261 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004262 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004263
Thomas Graf86872cb2006-08-22 00:01:08 -07004264 if (tb[RTA_OIF])
4265 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4266
4267 if (tb[RTA_PRIORITY])
4268 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4269
4270 if (tb[RTA_METRICS]) {
4271 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4272 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004273 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004274
4275 if (tb[RTA_TABLE])
4276 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4277
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004278 if (tb[RTA_MULTIPATH]) {
4279 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4280 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004281
4282 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004283 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004284 if (err < 0)
4285 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004286 }
4287
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004288 if (tb[RTA_PREF]) {
4289 pref = nla_get_u8(tb[RTA_PREF]);
4290 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4291 pref != ICMPV6_ROUTER_PREF_HIGH)
4292 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4293 cfg->fc_flags |= RTF_PREF(pref);
4294 }
4295
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004296 if (tb[RTA_ENCAP])
4297 cfg->fc_encap = tb[RTA_ENCAP];
4298
David Ahern9ed59592017-01-17 14:57:36 -08004299 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004300 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4301
David Ahernc255bd62017-05-27 16:19:27 -06004302 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004303 if (err < 0)
4304 goto errout;
4305 }
4306
Xin Long32bc2012015-12-16 17:50:11 +08004307 if (tb[RTA_EXPIRES]) {
4308 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4309
4310 if (addrconf_finite_timeout(timeout)) {
4311 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
4312 cfg->fc_flags |= RTF_EXPIRES;
4313 }
4314 }
4315
Thomas Graf86872cb2006-08-22 00:01:08 -07004316 err = 0;
4317errout:
4318 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004319}
4320
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004321struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07004322 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004323 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004324 struct list_head next;
4325};
4326
David Ahernd4ead6b2018-04-17 17:33:16 -07004327static int ip6_route_info_append(struct net *net,
4328 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07004329 struct fib6_info *rt,
4330 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004331{
4332 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004333 int err = -EEXIST;
4334
4335 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004336 /* check if fib6_info already exists */
4337 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004338 return err;
4339 }
4340
4341 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
4342 if (!nh)
4343 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07004344 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004345 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
4346 list_add_tail(&nh->next, rt6_nh_list);
4347
4348 return 0;
4349}
4350
David Ahern8d1c8022018-04-17 17:33:26 -07004351static void ip6_route_mpath_notify(struct fib6_info *rt,
4352 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08004353 struct nl_info *info,
4354 __u16 nlflags)
4355{
4356 /* if this is an APPEND route, then rt points to the first route
4357 * inserted and rt_last points to last route inserted. Userspace
4358 * wants a consistent dump of the route which starts at the first
4359 * nexthop. Since sibling routes are always added at the end of
4360 * the list, find the first sibling of the last route appended
4361 */
David Ahern93c2fb22018-04-18 15:38:59 -07004362 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
4363 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07004364 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07004365 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08004366 }
4367
4368 if (rt)
4369 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4370}
4371
David Ahern333c4302017-05-21 10:12:04 -06004372static int ip6_route_multipath_add(struct fib6_config *cfg,
4373 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004374{
David Ahern8d1c8022018-04-17 17:33:26 -07004375 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08004376 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004377 struct fib6_config r_cfg;
4378 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07004379 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004380 struct rt6_nh *err_nh;
4381 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08004382 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004383 int remaining;
4384 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004385 int err = 1;
4386 int nhn = 0;
4387 int replace = (cfg->fc_nlinfo.nlh &&
4388 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
4389 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004390
David Ahern3b1137f2017-02-02 12:37:10 -08004391 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
4392 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
4393 nlflags |= NLM_F_APPEND;
4394
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02004395 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004396 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004397
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004398 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07004399 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004400 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004401 while (rtnh_ok(rtnh, remaining)) {
4402 memcpy(&r_cfg, cfg, sizeof(*cfg));
4403 if (rtnh->rtnh_ifindex)
4404 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4405
4406 attrlen = rtnh_attrlen(rtnh);
4407 if (attrlen > 0) {
4408 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4409
4410 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4411 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004412 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004413 r_cfg.fc_flags |= RTF_GATEWAY;
4414 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004415 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
4416 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
4417 if (nla)
4418 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004419 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004420
David Ahern68e2ffd2018-03-20 10:06:59 -07004421 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07004422 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004423 if (IS_ERR(rt)) {
4424 err = PTR_ERR(rt);
4425 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004426 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004427 }
David Ahernb5d2d752018-07-15 09:35:19 -07004428 if (!rt6_qualify_for_ecmp(rt)) {
4429 err = -EINVAL;
4430 NL_SET_ERR_MSG(extack,
4431 "Device only routes can not be added for IPv6 using the multipath API.");
4432 fib6_info_release(rt);
4433 goto cleanup;
4434 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004435
David Ahernad1601a2019-03-27 20:53:56 -07004436 rt->fib6_nh.fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02004437
David Ahernd4ead6b2018-04-17 17:33:16 -07004438 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
4439 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004440 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07004441 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004442 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004443 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004444
4445 rtnh = rtnh_next(rtnh, &remaining);
4446 }
4447
David Ahern3b1137f2017-02-02 12:37:10 -08004448 /* for add and replace send one notification with all nexthops.
4449 * Skip the notification in fib6_add_rt2node and send one with
4450 * the full route when done
4451 */
4452 info->skip_notify = 1;
4453
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004454 err_nh = NULL;
4455 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004456 err = __ip6_ins_rt(nh->fib6_info, info, extack);
4457 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08004458
David Ahernf7225172018-06-04 13:41:42 -07004459 if (!err) {
4460 /* save reference to last route successfully inserted */
4461 rt_last = nh->fib6_info;
4462
4463 /* save reference to first route for notification */
4464 if (!rt_notif)
4465 rt_notif = nh->fib6_info;
4466 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004467
David Ahern8d1c8022018-04-17 17:33:26 -07004468 /* nh->fib6_info is used or freed at this point, reset to NULL*/
4469 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004470 if (err) {
4471 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08004472 NL_SET_ERR_MSG_MOD(extack,
4473 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004474 err_nh = nh;
4475 goto add_errout;
4476 }
4477
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004478 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02004479 * these flags after the first nexthop: if there is a collision,
4480 * we have already failed to add the first nexthop:
4481 * fib6_add_rt2node() has rejected it; when replacing, old
4482 * nexthops have been replaced by first new, the rest should
4483 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004484 */
Michal Kubeček27596472015-05-18 20:54:00 +02004485 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
4486 NLM_F_REPLACE);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004487 nhn++;
4488 }
4489
David Ahern3b1137f2017-02-02 12:37:10 -08004490 /* success ... tell user about new route */
4491 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004492 goto cleanup;
4493
4494add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08004495 /* send notification for routes that were added so that
4496 * the delete notifications sent by ip6_route_del are
4497 * coherent
4498 */
4499 if (rt_notif)
4500 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
4501
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004502 /* Delete routes that were already added */
4503 list_for_each_entry(nh, &rt6_nh_list, next) {
4504 if (err_nh == nh)
4505 break;
David Ahern333c4302017-05-21 10:12:04 -06004506 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004507 }
4508
4509cleanup:
4510 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004511 if (nh->fib6_info)
4512 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004513 list_del(&nh->next);
4514 kfree(nh);
4515 }
4516
4517 return err;
4518}
4519
David Ahern333c4302017-05-21 10:12:04 -06004520static int ip6_route_multipath_del(struct fib6_config *cfg,
4521 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004522{
4523 struct fib6_config r_cfg;
4524 struct rtnexthop *rtnh;
4525 int remaining;
4526 int attrlen;
4527 int err = 1, last_err = 0;
4528
4529 remaining = cfg->fc_mp_len;
4530 rtnh = (struct rtnexthop *)cfg->fc_mp;
4531
4532 /* Parse a Multipath Entry */
4533 while (rtnh_ok(rtnh, remaining)) {
4534 memcpy(&r_cfg, cfg, sizeof(*cfg));
4535 if (rtnh->rtnh_ifindex)
4536 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4537
4538 attrlen = rtnh_attrlen(rtnh);
4539 if (attrlen > 0) {
4540 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4541
4542 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4543 if (nla) {
4544 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
4545 r_cfg.fc_flags |= RTF_GATEWAY;
4546 }
4547 }
David Ahern333c4302017-05-21 10:12:04 -06004548 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004549 if (err)
4550 last_err = err;
4551
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004552 rtnh = rtnh_next(rtnh, &remaining);
4553 }
4554
4555 return last_err;
4556}
4557
David Ahernc21ef3e2017-04-16 09:48:24 -07004558static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4559 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004560{
Thomas Graf86872cb2006-08-22 00:01:08 -07004561 struct fib6_config cfg;
4562 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004563
David Ahern333c4302017-05-21 10:12:04 -06004564 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004565 if (err < 0)
4566 return err;
4567
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004568 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004569 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004570 else {
4571 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06004572 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004573 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004574}
4575
David Ahernc21ef3e2017-04-16 09:48:24 -07004576static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4577 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004578{
Thomas Graf86872cb2006-08-22 00:01:08 -07004579 struct fib6_config cfg;
4580 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004581
David Ahern333c4302017-05-21 10:12:04 -06004582 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004583 if (err < 0)
4584 return err;
4585
David Ahern67f69512019-03-21 05:21:34 -07004586 if (cfg.fc_metric == 0)
4587 cfg.fc_metric = IP6_RT_PRIO_USER;
4588
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004589 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004590 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004591 else
David Ahernacb54e32018-04-17 17:33:22 -07004592 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004593}
4594
David Ahern8d1c8022018-04-17 17:33:26 -07004595static size_t rt6_nlmsg_size(struct fib6_info *rt)
Thomas Graf339bf982006-11-10 14:10:15 -08004596{
David Ahernbeb1afac52017-02-02 12:37:09 -08004597 int nexthop_len = 0;
4598
David Ahern93c2fb22018-04-18 15:38:59 -07004599 if (rt->fib6_nsiblings) {
David Ahernbeb1afac52017-02-02 12:37:09 -08004600 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
4601 + NLA_ALIGN(sizeof(struct rtnexthop))
4602 + nla_total_size(16) /* RTA_GATEWAY */
David Ahernad1601a2019-03-27 20:53:56 -07004603 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08004604
David Ahern93c2fb22018-04-18 15:38:59 -07004605 nexthop_len *= rt->fib6_nsiblings;
David Ahernbeb1afac52017-02-02 12:37:09 -08004606 }
4607
Thomas Graf339bf982006-11-10 14:10:15 -08004608 return NLMSG_ALIGN(sizeof(struct rtmsg))
4609 + nla_total_size(16) /* RTA_SRC */
4610 + nla_total_size(16) /* RTA_DST */
4611 + nla_total_size(16) /* RTA_GATEWAY */
4612 + nla_total_size(16) /* RTA_PREFSRC */
4613 + nla_total_size(4) /* RTA_TABLE */
4614 + nla_total_size(4) /* RTA_IIF */
4615 + nla_total_size(4) /* RTA_OIF */
4616 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08004617 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01004618 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004619 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004620 + nla_total_size(1) /* RTA_PREF */
David Ahernad1601a2019-03-27 20:53:56 -07004621 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws)
David Ahernbeb1afac52017-02-02 12:37:09 -08004622 + nexthop_len;
4623}
4624
David Ahernd4ead6b2018-04-17 17:33:16 -07004625static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07004626 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07004627 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004628 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08004629 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004630{
Xin Long22d0bd82018-09-11 14:33:58 +08004631 struct rt6_info *rt6 = (struct rt6_info *)dst;
4632 struct rt6key *rt6_dst, *rt6_src;
4633 u32 *pmetrics, table, rt6_flags;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004634 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08004635 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07004636 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004637
Eric W. Biederman15e47302012-09-07 20:12:54 +00004638 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05004639 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08004640 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004641
Xin Long22d0bd82018-09-11 14:33:58 +08004642 if (rt6) {
4643 rt6_dst = &rt6->rt6i_dst;
4644 rt6_src = &rt6->rt6i_src;
4645 rt6_flags = rt6->rt6i_flags;
4646 } else {
4647 rt6_dst = &rt->fib6_dst;
4648 rt6_src = &rt->fib6_src;
4649 rt6_flags = rt->fib6_flags;
4650 }
4651
Thomas Graf2d7202b2006-08-22 00:01:27 -07004652 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004653 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08004654 rtm->rtm_dst_len = rt6_dst->plen;
4655 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004656 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07004657 if (rt->fib6_table)
4658 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07004659 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07004660 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08004661 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04004662 if (nla_put_u32(skb, RTA_TABLE, table))
4663 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07004664
4665 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004666 rtm->rtm_flags = 0;
4667 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07004668 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004669
Xin Long22d0bd82018-09-11 14:33:58 +08004670 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004671 rtm->rtm_flags |= RTM_F_CLONED;
4672
David Ahernd4ead6b2018-04-17 17:33:16 -07004673 if (dest) {
4674 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04004675 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004676 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004677 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08004678 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004679 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680#ifdef CONFIG_IPV6_SUBTREES
4681 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02004682 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04004683 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004684 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04004685 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08004686 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004687 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004689 if (iif) {
4690#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08004691 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08004692 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02004693
David Ahernfd61c6b2017-01-17 15:51:07 -08004694 if (err == 0)
4695 return 0;
4696 if (err < 0)
4697 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004698 } else
4699#endif
David S. Millerc78679e2012-04-01 20:27:33 -04004700 if (nla_put_u32(skb, RTA_IIF, iif))
4701 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07004702 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004703 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07004704 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02004705 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004706 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004707 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07004708
David Ahern93c2fb22018-04-18 15:38:59 -07004709 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00004710 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07004711 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02004712 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004713 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00004714 }
4715
David Ahernd4ead6b2018-04-17 17:33:16 -07004716 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
4717 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07004718 goto nla_put_failure;
4719
David Ahern93c2fb22018-04-18 15:38:59 -07004720 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04004721 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00004722
David Ahernbeb1afac52017-02-02 12:37:09 -08004723 /* For multipath routes, walk the siblings list and add
4724 * each as a nexthop within RTA_MULTIPATH.
4725 */
Xin Long22d0bd82018-09-11 14:33:58 +08004726 if (rt6) {
4727 if (rt6_flags & RTF_GATEWAY &&
4728 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
4729 goto nla_put_failure;
4730
4731 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
4732 goto nla_put_failure;
4733 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07004734 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08004735 struct nlattr *mp;
4736
4737 mp = nla_nest_start(skb, RTA_MULTIPATH);
4738 if (!mp)
4739 goto nla_put_failure;
4740
David Ahernc0a72072019-04-02 14:11:58 -07004741 if (fib_add_nexthop(skb, &rt->fib6_nh.nh_common,
4742 rt->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004743 goto nla_put_failure;
4744
4745 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07004746 &rt->fib6_siblings, fib6_siblings) {
David Ahernc0a72072019-04-02 14:11:58 -07004747 if (fib_add_nexthop(skb, &sibling->fib6_nh.nh_common,
4748 sibling->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004749 goto nla_put_failure;
4750 }
4751
4752 nla_nest_end(skb, mp);
4753 } else {
David Ahernc0a72072019-04-02 14:11:58 -07004754 if (fib_nexthop_info(skb, &rt->fib6_nh.nh_common,
4755 &rtm->rtm_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004756 goto nla_put_failure;
4757 }
4758
Xin Long22d0bd82018-09-11 14:33:58 +08004759 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07004760 expires = dst ? dst->expires : rt->expires;
4761 expires -= jiffies;
4762 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07004763
David Ahernd4ead6b2018-04-17 17:33:16 -07004764 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08004765 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004766
Xin Long22d0bd82018-09-11 14:33:58 +08004767 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004768 goto nla_put_failure;
4769
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004770
Johannes Berg053c0952015-01-16 22:09:00 +01004771 nlmsg_end(skb, nlh);
4772 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004773
4774nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08004775 nlmsg_cancel(skb, nlh);
4776 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004777}
4778
David Ahern13e38902018-10-15 18:56:44 -07004779static bool fib6_info_uses_dev(const struct fib6_info *f6i,
4780 const struct net_device *dev)
4781{
David Ahernad1601a2019-03-27 20:53:56 -07004782 if (f6i->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004783 return true;
4784
4785 if (f6i->fib6_nsiblings) {
4786 struct fib6_info *sibling, *next_sibling;
4787
4788 list_for_each_entry_safe(sibling, next_sibling,
4789 &f6i->fib6_siblings, fib6_siblings) {
David Ahernad1601a2019-03-27 20:53:56 -07004790 if (sibling->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004791 return true;
4792 }
4793 }
4794
4795 return false;
4796}
4797
David Ahern8d1c8022018-04-17 17:33:26 -07004798int rt6_dump_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004799{
4800 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07004801 struct fib_dump_filter *filter = &arg->filter;
4802 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08004803 struct net *net = arg->net;
4804
David Ahern421842e2018-04-17 17:33:18 -07004805 if (rt == net->ipv6.fib6_null_entry)
David Ahern1f17e2f2017-01-26 13:54:08 -08004806 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004807
David Ahern13e38902018-10-15 18:56:44 -07004808 if ((filter->flags & RTM_F_PREFIX) &&
4809 !(rt->fib6_flags & RTF_PREFIX_RT)) {
4810 /* success since this is not a prefix route */
4811 return 1;
4812 }
4813 if (filter->filter_set) {
4814 if ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
4815 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
4816 (filter->protocol && rt->fib6_protocol != filter->protocol)) {
David Ahernf8cfe2c2017-01-17 15:51:08 -08004817 return 1;
4818 }
David Ahern13e38902018-10-15 18:56:44 -07004819 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08004820 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004821
David Ahernd4ead6b2018-04-17 17:33:16 -07004822 return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
4823 RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
David Ahern13e38902018-10-15 18:56:44 -07004824 arg->cb->nlh->nlmsg_seq, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004825}
4826
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004827static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
4828 const struct nlmsghdr *nlh,
4829 struct nlattr **tb,
4830 struct netlink_ext_ack *extack)
4831{
4832 struct rtmsg *rtm;
4833 int i, err;
4834
4835 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
4836 NL_SET_ERR_MSG_MOD(extack,
4837 "Invalid header for get route request");
4838 return -EINVAL;
4839 }
4840
4841 if (!netlink_strict_get_check(skb))
4842 return nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
4843 rtm_ipv6_policy, extack);
4844
4845 rtm = nlmsg_data(nlh);
4846 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
4847 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
4848 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
4849 rtm->rtm_type) {
4850 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
4851 return -EINVAL;
4852 }
4853 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
4854 NL_SET_ERR_MSG_MOD(extack,
4855 "Invalid flags for get route request");
4856 return -EINVAL;
4857 }
4858
4859 err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
4860 rtm_ipv6_policy, extack);
4861 if (err)
4862 return err;
4863
4864 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
4865 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
4866 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
4867 return -EINVAL;
4868 }
4869
4870 for (i = 0; i <= RTA_MAX; i++) {
4871 if (!tb[i])
4872 continue;
4873
4874 switch (i) {
4875 case RTA_SRC:
4876 case RTA_DST:
4877 case RTA_IIF:
4878 case RTA_OIF:
4879 case RTA_MARK:
4880 case RTA_UID:
4881 case RTA_SPORT:
4882 case RTA_DPORT:
4883 case RTA_IP_PROTO:
4884 break;
4885 default:
4886 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
4887 return -EINVAL;
4888 }
4889 }
4890
4891 return 0;
4892}
4893
David Ahernc21ef3e2017-04-16 09:48:24 -07004894static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
4895 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004896{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09004897 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07004898 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004899 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07004900 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004901 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004902 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07004903 struct sk_buff *skb;
4904 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07004905 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004906 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07004907
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004908 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07004909 if (err < 0)
4910 goto errout;
4911
4912 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02004913 rtm = nlmsg_data(nlh);
4914 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004915 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07004916
4917 if (tb[RTA_SRC]) {
4918 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
4919 goto errout;
4920
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004921 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07004922 }
4923
4924 if (tb[RTA_DST]) {
4925 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
4926 goto errout;
4927
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004928 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07004929 }
4930
4931 if (tb[RTA_IIF])
4932 iif = nla_get_u32(tb[RTA_IIF]);
4933
4934 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004935 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07004936
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07004937 if (tb[RTA_MARK])
4938 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
4939
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004940 if (tb[RTA_UID])
4941 fl6.flowi6_uid = make_kuid(current_user_ns(),
4942 nla_get_u32(tb[RTA_UID]));
4943 else
4944 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
4945
Roopa Prabhueacb9382018-05-22 14:03:28 -07004946 if (tb[RTA_SPORT])
4947 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
4948
4949 if (tb[RTA_DPORT])
4950 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
4951
4952 if (tb[RTA_IP_PROTO]) {
4953 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08004954 &fl6.flowi6_proto, AF_INET6,
4955 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07004956 if (err)
4957 goto errout;
4958 }
4959
Thomas Grafab364a62006-08-22 00:01:47 -07004960 if (iif) {
4961 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004962 int flags = 0;
4963
Florian Westphal121622d2017-08-15 16:34:42 +02004964 rcu_read_lock();
4965
4966 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07004967 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02004968 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07004969 err = -ENODEV;
4970 goto errout;
4971 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004972
4973 fl6.flowi6_iif = iif;
4974
4975 if (!ipv6_addr_any(&fl6.saddr))
4976 flags |= RT6_LOOKUP_F_HAS_SADDR;
4977
David Ahernb75cc8f2018-03-02 08:32:17 -08004978 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02004979
4980 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004981 } else {
4982 fl6.flowi6_oif = oif;
4983
Ido Schimmel58acfd72017-12-20 12:28:25 +02004984 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004985 }
4986
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004987
4988 rt = container_of(dst, struct rt6_info, dst);
4989 if (rt->dst.error) {
4990 err = rt->dst.error;
4991 ip6_rt_put(rt);
4992 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07004993 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004994
WANG Cong9d6acb32017-03-01 20:48:39 -08004995 if (rt == net->ipv6.ip6_null_entry) {
4996 err = rt->dst.error;
4997 ip6_rt_put(rt);
4998 goto errout;
4999 }
5000
Linus Torvalds1da177e2005-04-16 15:20:36 -07005001 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05005002 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00005003 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07005004 err = -ENOBUFS;
5005 goto errout;
5006 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005007
Changli Gaod8d1f302010-06-10 23:31:35 -07005008 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07005009
5010 rcu_read_lock();
5011 from = rcu_dereference(rt->from);
5012
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005013 if (fibmatch)
David Aherna68886a2018-04-20 15:38:02 -07005014 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005015 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
5016 nlh->nlmsg_seq, 0);
5017 else
David Aherna68886a2018-04-20 15:38:02 -07005018 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5019 &fl6.saddr, iif, RTM_NEWROUTE,
David Ahernd4ead6b2018-04-17 17:33:16 -07005020 NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
5021 0);
David Aherna68886a2018-04-20 15:38:02 -07005022 rcu_read_unlock();
5023
Linus Torvalds1da177e2005-04-16 15:20:36 -07005024 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005025 kfree_skb(skb);
5026 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005027 }
5028
Eric W. Biederman15e47302012-09-07 20:12:54 +00005029 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005030errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005031 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005032}
5033
David Ahern8d1c8022018-04-17 17:33:26 -07005034void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07005035 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005036{
5037 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08005038 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005039 u32 seq;
5040 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005041
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005042 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05005043 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07005044
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005045 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05005046 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07005047 goto errout;
5048
David Ahernd4ead6b2018-04-17 17:33:16 -07005049 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
5050 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08005051 if (err < 0) {
5052 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
5053 WARN_ON(err == -EMSGSIZE);
5054 kfree_skb(skb);
5055 goto errout;
5056 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00005057 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08005058 info->nlh, gfp_any());
5059 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07005060errout:
5061 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08005062 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005063}
5064
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005065static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00005066 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005067{
Jiri Pirko351638e2013-05-28 01:30:21 +00005068 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09005069 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005070
WANG Cong242d3a42017-05-08 10:12:13 -07005071 if (!(dev->flags & IFF_LOOPBACK))
5072 return NOTIFY_OK;
5073
5074 if (event == NETDEV_REGISTER) {
David Ahernad1601a2019-03-27 20:53:56 -07005075 net->ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07005076 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005077 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
5078#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07005079 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005080 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07005081 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005082 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
5083#endif
WANG Cong76da0702017-06-20 11:42:27 -07005084 } else if (event == NETDEV_UNREGISTER &&
5085 dev->reg_state != NETREG_UNREGISTERED) {
5086 /* NETDEV_UNREGISTER could be fired for multiple times by
5087 * netdev_wait_allrefs(). Make sure we only call this once.
5088 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07005089 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005090#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07005091 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
5092 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005093#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005094 }
5095
5096 return NOTIFY_OK;
5097}
5098
Linus Torvalds1da177e2005-04-16 15:20:36 -07005099/*
5100 * /proc
5101 */
5102
5103#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07005104static int rt6_stats_seq_show(struct seq_file *seq, void *v)
5105{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005106 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005107 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005108 net->ipv6.rt6_stats->fib_nodes,
5109 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07005110 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005111 net->ipv6.rt6_stats->fib_rt_entries,
5112 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00005113 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005114 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005115
5116 return 0;
5117}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005118#endif /* CONFIG_PROC_FS */
5119
5120#ifdef CONFIG_SYSCTL
5121
Linus Torvalds1da177e2005-04-16 15:20:36 -07005122static
Joe Perchesfe2c6332013-06-11 23:04:25 -07005123int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005124 void __user *buffer, size_t *lenp, loff_t *ppos)
5125{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005126 struct net *net;
5127 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005128 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005129 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005130 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005131
5132 net = (struct net *)ctl->extra1;
5133 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005134 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5135 if (ret)
5136 return ret;
5137
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02005138 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005139 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005140}
5141
David Ahern7c6bb7d2018-10-11 20:17:21 -07005142static int zero;
5143static int one = 1;
5144
David Aherned792e22018-10-08 14:06:34 -07005145static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005146 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005147 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08005148 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005149 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07005150 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005151 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07005152 },
5153 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005154 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005155 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005156 .maxlen = sizeof(int),
5157 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005158 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005159 },
5160 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005161 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08005162 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005163 .maxlen = sizeof(int),
5164 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005165 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005166 },
5167 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005168 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005169 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170 .maxlen = sizeof(int),
5171 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005172 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005173 },
5174 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005175 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08005176 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005177 .maxlen = sizeof(int),
5178 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005179 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 },
5181 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005183 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005184 .maxlen = sizeof(int),
5185 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005186 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005187 },
5188 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005189 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08005190 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005191 .maxlen = sizeof(int),
5192 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005193 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005194 },
5195 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005196 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08005197 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005198 .maxlen = sizeof(int),
5199 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005200 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005201 },
5202 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005203 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08005204 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005205 .maxlen = sizeof(int),
5206 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005207 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005208 },
5209 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005210 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08005211 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005212 .maxlen = sizeof(int),
5213 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005214 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005215 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07005216 {
5217 .procname = "skip_notify_on_dev_down",
5218 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
5219 .maxlen = sizeof(int),
5220 .mode = 0644,
5221 .proc_handler = proc_dointvec,
5222 .extra1 = &zero,
5223 .extra2 = &one,
5224 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08005225 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226};
5227
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005228struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005229{
5230 struct ctl_table *table;
5231
5232 table = kmemdup(ipv6_route_table_template,
5233 sizeof(ipv6_route_table_template),
5234 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005235
5236 if (table) {
5237 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005238 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005239 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005240 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
5241 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
5242 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
5243 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
5244 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
5245 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
5246 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08005247 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005248 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00005249
5250 /* Don't export sysctls to unprivileged users */
5251 if (net->user_ns != &init_user_ns)
5252 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005253 }
5254
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005255 return table;
5256}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005257#endif
5258
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005259static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005260{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07005261 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005262
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005263 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
5264 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005265
Eric Dumazetfc66f952010-10-08 06:37:34 +00005266 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
5267 goto out_ip6_dst_ops;
5268
David Ahern421842e2018-04-17 17:33:18 -07005269 net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
5270 sizeof(*net->ipv6.fib6_null_entry),
5271 GFP_KERNEL);
5272 if (!net->ipv6.fib6_null_entry)
5273 goto out_ip6_dst_entries;
5274
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005275 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
5276 sizeof(*net->ipv6.ip6_null_entry),
5277 GFP_KERNEL);
5278 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07005279 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005280 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005281 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
5282 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005283
5284#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02005285 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005286 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
5287 sizeof(*net->ipv6.ip6_prohibit_entry),
5288 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005289 if (!net->ipv6.ip6_prohibit_entry)
5290 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005291 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005292 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
5293 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005294
5295 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
5296 sizeof(*net->ipv6.ip6_blk_hole_entry),
5297 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005298 if (!net->ipv6.ip6_blk_hole_entry)
5299 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005300 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005301 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
5302 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005303#endif
5304
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005305 net->ipv6.sysctl.flush_delay = 0;
5306 net->ipv6.sysctl.ip6_rt_max_size = 4096;
5307 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
5308 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
5309 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
5310 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
5311 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
5312 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005313 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005314
Benjamin Thery6891a342008-03-04 13:49:47 -08005315 net->ipv6.ip6_rt_gc_expire = 30*HZ;
5316
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005317 ret = 0;
5318out:
5319 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005320
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005321#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5322out_ip6_prohibit_entry:
5323 kfree(net->ipv6.ip6_prohibit_entry);
5324out_ip6_null_entry:
5325 kfree(net->ipv6.ip6_null_entry);
5326#endif
David Ahern421842e2018-04-17 17:33:18 -07005327out_fib6_null_entry:
5328 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005329out_ip6_dst_entries:
5330 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005331out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005332 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005333}
5334
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005335static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005336{
David Ahern421842e2018-04-17 17:33:18 -07005337 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005338 kfree(net->ipv6.ip6_null_entry);
5339#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5340 kfree(net->ipv6.ip6_prohibit_entry);
5341 kfree(net->ipv6.ip6_blk_hole_entry);
5342#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005343 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005344}
5345
Thomas Grafd1896342012-06-18 12:08:33 +00005346static int __net_init ip6_route_net_init_late(struct net *net)
5347{
5348#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02005349 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
5350 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02005351 proc_create_net_single("rt6_stats", 0444, net->proc_net,
5352 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00005353#endif
5354 return 0;
5355}
5356
5357static void __net_exit ip6_route_net_exit_late(struct net *net)
5358{
5359#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00005360 remove_proc_entry("ipv6_route", net->proc_net);
5361 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00005362#endif
5363}
5364
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005365static struct pernet_operations ip6_route_net_ops = {
5366 .init = ip6_route_net_init,
5367 .exit = ip6_route_net_exit,
5368};
5369
David S. Millerc3426b42012-06-09 16:27:05 -07005370static int __net_init ipv6_inetpeer_init(struct net *net)
5371{
5372 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
5373
5374 if (!bp)
5375 return -ENOMEM;
5376 inet_peer_base_init(bp);
5377 net->ipv6.peers = bp;
5378 return 0;
5379}
5380
5381static void __net_exit ipv6_inetpeer_exit(struct net *net)
5382{
5383 struct inet_peer_base *bp = net->ipv6.peers;
5384
5385 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07005386 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07005387 kfree(bp);
5388}
5389
David S. Miller2b823f72012-06-09 19:00:16 -07005390static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07005391 .init = ipv6_inetpeer_init,
5392 .exit = ipv6_inetpeer_exit,
5393};
5394
Thomas Grafd1896342012-06-18 12:08:33 +00005395static struct pernet_operations ip6_route_net_late_ops = {
5396 .init = ip6_route_net_init_late,
5397 .exit = ip6_route_net_exit_late,
5398};
5399
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005400static struct notifier_block ip6_route_dev_notifier = {
5401 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07005402 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005403};
5404
WANG Cong2f460932017-05-03 22:07:31 -07005405void __init ip6_route_init_special_entries(void)
5406{
5407 /* Registering of the loopback is done before this portion of code,
5408 * the loopback reference in rt6_info will not be taken, do it
5409 * manually for init_net */
David Ahernad1601a2019-03-27 20:53:56 -07005410 init_net.ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07005411 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
5412 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5413 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
5414 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
5415 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5416 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
5417 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5418 #endif
5419}
5420
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005421int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005422{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005423 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005424 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005425
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005426 ret = -ENOMEM;
5427 ip6_dst_ops_template.kmem_cachep =
5428 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
5429 SLAB_HWCACHE_ALIGN, NULL);
5430 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08005431 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07005432
Eric Dumazetfc66f952010-10-08 06:37:34 +00005433 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005434 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005435 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005436
David S. Millerc3426b42012-06-09 16:27:05 -07005437 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
5438 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005439 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00005440
David S. Miller7e52b332012-06-15 15:51:55 -07005441 ret = register_pernet_subsys(&ip6_route_net_ops);
5442 if (ret)
5443 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07005444
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07005445 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
5446
David S. Millere8803b62012-06-16 01:12:19 -07005447 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005448 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005449 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005450
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005451 ret = xfrm6_init();
5452 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005453 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08005454
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005455 ret = fib6_rules_init();
5456 if (ret)
5457 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08005458
Thomas Grafd1896342012-06-18 12:08:33 +00005459 ret = register_pernet_subsys(&ip6_route_net_late_ops);
5460 if (ret)
5461 goto fib6_rules_init;
5462
Florian Westphal16feebc2017-12-02 21:44:08 +01005463 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
5464 inet6_rtm_newroute, NULL, 0);
5465 if (ret < 0)
5466 goto out_register_late_subsys;
5467
5468 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
5469 inet6_rtm_delroute, NULL, 0);
5470 if (ret < 0)
5471 goto out_register_late_subsys;
5472
5473 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
5474 inet6_rtm_getroute, NULL,
5475 RTNL_FLAG_DOIT_UNLOCKED);
5476 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00005477 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005478
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005479 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005480 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00005481 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005482
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005483 for_each_possible_cpu(cpu) {
5484 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
5485
5486 INIT_LIST_HEAD(&ul->head);
5487 spin_lock_init(&ul->lock);
5488 }
5489
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005490out:
5491 return ret;
5492
Thomas Grafd1896342012-06-18 12:08:33 +00005493out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01005494 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00005495 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005496fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005497 fib6_rules_cleanup();
5498xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005499 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00005500out_fib6_init:
5501 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005502out_register_subsys:
5503 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07005504out_register_inetpeer:
5505 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005506out_dst_entries:
5507 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005508out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005509 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005510 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005511}
5512
5513void ip6_route_cleanup(void)
5514{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005515 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00005516 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07005517 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005518 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005519 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07005520 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005521 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005522 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005523 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005524}