blob: 9935e18146e5aea45267262a0f375e55d10310b1 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Linux INET6 implementation
4 * FIB front-end.
5 *
6 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09007 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 */
9
10/* Changes:
11 *
12 * YOSHIFUJI Hideaki @USAGI
13 * reworked default router selection.
14 * - respect outgoing interface
15 * - select from (probably) reachable routers (i.e.
16 * routers in REACHABLE, STALE, DELAY or PROBE states).
17 * - always select the same router if it is (probably)
18 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070019 * Ville Nuorvala
20 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 */
22
Joe Perchesf3213832012-05-15 14:11:53 +000023#define pr_fmt(fmt) "IPv6: " fmt
24
Randy Dunlap4fc268d2006-01-11 12:17:47 -080025#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040027#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/types.h>
29#include <linux/times.h>
30#include <linux/socket.h>
31#include <linux/sockios.h>
32#include <linux/net.h>
33#include <linux/route.h>
34#include <linux/netdevice.h>
35#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090036#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/proc_fs.h>
40#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080041#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090042#include <linux/slab.h>
Wei Wang35732d02017-10-06 12:05:57 -070043#include <linux/jhash.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020044#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#include <net/snmp.h>
46#include <net/ipv6.h>
47#include <net/ip6_fib.h>
48#include <net/ip6_route.h>
49#include <net/ndisc.h>
50#include <net/addrconf.h>
51#include <net/tcp.h>
52#include <linux/rtnetlink.h>
53#include <net/dst.h>
Jiri Benc904af042015-08-20 13:56:31 +020054#include <net/dst_metadata.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070056#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070057#include <net/netlink.h>
David Ahern3c618c12019-04-20 09:28:20 -070058#include <net/rtnh.h>
Roopa Prabhu19e42e42015-07-21 10:43:48 +020059#include <net/lwtunnel.h>
Jiri Benc904af042015-08-20 13:56:31 +020060#include <net/ip_tunnels.h>
David Ahernca254492015-10-12 11:47:10 -070061#include <net/l3mdev.h>
Roopa Prabhueacb9382018-05-22 14:03:28 -070062#include <net/ip.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080063#include <linux/uaccess.h>
Yonghong Song951cf362020-07-20 09:34:03 -070064#include <linux/btf_ids.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66#ifdef CONFIG_SYSCTL
67#include <linux/sysctl.h>
68#endif
69
David Ahern30d444d2018-05-23 17:08:48 -070070static int ip6_rt_type_to_error(u8 fib6_type);
71
72#define CREATE_TRACE_POINTS
73#include <trace/events/fib6.h>
74EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
75#undef CREATE_TRACE_POINTS
76
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020077enum rt6_nud_state {
Jiri Benc7e980562013-12-11 13:48:20 +010078 RT6_NUD_FAIL_HARD = -3,
79 RT6_NUD_FAIL_PROBE = -2,
80 RT6_NUD_FAIL_DO_RR = -1,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020081 RT6_NUD_SUCCEED = 1
82};
83
Brian Vazquezbbd807d2021-02-01 17:41:32 +000084INDIRECT_CALLABLE_SCOPE
85struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080086static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Brian Vazquezf67fbea2021-02-01 17:41:31 +000087INDIRECT_CALLABLE_SCOPE
88unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089static struct dst_entry *ip6_negative_advice(struct dst_entry *);
90static void ip6_dst_destroy(struct dst_entry *);
91static void ip6_dst_ifdown(struct dst_entry *,
92 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080093static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
95static int ip6_pkt_discard(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050096static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Kamala R7150aed2013-12-02 19:55:21 +053097static int ip6_pkt_prohibit(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050098static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099static void ip6_link_failure(struct sk_buff *skb);
David S. Miller6700c272012-07-17 03:29:28 -0700100static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
Hangbin Liubd085ef2019-12-22 10:51:09 +0800101 struct sk_buff *skb, u32 mtu,
102 bool confirm_neigh);
David S. Miller6700c272012-07-17 03:29:28 -0700103static 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 Aherna1b7a1f2019-06-08 14:53:26 -0700107static size_t rt6_nlmsg_size(struct fib6_info *f6i);
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 Wang510e2ce2019-05-16 13:30:54 -0700114 const struct in6_addr *daddr,
115 const 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) {
Mahesh Bandewar8d7017f2019-07-01 14:38:57 -0700183 rt->dst.dev = blackhole_netdev;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700184 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
Nicolas Dichtel2c6b55f2019-06-24 16:01:09 +0200225 return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any),
226 dst->dev, skb, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500227}
228
Julian Anastasov63fca652017-02-06 23:14:15 +0200229static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
230{
231 struct net_device *dev = dst->dev;
232 struct rt6_info *rt = (struct rt6_info *)dst;
233
Stefano Briviocbfd6892019-09-09 22:44:06 +0200234 daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr);
Julian Anastasov63fca652017-02-06 23:14:15 +0200235 if (!daddr)
236 return;
237 if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
238 return;
239 if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
240 return;
241 __ipv6_confirm_neigh(dev, daddr);
242}
243
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800244static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 .gc = ip6_dst_gc,
247 .gc_thresh = 1024,
248 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800249 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000250 .mtu = ip6_mtu,
David Ahernd4ead6b2018-04-17 17:33:16 -0700251 .cow_metrics = dst_cow_metrics_generic,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 .destroy = ip6_dst_destroy,
253 .ifdown = ip6_dst_ifdown,
254 .negative_advice = ip6_negative_advice,
255 .link_failure = ip6_link_failure,
256 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700257 .redirect = rt6_do_redirect,
Eric W. Biederman9f8955c2015-10-07 16:48:39 -0500258 .local_out = __ip6_local_out,
David Ahernf8a1b432018-04-17 17:33:21 -0700259 .neigh_lookup = ip6_dst_neigh_lookup,
Julian Anastasov63fca652017-02-06 23:14:15 +0200260 .confirm_neigh = ip6_confirm_neigh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261};
262
David S. Miller14e50e52007-05-24 18:17:54 -0700263static struct dst_ops ip6_dst_blackhole_ops = {
Daniel Borkmannc4c877b2021-03-10 01:38:09 +0100264 .family = AF_INET6,
265 .default_advmss = ip6_default_advmss,
266 .neigh_lookup = ip6_dst_neigh_lookup,
267 .check = ip6_dst_check,
268 .destroy = ip6_dst_destroy,
269 .cow_metrics = dst_cow_metrics_generic,
270 .update_pmtu = dst_blackhole_update_pmtu,
271 .redirect = dst_blackhole_redirect,
272 .mtu = dst_blackhole_mtu,
David S. Miller14e50e52007-05-24 18:17:54 -0700273};
274
David S. Miller62fa8a82011-01-26 20:51:05 -0800275static const u32 ip6_template_metrics[RTAX_MAX] = {
Li RongQing14edd872012-10-24 14:01:18 +0800276 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800277};
278
David Ahern8d1c8022018-04-17 17:33:26 -0700279static const struct fib6_info fib6_null_entry_template = {
David Ahern93c2fb22018-04-18 15:38:59 -0700280 .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP),
281 .fib6_protocol = RTPROT_KERNEL,
282 .fib6_metric = ~(u32)0,
Eric Dumazetf05713e2019-04-22 18:35:03 -0700283 .fib6_ref = REFCOUNT_INIT(1),
David Ahern421842e2018-04-17 17:33:18 -0700284 .fib6_type = RTN_UNREACHABLE,
285 .fib6_metrics = (struct dst_metrics *)&dst_default_metrics,
286};
287
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000288static const struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700289 .dst = {
290 .__refcnt = ATOMIC_INIT(1),
291 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000292 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700293 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700294 .input = ip6_pkt_discard,
295 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 },
297 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298};
299
Thomas Graf101367c2006-08-04 03:39:02 -0700300#ifdef CONFIG_IPV6_MULTIPLE_TABLES
301
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000302static const struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700303 .dst = {
304 .__refcnt = ATOMIC_INIT(1),
305 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000306 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700307 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700308 .input = ip6_pkt_prohibit,
309 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700310 },
311 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700312};
313
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000314static const struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700315 .dst = {
316 .__refcnt = ATOMIC_INIT(1),
317 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000318 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700319 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700320 .input = dst_discard,
Eric W. Biedermanede20592015-10-07 16:48:47 -0500321 .output = dst_discard_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700322 },
323 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700324};
325
326#endif
327
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700328static void rt6_info_init(struct rt6_info *rt)
329{
330 struct dst_entry *dst = &rt->dst;
331
332 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700333 INIT_LIST_HEAD(&rt->rt6i_uncached);
334}
335
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336/* allocate dst with ip6_dst_ops */
David Ahern93531c62018-04-17 17:33:25 -0700337struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
338 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
David S. Miller97bab732012-06-09 22:36:36 -0700340 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
Wei Wangb2a9c0e2017-06-17 10:42:41 -0700341 1, DST_OBSOLETE_FORCE_CHK, flags);
David S. Millercf911662011-04-28 14:31:47 -0700342
Wei Wang81eb8442017-10-06 12:06:11 -0700343 if (rt) {
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700344 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -0700345 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
346 }
Steffen Klassert81048912012-07-05 23:37:09 +0000347
David S. Millercf911662011-04-28 14:31:47 -0700348 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349}
David Ahern9ab179d2016-04-07 11:10:06 -0700350EXPORT_SYMBOL(ip6_dst_alloc);
Martin KaFai Laud52d3992015-05-22 20:56:06 -0700351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352static void ip6_dst_destroy(struct dst_entry *dst)
353{
354 struct rt6_info *rt = (struct rt6_info *)dst;
David Aherna68886a2018-04-20 15:38:02 -0700355 struct fib6_info *from;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700356 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
David Ahern1620a332018-10-04 20:07:54 -0700358 ip_dst_metrics_put(dst);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700359 rt6_uncached_list_del(rt);
360
361 idev = rt->rt6i_idev;
David S. Miller38308472011-12-03 18:02:47 -0500362 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 rt->rt6i_idev = NULL;
364 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900365 }
Gao feng1716a962012-04-06 00:13:10 +0000366
Eric Dumazet0e233872019-04-28 12:22:25 -0700367 from = xchg((__force struct fib6_info **)&rt->from, NULL);
David Ahern93531c62018-04-17 17:33:25 -0700368 fib6_info_release(from);
David S. Millerb3419362010-11-30 12:27:11 -0800369}
370
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
372 int how)
373{
374 struct rt6_info *rt = (struct rt6_info *)dst;
375 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800376 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900377 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Wei Wange5645f52017-08-14 10:44:59 -0700379 if (idev && idev->dev != loopback_dev) {
380 struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev);
381 if (loopback_idev) {
382 rt->rt6i_idev = loopback_idev;
383 in6_dev_put(idev);
David S. Miller97cac082012-07-02 22:43:47 -0700384 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 }
386}
387
Martin KaFai Lau5973fb12015-11-11 11:51:07 -0800388static bool __rt6_check_expired(const struct rt6_info *rt)
389{
390 if (rt->rt6i_flags & RTF_EXPIRES)
391 return time_after(jiffies, rt->dst.expires);
392 else
393 return false;
394}
395
Eric Dumazeta50feda2012-05-18 18:57:34 +0000396static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397{
David Aherna68886a2018-04-20 15:38:02 -0700398 struct fib6_info *from;
399
400 from = rcu_dereference(rt->from);
401
Gao feng1716a962012-04-06 00:13:10 +0000402 if (rt->rt6i_flags & RTF_EXPIRES) {
403 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000404 return true;
David Aherna68886a2018-04-20 15:38:02 -0700405 } else if (from) {
Xin Long1e2ea8a2017-08-26 20:10:10 +0800406 return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
David Aherna68886a2018-04-20 15:38:02 -0700407 fib6_check_expired(from);
Gao feng1716a962012-04-06 00:13:10 +0000408 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000409 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410}
411
David Ahernb1d40992019-04-16 14:35:59 -0700412void fib6_select_path(const struct net *net, struct fib6_result *res,
413 struct flowi6 *fl6, int oif, bool have_oif_match,
414 const struct sk_buff *skb, int strict)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000415{
David Ahern8d1c8022018-04-17 17:33:26 -0700416 struct fib6_info *sibling, *next_sibling;
David Ahernb1d40992019-04-16 14:35:59 -0700417 struct fib6_info *match = res->f6i;
418
David Ahern34fe5a12020-07-06 11:45:07 -0600419 if (!match->nh && (!match->fib6_nsiblings || have_oif_match))
David Ahernb1d40992019-04-16 14:35:59 -0700420 goto out;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000421
David Ahern34fe5a12020-07-06 11:45:07 -0600422 if (match->nh && have_oif_match && res->nh)
423 return;
424
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200425 /* We might have already computed the hash for ICMPv6 errors. In such
426 * case it will always be non-zero. Otherwise now is the time to do it.
427 */
David Ahernf88d8ea2019-06-03 20:19:52 -0700428 if (!fl6->mp_hash &&
429 (!match->nh || nexthop_is_multipath(match->nh)))
David Ahernb4bac172018-03-02 08:32:18 -0800430 fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200431
David Ahernf88d8ea2019-06-03 20:19:52 -0700432 if (unlikely(match->nh)) {
433 nexthop_path_fib6_result(res, fl6->mp_hash);
434 return;
435 }
436
David Ahern1cf844c2019-05-22 20:27:59 -0700437 if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound))
David Ahernb1d40992019-04-16 14:35:59 -0700438 goto out;
Ido Schimmelbbfcd772017-11-21 09:50:12 +0200439
David Ahern93c2fb22018-04-18 15:38:59 -0700440 list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
441 fib6_siblings) {
David Ahern1cf844c2019-05-22 20:27:59 -0700442 const struct fib6_nh *nh = sibling->fib6_nh;
David Ahern5e670d82018-04-17 17:33:14 -0700443 int nh_upper_bound;
444
David Ahern702cea52019-04-09 14:41:13 -0700445 nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
David Ahern5e670d82018-04-17 17:33:14 -0700446 if (fl6->mp_hash > nh_upper_bound)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200447 continue;
David Ahern702cea52019-04-09 14:41:13 -0700448 if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200449 break;
450 match = sibling;
451 break;
452 }
453
David Ahernb1d40992019-04-16 14:35:59 -0700454out:
455 res->f6i = match;
David Ahern1cf844c2019-05-22 20:27:59 -0700456 res->nh = match->fib6_nh;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000457}
458
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459/*
Wei Wang66f5d6c2017-10-06 12:06:10 -0700460 * Route lookup. rcu_read_lock() should be held.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 */
462
David Ahern0c59d002019-04-09 14:41:18 -0700463static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
464 const struct in6_addr *saddr, int oif, int flags)
465{
466 const struct net_device *dev;
467
468 if (nh->fib_nh_flags & RTNH_F_DEAD)
469 return false;
470
471 dev = nh->fib_nh_dev;
472 if (oif) {
473 if (dev->ifindex == oif)
474 return true;
475 } else {
476 if (ipv6_chk_addr(net, saddr, dev,
477 flags & RT6_LOOKUP_F_IFACE))
478 return true;
479 }
480
481 return false;
482}
483
David Ahern962b6802019-06-08 14:53:24 -0700484struct fib6_nh_dm_arg {
485 struct net *net;
486 const struct in6_addr *saddr;
487 int oif;
488 int flags;
489 struct fib6_nh *nh;
490};
491
492static int __rt6_nh_dev_match(struct fib6_nh *nh, void *_arg)
493{
494 struct fib6_nh_dm_arg *arg = _arg;
495
496 arg->nh = nh;
497 return __rt6_device_match(arg->net, nh, arg->saddr, arg->oif,
498 arg->flags);
499}
500
501/* returns fib6_nh from nexthop or NULL */
502static struct fib6_nh *rt6_nh_dev_match(struct net *net, struct nexthop *nh,
503 struct fib6_result *res,
504 const struct in6_addr *saddr,
505 int oif, int flags)
506{
507 struct fib6_nh_dm_arg arg = {
508 .net = net,
509 .saddr = saddr,
510 .oif = oif,
511 .flags = flags,
512 };
513
514 if (nexthop_is_blackhole(nh))
515 return NULL;
516
517 if (nexthop_for_each_fib6_nh(nh, __rt6_nh_dev_match, &arg))
518 return arg.nh;
519
520 return NULL;
521}
522
David Ahern75ef7382019-04-16 14:36:07 -0700523static void rt6_device_match(struct net *net, struct fib6_result *res,
524 const struct in6_addr *saddr, int oif, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
David Ahern75ef7382019-04-16 14:36:07 -0700526 struct fib6_info *f6i = res->f6i;
527 struct fib6_info *spf6i;
528 struct fib6_nh *nh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
David Ahern75ef7382019-04-16 14:36:07 -0700530 if (!oif && ipv6_addr_any(saddr)) {
David Ahernf88d8ea2019-06-03 20:19:52 -0700531 if (unlikely(f6i->nh)) {
532 nh = nexthop_fib6_nh(f6i->nh);
533 if (nexthop_is_blackhole(f6i->nh))
534 goto out_blackhole;
535 } else {
536 nh = f6i->fib6_nh;
537 }
David Ahern7d21fec2019-04-16 14:36:11 -0700538 if (!(nh->fib_nh_flags & RTNH_F_DEAD))
539 goto out;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900540 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
David Ahern75ef7382019-04-16 14:36:07 -0700542 for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
David Ahern962b6802019-06-08 14:53:24 -0700543 bool matched = false;
544
545 if (unlikely(spf6i->nh)) {
546 nh = rt6_nh_dev_match(net, spf6i->nh, res, saddr,
547 oif, flags);
548 if (nh)
549 matched = true;
550 } else {
551 nh = spf6i->fib6_nh;
552 if (__rt6_device_match(net, nh, saddr, oif, flags))
553 matched = true;
554 }
555 if (matched) {
David Ahern75ef7382019-04-16 14:36:07 -0700556 res->f6i = spf6i;
David Ahern7d21fec2019-04-16 14:36:11 -0700557 goto out;
David Ahern75ef7382019-04-16 14:36:07 -0700558 }
559 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
David Ahern75ef7382019-04-16 14:36:07 -0700561 if (oif && flags & RT6_LOOKUP_F_IFACE) {
562 res->f6i = net->ipv6.fib6_null_entry;
David Ahern1cf844c2019-05-22 20:27:59 -0700563 nh = res->f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700564 goto out;
David Ahern75ef7382019-04-16 14:36:07 -0700565 }
566
David Ahernf88d8ea2019-06-03 20:19:52 -0700567 if (unlikely(f6i->nh)) {
568 nh = nexthop_fib6_nh(f6i->nh);
569 if (nexthop_is_blackhole(f6i->nh))
570 goto out_blackhole;
571 } else {
572 nh = f6i->fib6_nh;
573 }
574
David Ahern7d21fec2019-04-16 14:36:11 -0700575 if (nh->fib_nh_flags & RTNH_F_DEAD) {
David Ahern75ef7382019-04-16 14:36:07 -0700576 res->f6i = net->ipv6.fib6_null_entry;
David Ahern1cf844c2019-05-22 20:27:59 -0700577 nh = res->f6i->fib6_nh;
David Ahern75ef7382019-04-16 14:36:07 -0700578 }
David Ahern7d21fec2019-04-16 14:36:11 -0700579out:
580 res->nh = nh;
581 res->fib6_type = res->f6i->fib6_type;
582 res->fib6_flags = res->f6i->fib6_flags;
David Ahernf88d8ea2019-06-03 20:19:52 -0700583 return;
584
585out_blackhole:
586 res->fib6_flags |= RTF_REJECT;
587 res->fib6_type = RTN_BLACKHOLE;
588 res->nh = nh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800591#ifdef CONFIG_IPV6_ROUTER_PREF
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200592struct __rt6_probe_work {
593 struct work_struct work;
594 struct in6_addr target;
595 struct net_device *dev;
596};
597
598static void rt6_probe_deferred(struct work_struct *w)
599{
600 struct in6_addr mcaddr;
601 struct __rt6_probe_work *work =
602 container_of(w, struct __rt6_probe_work, work);
603
604 addrconf_addr_solict_mult(&work->target, &mcaddr);
Erik Nordmarkadc176c2016-12-02 14:00:08 -0800605 ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200606 dev_put(work->dev);
Michael Büsch662f5532015-02-08 10:14:07 +0100607 kfree(work);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200608}
609
David Aherncc3a86c2019-04-09 14:41:12 -0700610static void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800611{
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200612 struct __rt6_probe_work *work = NULL;
David Ahern5e670d82018-04-17 17:33:14 -0700613 const struct in6_addr *nh_gw;
Eric Dumazet1bef4c22019-11-07 09:26:19 -0800614 unsigned long last_probe;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000615 struct neighbour *neigh;
David Ahern5e670d82018-04-17 17:33:14 -0700616 struct net_device *dev;
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200617 struct inet6_dev *idev;
David Ahern5e670d82018-04-17 17:33:14 -0700618
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800619 /*
620 * Okay, this does not seem to be appropriate
621 * for now, however, we need to check if it
622 * is really so; aka Router Reachability Probing.
623 *
624 * Router Reachability Probe MUST be rate-limited
625 * to no more than one per minute.
626 */
Hangbin Liu004b3942019-11-20 15:39:06 +0800627 if (!fib6_nh->fib_nh_gw_family)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000628 return;
David Ahern5e670d82018-04-17 17:33:14 -0700629
David Aherncc3a86c2019-04-09 14:41:12 -0700630 nh_gw = &fib6_nh->fib_nh_gw6;
631 dev = fib6_nh->fib_nh_dev;
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000632 rcu_read_lock_bh();
Eric Dumazet1bef4c22019-11-07 09:26:19 -0800633 last_probe = READ_ONCE(fib6_nh->last_probe);
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200634 idev = __in6_dev_get(dev);
David Ahern5e670d82018-04-17 17:33:14 -0700635 neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000636 if (neigh) {
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700637 if (neigh->nud_state & NUD_VALID)
638 goto out;
639
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000640 write_lock(&neigh->lock);
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700641 if (!(neigh->nud_state & NUD_VALID) &&
642 time_after(jiffies,
David Aherndcd1f572018-04-18 15:39:05 -0700643 neigh->updated + idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700644 work = kmalloc(sizeof(*work), GFP_ATOMIC);
645 if (work)
646 __neigh_set_probe_once(neigh);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200647 }
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000648 write_unlock(&neigh->lock);
Eric Dumazet1bef4c22019-11-07 09:26:19 -0800649 } else if (time_after(jiffies, last_probe +
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200650 idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700651 work = kmalloc(sizeof(*work), GFP_ATOMIC);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000652 }
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700653
Eric Dumazet1bef4c22019-11-07 09:26:19 -0800654 if (!work || cmpxchg(&fib6_nh->last_probe,
655 last_probe, jiffies) != last_probe) {
656 kfree(work);
657 } else {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700658 INIT_WORK(&work->work, rt6_probe_deferred);
David Ahern5e670d82018-04-17 17:33:14 -0700659 work->target = *nh_gw;
660 dev_hold(dev);
661 work->dev = dev;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700662 schedule_work(&work->work);
663 }
664
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700665out:
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000666 rcu_read_unlock_bh();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800667}
668#else
David Aherncc3a86c2019-04-09 14:41:12 -0700669static inline void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800670{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800671}
672#endif
673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800675 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 */
David Ahern1ba9a892019-04-09 14:41:10 -0700677static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200679 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
David Ahern5e670d82018-04-17 17:33:14 -0700680 struct neighbour *neigh;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000681
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000682 rcu_read_lock_bh();
David Ahern1ba9a892019-04-09 14:41:10 -0700683 neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
684 &fib6_nh->fib_nh_gw6);
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000685 if (neigh) {
686 read_lock(&neigh->lock);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800687 if (neigh->nud_state & NUD_VALID)
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200688 ret = RT6_NUD_SUCCEED;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800689#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000690 else if (!(neigh->nud_state & NUD_FAILED))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200691 ret = RT6_NUD_SUCCEED;
Jiri Benc7e980562013-12-11 13:48:20 +0100692 else
693 ret = RT6_NUD_FAIL_PROBE;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800694#endif
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000695 read_unlock(&neigh->lock);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200696 } else {
697 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
Jiri Benc7e980562013-12-11 13:48:20 +0100698 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
Paul Marksa5a81f02012-12-03 10:26:54 +0000699 }
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000700 rcu_read_unlock_bh();
701
Paul Marksa5a81f02012-12-03 10:26:54 +0000702 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800703}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
David Ahern702cea52019-04-09 14:41:13 -0700705static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
706 int strict)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800707{
David Ahern6e1809a2019-04-09 14:41:11 -0700708 int m = 0;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900709
David Ahern6e1809a2019-04-09 14:41:11 -0700710 if (!oif || nh->fib_nh_dev->ifindex == oif)
711 m = 2;
712
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700713 if (!m && (strict & RT6_LOOKUP_F_IFACE))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200714 return RT6_NUD_FAIL_HARD;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800715#ifdef CONFIG_IPV6_ROUTER_PREF
David Ahern702cea52019-04-09 14:41:13 -0700716 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800717#endif
David Ahern1ba9a892019-04-09 14:41:10 -0700718 if ((strict & RT6_LOOKUP_F_REACHABLE) &&
David Ahern702cea52019-04-09 14:41:13 -0700719 !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
David Ahern1ba9a892019-04-09 14:41:10 -0700720 int n = rt6_check_neigh(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200721 if (n < 0)
722 return n;
723 }
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800724 return m;
725}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
David Ahern28679ed2019-04-09 14:41:14 -0700727static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
728 int oif, int strict, int *mpri, bool *do_rr)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800729{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200730 bool match_do_rr = false;
David Ahern28679ed2019-04-09 14:41:14 -0700731 bool rc = false;
732 int m;
Andy Gospodarek35103d12015-08-13 10:39:01 -0400733
David Ahern28679ed2019-04-09 14:41:14 -0700734 if (nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel8067bb82018-01-07 12:45:09 +0200735 goto out;
736
David Ahern28679ed2019-04-09 14:41:14 -0700737 if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
738 nh->fib_nh_flags & RTNH_F_LINKDOWN &&
David Ahernd5d32e42016-10-24 12:27:23 -0700739 !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
Andy Gospodarek35103d12015-08-13 10:39:01 -0400740 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700741
David Ahern28679ed2019-04-09 14:41:14 -0700742 m = rt6_score_route(nh, fib6_flags, oif, strict);
Jiri Benc7e980562013-12-11 13:48:20 +0100743 if (m == RT6_NUD_FAIL_DO_RR) {
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200744 match_do_rr = true;
745 m = 0; /* lowest valid score */
Jiri Benc7e980562013-12-11 13:48:20 +0100746 } else if (m == RT6_NUD_FAIL_HARD) {
David S. Millerf11e6652007-03-24 20:36:25 -0700747 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700748 }
749
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200750 if (strict & RT6_LOOKUP_F_REACHABLE)
David Ahern28679ed2019-04-09 14:41:14 -0700751 rt6_probe(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200752
Jiri Benc7e980562013-12-11 13:48:20 +0100753 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200754 if (m > *mpri) {
755 *do_rr = match_do_rr;
756 *mpri = m;
David Ahern28679ed2019-04-09 14:41:14 -0700757 rc = true;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200758 }
David S. Millerf11e6652007-03-24 20:36:25 -0700759out:
David Ahern28679ed2019-04-09 14:41:14 -0700760 return rc;
David S. Millerf11e6652007-03-24 20:36:25 -0700761}
762
David Ahern17a59842019-06-08 14:53:25 -0700763struct fib6_nh_frl_arg {
764 u32 flags;
765 int oif;
766 int strict;
767 int *mpri;
768 bool *do_rr;
769 struct fib6_nh *nh;
770};
771
772static int rt6_nh_find_match(struct fib6_nh *nh, void *_arg)
773{
774 struct fib6_nh_frl_arg *arg = _arg;
775
776 arg->nh = nh;
777 return find_match(nh, arg->flags, arg->oif, arg->strict,
778 arg->mpri, arg->do_rr);
779}
780
David Ahernb7bc4b62019-04-16 14:36:08 -0700781static void __find_rr_leaf(struct fib6_info *f6i_start,
David Ahern30c15f02019-04-09 14:41:15 -0700782 struct fib6_info *nomatch, u32 metric,
David Ahernb7bc4b62019-04-16 14:36:08 -0700783 struct fib6_result *res, struct fib6_info **cont,
David Ahern30c15f02019-04-09 14:41:15 -0700784 int oif, int strict, bool *do_rr, int *mpri)
David S. Millerf11e6652007-03-24 20:36:25 -0700785{
David Ahernb7bc4b62019-04-16 14:36:08 -0700786 struct fib6_info *f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700787
David Ahernb7bc4b62019-04-16 14:36:08 -0700788 for (f6i = f6i_start;
789 f6i && f6i != nomatch;
790 f6i = rcu_dereference(f6i->fib6_next)) {
David Ahern17a59842019-06-08 14:53:25 -0700791 bool matched = false;
David Ahern30c15f02019-04-09 14:41:15 -0700792 struct fib6_nh *nh;
793
David Ahernb7bc4b62019-04-16 14:36:08 -0700794 if (cont && f6i->fib6_metric != metric) {
795 *cont = f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700796 return;
797 }
798
David Ahernb7bc4b62019-04-16 14:36:08 -0700799 if (fib6_check_expired(f6i))
David Ahern30c15f02019-04-09 14:41:15 -0700800 continue;
801
David Ahern17a59842019-06-08 14:53:25 -0700802 if (unlikely(f6i->nh)) {
803 struct fib6_nh_frl_arg arg = {
804 .flags = f6i->fib6_flags,
805 .oif = oif,
806 .strict = strict,
807 .mpri = mpri,
808 .do_rr = do_rr
809 };
810
811 if (nexthop_is_blackhole(f6i->nh)) {
812 res->fib6_flags = RTF_REJECT;
813 res->fib6_type = RTN_BLACKHOLE;
814 res->f6i = f6i;
815 res->nh = nexthop_fib6_nh(f6i->nh);
816 return;
817 }
818 if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match,
819 &arg)) {
820 matched = true;
821 nh = arg.nh;
822 }
823 } else {
824 nh = f6i->fib6_nh;
825 if (find_match(nh, f6i->fib6_flags, oif, strict,
826 mpri, do_rr))
827 matched = true;
828 }
829 if (matched) {
David Ahernb7bc4b62019-04-16 14:36:08 -0700830 res->f6i = f6i;
831 res->nh = nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700832 res->fib6_flags = f6i->fib6_flags;
833 res->fib6_type = f6i->fib6_type;
David Ahernb7bc4b62019-04-16 14:36:08 -0700834 }
David Ahern30c15f02019-04-09 14:41:15 -0700835 }
836}
837
David Ahernb7bc4b62019-04-16 14:36:08 -0700838static void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
839 struct fib6_info *rr_head, int oif, int strict,
840 bool *do_rr, struct fib6_result *res)
David Ahern30c15f02019-04-09 14:41:15 -0700841{
David Ahernb7bc4b62019-04-16 14:36:08 -0700842 u32 metric = rr_head->fib6_metric;
843 struct fib6_info *cont = NULL;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800844 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
David Ahernb7bc4b62019-04-16 14:36:08 -0700846 __find_rr_leaf(rr_head, NULL, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700847 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700848
David Ahernb7bc4b62019-04-16 14:36:08 -0700849 __find_rr_leaf(leaf, rr_head, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700850 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700851
David Ahernb7bc4b62019-04-16 14:36:08 -0700852 if (res->f6i || !cont)
853 return;
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700854
David Ahernb7bc4b62019-04-16 14:36:08 -0700855 __find_rr_leaf(cont, NULL, metric, res, NULL,
David Ahern30c15f02019-04-09 14:41:15 -0700856 oif, strict, do_rr, &mpri);
David S. Millerf11e6652007-03-24 20:36:25 -0700857}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800858
David Ahernb7bc4b62019-04-16 14:36:08 -0700859static void rt6_select(struct net *net, struct fib6_node *fn, int oif,
860 struct fib6_result *res, int strict)
David S. Millerf11e6652007-03-24 20:36:25 -0700861{
David Ahern8d1c8022018-04-17 17:33:26 -0700862 struct fib6_info *leaf = rcu_dereference(fn->leaf);
David Ahernb7bc4b62019-04-16 14:36:08 -0700863 struct fib6_info *rt0;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200864 bool do_rr = false;
Wei Wang17ecf592017-10-06 12:06:09 -0700865 int key_plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
David Ahernb7bc4b62019-04-16 14:36:08 -0700867 /* make sure this function or its helpers sets f6i */
868 res->f6i = NULL;
869
David Ahern421842e2018-04-17 17:33:18 -0700870 if (!leaf || leaf == net->ipv6.fib6_null_entry)
David Ahernb7bc4b62019-04-16 14:36:08 -0700871 goto out;
Wei Wang8d1040e2017-10-06 12:06:08 -0700872
Wei Wang66f5d6c2017-10-06 12:06:10 -0700873 rt0 = rcu_dereference(fn->rr_ptr);
David S. Millerf11e6652007-03-24 20:36:25 -0700874 if (!rt0)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700875 rt0 = leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
Wei Wang17ecf592017-10-06 12:06:09 -0700877 /* Double check to make sure fn is not an intermediate node
878 * and fn->leaf does not points to its child's leaf
879 * (This might happen if all routes under fn are deleted from
880 * the tree and fib6_repair_tree() is called on the node.)
881 */
David Ahern93c2fb22018-04-18 15:38:59 -0700882 key_plen = rt0->fib6_dst.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700883#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -0700884 if (rt0->fib6_src.plen)
885 key_plen = rt0->fib6_src.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700886#endif
887 if (fn->fn_bit != key_plen)
David Ahernb7bc4b62019-04-16 14:36:08 -0700888 goto out;
Wei Wang17ecf592017-10-06 12:06:09 -0700889
David Ahernb7bc4b62019-04-16 14:36:08 -0700890 find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200891 if (do_rr) {
David Ahern8fb11a92018-05-04 13:54:24 -0700892 struct fib6_info *next = rcu_dereference(rt0->fib6_next);
David S. Millerf11e6652007-03-24 20:36:25 -0700893
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800894 /* no entries matched; do round-robin */
David Ahern93c2fb22018-04-18 15:38:59 -0700895 if (!next || next->fib6_metric != rt0->fib6_metric)
Wei Wang8d1040e2017-10-06 12:06:08 -0700896 next = leaf;
David S. Millerf11e6652007-03-24 20:36:25 -0700897
Wei Wang66f5d6c2017-10-06 12:06:10 -0700898 if (next != rt0) {
David Ahern93c2fb22018-04-18 15:38:59 -0700899 spin_lock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700900 /* make sure next is not being deleted from the tree */
David Ahern93c2fb22018-04-18 15:38:59 -0700901 if (next->fib6_node)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700902 rcu_assign_pointer(fn->rr_ptr, next);
David Ahern93c2fb22018-04-18 15:38:59 -0700903 spin_unlock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700904 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 }
906
David Ahernb7bc4b62019-04-16 14:36:08 -0700907out:
908 if (!res->f6i) {
909 res->f6i = net->ipv6.fib6_null_entry;
David Ahern1cf844c2019-05-22 20:27:59 -0700910 res->nh = res->f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700911 res->fib6_flags = res->f6i->fib6_flags;
912 res->fib6_type = res->f6i->fib6_type;
David Ahernb7bc4b62019-04-16 14:36:08 -0700913 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914}
915
David Ahern85bd05d2019-04-16 14:36:01 -0700916static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700917{
David Ahern85bd05d2019-04-16 14:36:01 -0700918 return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
919 res->nh->fib_nh_gw_family;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700920}
921
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800922#ifdef CONFIG_IPV6_ROUTE_INFO
923int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000924 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800925{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900926 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800927 struct route_info *rinfo = (struct route_info *) opt;
928 struct in6_addr prefix_buf, *prefix;
929 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900930 unsigned long lifetime;
David Ahern8d1c8022018-04-17 17:33:26 -0700931 struct fib6_info *rt;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800932
933 if (len < sizeof(struct route_info)) {
934 return -EINVAL;
935 }
936
937 /* Sanity check for prefix_len and length */
938 if (rinfo->length > 3) {
939 return -EINVAL;
940 } else if (rinfo->prefix_len > 128) {
941 return -EINVAL;
942 } else if (rinfo->prefix_len > 64) {
943 if (rinfo->length < 2) {
944 return -EINVAL;
945 }
946 } else if (rinfo->prefix_len > 0) {
947 if (rinfo->length < 1) {
948 return -EINVAL;
949 }
950 }
951
952 pref = rinfo->route_pref;
953 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000954 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800955
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900956 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800957
958 if (rinfo->length == 3)
959 prefix = (struct in6_addr *)rinfo->prefix;
960 else {
961 /* this function is safe */
962 ipv6_addr_prefix(&prefix_buf,
963 (struct in6_addr *)rinfo->prefix,
964 rinfo->prefix_len);
965 prefix = &prefix_buf;
966 }
967
Duan Jiongf104a562013-11-08 09:56:53 +0800968 if (rinfo->prefix_len == 0)
David Ahernafb1d4b52018-04-17 17:33:11 -0700969 rt = rt6_get_dflt_router(net, gwaddr, dev);
Duan Jiongf104a562013-11-08 09:56:53 +0800970 else
971 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
David Ahern830218c2016-10-24 10:52:35 -0700972 gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800973
974 if (rt && !lifetime) {
Roopa Prabhu11dd74b2020-04-27 13:56:45 -0700975 ip6_del_rt(net, rt, false);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800976 rt = NULL;
977 }
978
979 if (!rt && lifetime)
David Ahern830218c2016-10-24 10:52:35 -0700980 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
981 dev, pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800982 else if (rt)
David Ahern93c2fb22018-04-18 15:38:59 -0700983 rt->fib6_flags = RTF_ROUTEINFO |
984 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800985
986 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000987 if (!addrconf_finite_timeout(lifetime))
David Ahern14895682018-04-17 17:33:17 -0700988 fib6_clean_expires(rt);
Gao feng1716a962012-04-06 00:13:10 +0000989 else
David Ahern14895682018-04-17 17:33:17 -0700990 fib6_set_expires(rt, jiffies + HZ * lifetime);
Gao feng1716a962012-04-06 00:13:10 +0000991
David Ahern93531c62018-04-17 17:33:25 -0700992 fib6_info_release(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800993 }
994 return 0;
995}
996#endif
997
David Ahernae90d862018-04-17 17:33:12 -0700998/*
999 * Misc support functions
1000 */
1001
1002/* called with rcu_lock held */
David Ahern0d161582019-04-16 14:36:04 -07001003static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -07001004{
David Ahern0d161582019-04-16 14:36:04 -07001005 struct net_device *dev = res->nh->fib_nh_dev;
David Ahernae90d862018-04-17 17:33:12 -07001006
David Ahern7d21fec2019-04-16 14:36:11 -07001007 if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
David Ahernae90d862018-04-17 17:33:12 -07001008 /* for copies of local routes, dst->dev needs to be the
1009 * device if it is a master device, the master device if
1010 * device is enslaved, and the loopback as the default
1011 */
1012 if (netif_is_l3_slave(dev) &&
David Ahern7d21fec2019-04-16 14:36:11 -07001013 !rt6_need_strict(&res->f6i->fib6_dst.addr))
David Ahernae90d862018-04-17 17:33:12 -07001014 dev = l3mdev_master_dev_rcu(dev);
1015 else if (!netif_is_l3_master(dev))
1016 dev = dev_net(dev)->loopback_dev;
1017 /* last case is netif_is_l3_master(dev) is true in which
1018 * case we want dev returned to be dev
1019 */
1020 }
1021
1022 return dev;
1023}
1024
David Ahern6edb3c92018-04-17 17:33:15 -07001025static const int fib6_prop[RTN_MAX + 1] = {
1026 [RTN_UNSPEC] = 0,
1027 [RTN_UNICAST] = 0,
1028 [RTN_LOCAL] = 0,
1029 [RTN_BROADCAST] = 0,
1030 [RTN_ANYCAST] = 0,
1031 [RTN_MULTICAST] = 0,
1032 [RTN_BLACKHOLE] = -EINVAL,
1033 [RTN_UNREACHABLE] = -EHOSTUNREACH,
1034 [RTN_PROHIBIT] = -EACCES,
1035 [RTN_THROW] = -EAGAIN,
1036 [RTN_NAT] = -EINVAL,
1037 [RTN_XRESOLVE] = -EINVAL,
1038};
1039
1040static int ip6_rt_type_to_error(u8 fib6_type)
1041{
1042 return fib6_prop[fib6_type];
1043}
1044
David Ahern8d1c8022018-04-17 17:33:26 -07001045static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
David Ahern3b6761d2018-04-17 17:33:20 -07001046{
1047 unsigned short flags = 0;
1048
1049 if (rt->dst_nocount)
1050 flags |= DST_NOCOUNT;
1051 if (rt->dst_nopolicy)
1052 flags |= DST_NOPOLICY;
David Ahern3b6761d2018-04-17 17:33:20 -07001053
1054 return flags;
1055}
1056
David Ahern7d21fec2019-04-16 14:36:11 -07001057static void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type)
David Ahern6edb3c92018-04-17 17:33:15 -07001058{
David Ahern7d21fec2019-04-16 14:36:11 -07001059 rt->dst.error = ip6_rt_type_to_error(fib6_type);
David Ahern6edb3c92018-04-17 17:33:15 -07001060
David Ahern7d21fec2019-04-16 14:36:11 -07001061 switch (fib6_type) {
David Ahern6edb3c92018-04-17 17:33:15 -07001062 case RTN_BLACKHOLE:
1063 rt->dst.output = dst_discard_out;
1064 rt->dst.input = dst_discard;
1065 break;
1066 case RTN_PROHIBIT:
1067 rt->dst.output = ip6_pkt_prohibit_out;
1068 rt->dst.input = ip6_pkt_prohibit;
1069 break;
1070 case RTN_THROW:
1071 case RTN_UNREACHABLE:
1072 default:
1073 rt->dst.output = ip6_pkt_discard_out;
1074 rt->dst.input = ip6_pkt_discard;
1075 break;
1076 }
1077}
1078
David Ahern0d161582019-04-16 14:36:04 -07001079static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
David Ahern6edb3c92018-04-17 17:33:15 -07001080{
David Ahern7d21fec2019-04-16 14:36:11 -07001081 struct fib6_info *f6i = res->f6i;
David Ahern0d161582019-04-16 14:36:04 -07001082
David Ahern7d21fec2019-04-16 14:36:11 -07001083 if (res->fib6_flags & RTF_REJECT) {
1084 ip6_rt_init_dst_reject(rt, res->fib6_type);
David Ahern6edb3c92018-04-17 17:33:15 -07001085 return;
1086 }
1087
1088 rt->dst.error = 0;
1089 rt->dst.output = ip6_output;
1090
David Ahern7d21fec2019-04-16 14:36:11 -07001091 if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) {
David Ahern6edb3c92018-04-17 17:33:15 -07001092 rt->dst.input = ip6_input;
David Ahern7d21fec2019-04-16 14:36:11 -07001093 } else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
David Ahern6edb3c92018-04-17 17:33:15 -07001094 rt->dst.input = ip6_mc_input;
1095 } else {
1096 rt->dst.input = ip6_forward;
1097 }
1098
David Ahern0d161582019-04-16 14:36:04 -07001099 if (res->nh->fib_nh_lws) {
1100 rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
David Ahern6edb3c92018-04-17 17:33:15 -07001101 lwtunnel_set_redirect(&rt->dst);
1102 }
1103
1104 rt->dst.lastuse = jiffies;
1105}
1106
Wei Wange873e4b2018-07-21 20:56:32 -07001107/* Caller must already hold reference to @from */
David Ahern8d1c8022018-04-17 17:33:26 -07001108static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
David Ahernae90d862018-04-17 17:33:12 -07001109{
David Ahernae90d862018-04-17 17:33:12 -07001110 rt->rt6i_flags &= ~RTF_EXPIRES;
David Aherna68886a2018-04-20 15:38:02 -07001111 rcu_assign_pointer(rt->from, from);
David Aherne1255ed2018-10-04 20:07:53 -07001112 ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
David Ahernae90d862018-04-17 17:33:12 -07001113}
1114
David Ahern0d161582019-04-16 14:36:04 -07001115/* Caller must already hold reference to f6i in result */
1116static void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -07001117{
David Ahern0d161582019-04-16 14:36:04 -07001118 const struct fib6_nh *nh = res->nh;
1119 const struct net_device *dev = nh->fib_nh_dev;
1120 struct fib6_info *f6i = res->f6i;
David Aherndcd1f572018-04-18 15:39:05 -07001121
David Ahern0d161582019-04-16 14:36:04 -07001122 ip6_rt_init_dst(rt, res);
David Ahern6edb3c92018-04-17 17:33:15 -07001123
David Ahern0d161582019-04-16 14:36:04 -07001124 rt->rt6i_dst = f6i->fib6_dst;
David Aherndcd1f572018-04-18 15:39:05 -07001125 rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
David Ahern7d21fec2019-04-16 14:36:11 -07001126 rt->rt6i_flags = res->fib6_flags;
David Ahern0d161582019-04-16 14:36:04 -07001127 if (nh->fib_nh_gw_family) {
1128 rt->rt6i_gateway = nh->fib_nh_gw6;
David Ahern2b2450c2019-03-27 20:53:52 -07001129 rt->rt6i_flags |= RTF_GATEWAY;
1130 }
David Ahern0d161582019-04-16 14:36:04 -07001131 rt6_set_from(rt, f6i);
David Ahernae90d862018-04-17 17:33:12 -07001132#ifdef CONFIG_IPV6_SUBTREES
David Ahern0d161582019-04-16 14:36:04 -07001133 rt->rt6i_src = f6i->fib6_src;
David Ahernae90d862018-04-17 17:33:12 -07001134#endif
David Ahernae90d862018-04-17 17:33:12 -07001135}
1136
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001137static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1138 struct in6_addr *saddr)
1139{
Wei Wang66f5d6c2017-10-06 12:06:10 -07001140 struct fib6_node *pn, *sn;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001141 while (1) {
1142 if (fn->fn_flags & RTN_TL_ROOT)
1143 return NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001144 pn = rcu_dereference(fn->parent);
1145 sn = FIB6_SUBTREE(pn);
1146 if (sn && sn != fn)
David Ahern64547432018-05-09 20:34:19 -07001147 fn = fib6_node_lookup(sn, NULL, saddr);
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001148 else
1149 fn = pn;
1150 if (fn->fn_flags & RTN_RTINFO)
1151 return fn;
1152 }
1153}
Thomas Grafc71099a2006-08-04 23:20:06 -07001154
David Ahern10585b42019-03-20 09:24:50 -07001155static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
Wei Wangd3843fe2017-10-06 12:06:06 -07001156{
1157 struct rt6_info *rt = *prt;
1158
1159 if (dst_hold_safe(&rt->dst))
1160 return true;
David Ahern10585b42019-03-20 09:24:50 -07001161 if (net) {
Wei Wangd3843fe2017-10-06 12:06:06 -07001162 rt = net->ipv6.ip6_null_entry;
1163 dst_hold(&rt->dst);
1164 } else {
1165 rt = NULL;
1166 }
1167 *prt = rt;
1168 return false;
1169}
1170
David Aherndec9b0e2018-04-17 17:33:19 -07001171/* called with rcu_lock held */
David Ahern9b6b35a2019-04-16 14:36:02 -07001172static struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
David Aherndec9b0e2018-04-17 17:33:19 -07001173{
David Ahern9b6b35a2019-04-16 14:36:02 -07001174 struct net_device *dev = res->nh->fib_nh_dev;
1175 struct fib6_info *f6i = res->f6i;
1176 unsigned short flags;
David Aherndec9b0e2018-04-17 17:33:19 -07001177 struct rt6_info *nrt;
1178
David Ahern9b6b35a2019-04-16 14:36:02 -07001179 if (!fib6_info_hold_safe(f6i))
Xin Long1c87e792019-03-20 14:45:48 +08001180 goto fallback;
Wei Wange873e4b2018-07-21 20:56:32 -07001181
David Ahern9b6b35a2019-04-16 14:36:02 -07001182 flags = fib6_info_dst_flags(f6i);
David Ahern93531c62018-04-17 17:33:25 -07001183 nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
Xin Long1c87e792019-03-20 14:45:48 +08001184 if (!nrt) {
David Ahern9b6b35a2019-04-16 14:36:02 -07001185 fib6_info_release(f6i);
Xin Long1c87e792019-03-20 14:45:48 +08001186 goto fallback;
1187 }
David Aherndec9b0e2018-04-17 17:33:19 -07001188
David Ahern0d161582019-04-16 14:36:04 -07001189 ip6_rt_copy_init(nrt, res);
Xin Long1c87e792019-03-20 14:45:48 +08001190 return nrt;
1191
1192fallback:
1193 nrt = dev_net(dev)->ipv6.ip6_null_entry;
1194 dst_hold(&nrt->dst);
David Aherndec9b0e2018-04-17 17:33:19 -07001195 return nrt;
1196}
1197
Brian Vazquez55cced42020-06-23 09:42:32 -07001198INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001199 struct fib6_table *table,
David Ahernb75cc8f2018-03-02 08:32:17 -08001200 struct flowi6 *fl6,
1201 const struct sk_buff *skb,
1202 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203{
David Ahernb1d40992019-04-16 14:35:59 -07001204 struct fib6_result res = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 struct fib6_node *fn;
David Ahern23fb93a2018-04-17 17:33:23 -07001206 struct rt6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207
David Ahernb6cdbc82018-03-29 17:44:57 -07001208 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1209 flags &= ~RT6_LOOKUP_F_IFACE;
1210
Wei Wang66f5d6c2017-10-06 12:06:10 -07001211 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07001212 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -07001213restart:
David Ahernb1d40992019-04-16 14:35:59 -07001214 res.f6i = rcu_dereference(fn->leaf);
1215 if (!res.f6i)
1216 res.f6i = net->ipv6.fib6_null_entry;
David Ahernaf52a522019-04-09 14:41:16 -07001217 else
David Ahern75ef7382019-04-16 14:36:07 -07001218 rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
1219 flags);
David Ahernaf52a522019-04-09 14:41:16 -07001220
David Ahernb1d40992019-04-16 14:35:59 -07001221 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001222 fn = fib6_backtrack(fn, &fl6->saddr);
1223 if (fn)
1224 goto restart;
David Ahernaf52a522019-04-09 14:41:16 -07001225
1226 rt = net->ipv6.ip6_null_entry;
1227 dst_hold(&rt->dst);
1228 goto out;
David Ahernf88d8ea2019-06-03 20:19:52 -07001229 } else if (res.fib6_flags & RTF_REJECT) {
1230 goto do_create;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001231 }
Wei Wang2b760fc2017-10-06 12:06:03 -07001232
David Ahernb1d40992019-04-16 14:35:59 -07001233 fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1234 fl6->flowi6_oif != 0, skb, flags);
1235
David S. Miller4c9483b2011-03-12 16:22:43 -05001236 /* Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001237 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001238 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001239 if (ip6_hold_safe(net, &rt))
David Aherndec9b0e2018-04-17 17:33:19 -07001240 dst_use_noref(&rt->dst, jiffies);
David Ahern23fb93a2018-04-17 17:33:23 -07001241 } else {
David Ahernf88d8ea2019-06-03 20:19:52 -07001242do_create:
David Ahern9b6b35a2019-04-16 14:36:02 -07001243 rt = ip6_create_rt_rcu(&res);
David Aherndec9b0e2018-04-17 17:33:19 -07001244 }
Wei Wangd3843fe2017-10-06 12:06:06 -07001245
David Ahernaf52a522019-04-09 14:41:16 -07001246out:
David Ahern8ff2e5b2019-04-16 14:36:09 -07001247 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahernaf52a522019-04-09 14:41:16 -07001248
Wei Wang66f5d6c2017-10-06 12:06:10 -07001249 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08001250
Thomas Grafc71099a2006-08-04 23:20:06 -07001251 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001252}
1253
Ian Morris67ba4152014-08-24 21:53:10 +01001254struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08001255 const struct sk_buff *skb, int flags)
Florian Westphalea6e5742011-09-05 16:05:44 +02001256{
David Ahernb75cc8f2018-03-02 08:32:17 -08001257 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
Florian Westphalea6e5742011-09-05 16:05:44 +02001258}
1259EXPORT_SYMBOL_GPL(ip6_route_lookup);
1260
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001261struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
David Ahernb75cc8f2018-03-02 08:32:17 -08001262 const struct in6_addr *saddr, int oif,
1263 const struct sk_buff *skb, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -07001264{
David S. Miller4c9483b2011-03-12 16:22:43 -05001265 struct flowi6 fl6 = {
1266 .flowi6_oif = oif,
1267 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -07001268 };
1269 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001270 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07001271
Thomas Grafadaa70b2006-10-13 15:01:03 -07001272 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -05001273 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -07001274 flags |= RT6_LOOKUP_F_HAS_SADDR;
1275 }
1276
David Ahernb75cc8f2018-03-02 08:32:17 -08001277 dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -07001278 if (dst->error == 0)
1279 return (struct rt6_info *) dst;
1280
1281 dst_release(dst);
1282
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 return NULL;
1284}
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001285EXPORT_SYMBOL(rt6_lookup);
1286
Thomas Grafc71099a2006-08-04 23:20:06 -07001287/* ip6_ins_rt is called with FREE table->tb6_lock.
Wei Wang1cfb71e2017-06-17 10:42:33 -07001288 * It takes new route entry, the addition fails by any reason the
1289 * route is released.
1290 * Caller must hold dst before calling it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 */
1292
David Ahern8d1c8022018-04-17 17:33:26 -07001293static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
David Ahern333c4302017-05-21 10:12:04 -06001294 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295{
1296 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001297 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
David Ahern93c2fb22018-04-18 15:38:59 -07001299 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001300 spin_lock_bh(&table->tb6_lock);
David Ahernd4ead6b2018-04-17 17:33:16 -07001301 err = fib6_add(&table->tb6_root, rt, info, extack);
Wei Wang66f5d6c2017-10-06 12:06:10 -07001302 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
1304 return err;
1305}
1306
David Ahern8d1c8022018-04-17 17:33:26 -07001307int ip6_ins_rt(struct net *net, struct fib6_info *rt)
Thomas Graf40e22e82006-08-22 00:00:45 -07001308{
David Ahernafb1d4b52018-04-17 17:33:11 -07001309 struct nl_info info = { .nl_net = net, };
Florian Westphale715b6d2015-01-05 23:57:44 +01001310
David Ahernd4ead6b2018-04-17 17:33:16 -07001311 return __ip6_ins_rt(rt, &info, NULL);
Thomas Graf40e22e82006-08-22 00:00:45 -07001312}
1313
David Ahern85bd05d2019-04-16 14:36:01 -07001314static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001315 const struct in6_addr *daddr,
1316 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317{
David Ahern85bd05d2019-04-16 14:36:01 -07001318 struct fib6_info *f6i = res->f6i;
David Ahern4832c302017-08-17 12:17:20 -07001319 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 struct rt6_info *rt;
1321
1322 /*
1323 * Clone the route.
1324 */
1325
David Ahern85bd05d2019-04-16 14:36:01 -07001326 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001327 return NULL;
1328
David Ahern0d161582019-04-16 14:36:04 -07001329 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001330 rt = ip6_dst_alloc(dev_net(dev), dev, 0);
Wei Wange873e4b2018-07-21 20:56:32 -07001331 if (!rt) {
David Ahern85bd05d2019-04-16 14:36:01 -07001332 fib6_info_release(f6i);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001333 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001334 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001335
David Ahern0d161582019-04-16 14:36:04 -07001336 ip6_rt_copy_init(rt, res);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001337 rt->rt6i_flags |= RTF_CACHE;
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001338 rt->rt6i_dst.addr = *daddr;
1339 rt->rt6i_dst.plen = 128;
1340
David Ahern85bd05d2019-04-16 14:36:01 -07001341 if (!rt6_is_gw_or_nonexthop(res)) {
1342 if (f6i->fib6_dst.plen != 128 &&
1343 ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001344 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345#ifdef CONFIG_IPV6_SUBTREES
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001346 if (rt->rt6i_src.plen && saddr) {
1347 rt->rt6i_src.addr = *saddr;
1348 rt->rt6i_src.plen = 128;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001349 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001350#endif
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001351 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001353 return rt;
1354}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
David Aherndb3fede2019-04-16 14:36:03 -07001356static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001357{
David Aherndb3fede2019-04-16 14:36:03 -07001358 struct fib6_info *f6i = res->f6i;
1359 unsigned short flags = fib6_info_dst_flags(f6i);
David Ahern4832c302017-08-17 12:17:20 -07001360 struct net_device *dev;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001361 struct rt6_info *pcpu_rt;
1362
David Aherndb3fede2019-04-16 14:36:03 -07001363 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001364 return NULL;
1365
David Ahern4832c302017-08-17 12:17:20 -07001366 rcu_read_lock();
David Ahern0d161582019-04-16 14:36:04 -07001367 dev = ip6_rt_get_dev_rcu(res);
Eric Dumazetd88829352020-05-08 07:34:14 -07001368 pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags | DST_NOCOUNT);
David Ahern4832c302017-08-17 12:17:20 -07001369 rcu_read_unlock();
Wei Wange873e4b2018-07-21 20:56:32 -07001370 if (!pcpu_rt) {
David Aherndb3fede2019-04-16 14:36:03 -07001371 fib6_info_release(f6i);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001372 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001373 }
David Ahern0d161582019-04-16 14:36:04 -07001374 ip6_rt_copy_init(pcpu_rt, res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001375 pcpu_rt->rt6i_flags |= RTF_PCPU;
David Ahern8f34e532020-05-01 08:53:08 -06001376
1377 if (f6i->nh)
1378 pcpu_rt->sernum = rt_genid_ipv6(dev_net(dev));
1379
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001380 return pcpu_rt;
1381}
1382
David Ahern8f34e532020-05-01 08:53:08 -06001383static bool rt6_is_valid(const struct rt6_info *rt6)
1384{
1385 return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev));
1386}
1387
Wei Wang66f5d6c2017-10-06 12:06:10 -07001388/* It should be called with rcu_read_lock() acquired */
David Aherndb3fede2019-04-16 14:36:03 -07001389static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001390{
Eric Dumazetc3530712019-05-31 18:11:25 -07001391 struct rt6_info *pcpu_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001392
Eric Dumazetc3530712019-05-31 18:11:25 -07001393 pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001394
David Ahern8f34e532020-05-01 08:53:08 -06001395 if (pcpu_rt && pcpu_rt->sernum && !rt6_is_valid(pcpu_rt)) {
1396 struct rt6_info *prev, **p;
1397
1398 p = this_cpu_ptr(res->nh->rt6i_pcpu);
1399 prev = xchg(p, NULL);
1400 if (prev) {
1401 dst_dev_put(&prev->dst);
1402 dst_release(&prev->dst);
1403 }
1404
1405 pcpu_rt = NULL;
1406 }
1407
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001408 return pcpu_rt;
1409}
1410
David Ahernafb1d4b52018-04-17 17:33:11 -07001411static struct rt6_info *rt6_make_pcpu_route(struct net *net,
David Aherndb3fede2019-04-16 14:36:03 -07001412 const struct fib6_result *res)
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001413{
1414 struct rt6_info *pcpu_rt, *prev, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001415
David Aherndb3fede2019-04-16 14:36:03 -07001416 pcpu_rt = ip6_rt_pcpu_alloc(res);
Wei Wang0e09edc2019-06-20 17:36:37 -07001417 if (!pcpu_rt)
1418 return NULL;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001419
David Ahernf40b6ae2019-05-22 20:27:55 -07001420 p = this_cpu_ptr(res->nh->rt6i_pcpu);
Wei Wanga94b9362017-10-06 12:06:04 -07001421 prev = cmpxchg(p, NULL, pcpu_rt);
Eric Dumazet951f7882017-10-08 21:07:18 -07001422 BUG_ON(prev);
Wei Wanga94b9362017-10-06 12:06:04 -07001423
Eric Dumazet61fb0d02019-05-15 19:39:52 -07001424 if (res->f6i->fib6_destroying) {
1425 struct fib6_info *from;
1426
1427 from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
1428 fib6_info_release(from);
1429 }
1430
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001431 return pcpu_rt;
1432}
1433
Wei Wang35732d02017-10-06 12:05:57 -07001434/* exception hash table implementation
1435 */
1436static DEFINE_SPINLOCK(rt6_exception_lock);
1437
1438/* Remove rt6_ex from hash table and free the memory
1439 * Caller must hold rt6_exception_lock
1440 */
1441static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1442 struct rt6_exception *rt6_ex)
1443{
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001444 struct fib6_info *from;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001445 struct net *net;
Wei Wang81eb8442017-10-06 12:06:11 -07001446
Wei Wang35732d02017-10-06 12:05:57 -07001447 if (!bucket || !rt6_ex)
1448 return;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001449
1450 net = dev_net(rt6_ex->rt6i->dst.dev);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001451 net->ipv6.rt6_stats->fib_rt_cache--;
1452
1453 /* purge completely the exception to allow releasing the held resources:
1454 * some [sk] cache may keep the dst around for unlimited time
1455 */
Eric Dumazet0e233872019-04-28 12:22:25 -07001456 from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001457 fib6_info_release(from);
1458 dst_dev_put(&rt6_ex->rt6i->dst);
1459
Wei Wang35732d02017-10-06 12:05:57 -07001460 hlist_del_rcu(&rt6_ex->hlist);
David Ahern77634cc2018-04-17 17:33:27 -07001461 dst_release(&rt6_ex->rt6i->dst);
Wei Wang35732d02017-10-06 12:05:57 -07001462 kfree_rcu(rt6_ex, rcu);
1463 WARN_ON_ONCE(!bucket->depth);
1464 bucket->depth--;
1465}
1466
1467/* Remove oldest rt6_ex in bucket and free the memory
1468 * Caller must hold rt6_exception_lock
1469 */
1470static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
1471{
1472 struct rt6_exception *rt6_ex, *oldest = NULL;
1473
1474 if (!bucket)
1475 return;
1476
1477 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1478 if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
1479 oldest = rt6_ex;
1480 }
1481 rt6_remove_exception(bucket, oldest);
1482}
1483
1484static u32 rt6_exception_hash(const struct in6_addr *dst,
1485 const struct in6_addr *src)
1486{
1487 static u32 seed __read_mostly;
1488 u32 val;
1489
1490 net_get_random_once(&seed, sizeof(seed));
Eric Dumazetb6b556a2019-11-03 18:24:16 -08001491 val = jhash2((const u32 *)dst, sizeof(*dst)/sizeof(u32), seed);
Wei Wang35732d02017-10-06 12:05:57 -07001492
1493#ifdef CONFIG_IPV6_SUBTREES
1494 if (src)
Eric Dumazetb6b556a2019-11-03 18:24:16 -08001495 val = jhash2((const u32 *)src, sizeof(*src)/sizeof(u32), val);
Wei Wang35732d02017-10-06 12:05:57 -07001496#endif
1497 return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
1498}
1499
1500/* Helper function to find the cached rt in the hash table
1501 * and update bucket pointer to point to the bucket for this
1502 * (daddr, saddr) pair
1503 * Caller must hold rt6_exception_lock
1504 */
1505static struct rt6_exception *
1506__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
1507 const struct in6_addr *daddr,
1508 const struct in6_addr *saddr)
1509{
1510 struct rt6_exception *rt6_ex;
1511 u32 hval;
1512
1513 if (!(*bucket) || !daddr)
1514 return NULL;
1515
1516 hval = rt6_exception_hash(daddr, saddr);
1517 *bucket += hval;
1518
1519 hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
1520 struct rt6_info *rt6 = rt6_ex->rt6i;
1521 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1522
1523#ifdef CONFIG_IPV6_SUBTREES
1524 if (matched && saddr)
1525 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1526#endif
1527 if (matched)
1528 return rt6_ex;
1529 }
1530 return NULL;
1531}
1532
1533/* Helper function to find the cached rt in the hash table
1534 * and update bucket pointer to point to the bucket for this
1535 * (daddr, saddr) pair
1536 * Caller must hold rcu_read_lock()
1537 */
1538static struct rt6_exception *
1539__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
1540 const struct in6_addr *daddr,
1541 const struct in6_addr *saddr)
1542{
1543 struct rt6_exception *rt6_ex;
1544 u32 hval;
1545
1546 WARN_ON_ONCE(!rcu_read_lock_held());
1547
1548 if (!(*bucket) || !daddr)
1549 return NULL;
1550
1551 hval = rt6_exception_hash(daddr, saddr);
1552 *bucket += hval;
1553
1554 hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
1555 struct rt6_info *rt6 = rt6_ex->rt6i;
1556 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1557
1558#ifdef CONFIG_IPV6_SUBTREES
1559 if (matched && saddr)
1560 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1561#endif
1562 if (matched)
1563 return rt6_ex;
1564 }
1565 return NULL;
1566}
1567
David Ahernb748f262019-04-16 14:36:06 -07001568static unsigned int fib6_mtu(const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001569{
David Ahernb748f262019-04-16 14:36:06 -07001570 const struct fib6_nh *nh = res->nh;
David Ahernd4ead6b2018-04-17 17:33:16 -07001571 unsigned int mtu;
1572
David Ahernb748f262019-04-16 14:36:06 -07001573 if (res->f6i->fib6_pmtu) {
1574 mtu = res->f6i->fib6_pmtu;
David Aherndcd1f572018-04-18 15:39:05 -07001575 } else {
David Ahernb748f262019-04-16 14:36:06 -07001576 struct net_device *dev = nh->fib_nh_dev;
David Aherndcd1f572018-04-18 15:39:05 -07001577 struct inet6_dev *idev;
1578
1579 rcu_read_lock();
1580 idev = __in6_dev_get(dev);
1581 mtu = idev->cnf.mtu6;
1582 rcu_read_unlock();
1583 }
1584
David Ahernd4ead6b2018-04-17 17:33:16 -07001585 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1586
David Ahernb748f262019-04-16 14:36:06 -07001587 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahernd4ead6b2018-04-17 17:33:16 -07001588}
1589
David Aherncc5c0732019-05-22 20:27:58 -07001590#define FIB6_EXCEPTION_BUCKET_FLUSHED 0x1UL
1591
1592/* used when the flushed bit is not relevant, only access to the bucket
1593 * (ie., all bucket users except rt6_insert_exception);
1594 *
1595 * called under rcu lock; sometimes called with rt6_exception_lock held
1596 */
1597static
1598struct rt6_exception_bucket *fib6_nh_get_excptn_bucket(const struct fib6_nh *nh,
1599 spinlock_t *lock)
1600{
1601 struct rt6_exception_bucket *bucket;
1602
1603 if (lock)
1604 bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
1605 lockdep_is_held(lock));
1606 else
1607 bucket = rcu_dereference(nh->rt6i_exception_bucket);
1608
1609 /* remove bucket flushed bit if set */
1610 if (bucket) {
1611 unsigned long p = (unsigned long)bucket;
1612
1613 p &= ~FIB6_EXCEPTION_BUCKET_FLUSHED;
1614 bucket = (struct rt6_exception_bucket *)p;
1615 }
1616
1617 return bucket;
1618}
1619
1620static bool fib6_nh_excptn_bucket_flushed(struct rt6_exception_bucket *bucket)
1621{
1622 unsigned long p = (unsigned long)bucket;
1623
1624 return !!(p & FIB6_EXCEPTION_BUCKET_FLUSHED);
1625}
1626
1627/* called with rt6_exception_lock held */
1628static void fib6_nh_excptn_bucket_set_flushed(struct fib6_nh *nh,
1629 spinlock_t *lock)
1630{
1631 struct rt6_exception_bucket *bucket;
1632 unsigned long p;
1633
1634 bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
1635 lockdep_is_held(lock));
1636
1637 p = (unsigned long)bucket;
1638 p |= FIB6_EXCEPTION_BUCKET_FLUSHED;
1639 bucket = (struct rt6_exception_bucket *)p;
1640 rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
1641}
1642
Wei Wang35732d02017-10-06 12:05:57 -07001643static int rt6_insert_exception(struct rt6_info *nrt,
David Ahern5012f0a2019-04-16 14:36:05 -07001644 const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001645{
David Ahern5e670d82018-04-17 17:33:14 -07001646 struct net *net = dev_net(nrt->dst.dev);
Wei Wang35732d02017-10-06 12:05:57 -07001647 struct rt6_exception_bucket *bucket;
David Aherncc5c0732019-05-22 20:27:58 -07001648 struct fib6_info *f6i = res->f6i;
Wei Wang35732d02017-10-06 12:05:57 -07001649 struct in6_addr *src_key = NULL;
1650 struct rt6_exception *rt6_ex;
David Aherncc5c0732019-05-22 20:27:58 -07001651 struct fib6_nh *nh = res->nh;
Wei Wang35732d02017-10-06 12:05:57 -07001652 int err = 0;
1653
Wei Wang35732d02017-10-06 12:05:57 -07001654 spin_lock_bh(&rt6_exception_lock);
1655
David Aherncc5c0732019-05-22 20:27:58 -07001656 bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
1657 lockdep_is_held(&rt6_exception_lock));
Wei Wang35732d02017-10-06 12:05:57 -07001658 if (!bucket) {
1659 bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
1660 GFP_ATOMIC);
1661 if (!bucket) {
1662 err = -ENOMEM;
1663 goto out;
1664 }
David Aherncc5c0732019-05-22 20:27:58 -07001665 rcu_assign_pointer(nh->rt6i_exception_bucket, bucket);
1666 } else if (fib6_nh_excptn_bucket_flushed(bucket)) {
1667 err = -EINVAL;
1668 goto out;
Wei Wang35732d02017-10-06 12:05:57 -07001669 }
1670
1671#ifdef CONFIG_IPV6_SUBTREES
David Ahern5012f0a2019-04-16 14:36:05 -07001672 /* fib6_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001673 * and exception table is indexed by a hash of
David Ahern5012f0a2019-04-16 14:36:05 -07001674 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001675 * Otherwise, the exception table is indexed by
David Ahern5012f0a2019-04-16 14:36:05 -07001676 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001677 */
David Ahern5012f0a2019-04-16 14:36:05 -07001678 if (f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001679 src_key = &nrt->rt6i_src.addr;
1680#endif
David Ahern5012f0a2019-04-16 14:36:05 -07001681 /* rt6_mtu_change() might lower mtu on f6i.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001682 * Only insert this exception route if its mtu
David Ahern5012f0a2019-04-16 14:36:05 -07001683 * is less than f6i's mtu value.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001684 */
David Ahernb748f262019-04-16 14:36:06 -07001685 if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001686 err = -EINVAL;
1687 goto out;
1688 }
Wei Wang60006a42017-10-06 12:05:58 -07001689
Wei Wang35732d02017-10-06 12:05:57 -07001690 rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
1691 src_key);
1692 if (rt6_ex)
1693 rt6_remove_exception(bucket, rt6_ex);
1694
1695 rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
1696 if (!rt6_ex) {
1697 err = -ENOMEM;
1698 goto out;
1699 }
1700 rt6_ex->rt6i = nrt;
1701 rt6_ex->stamp = jiffies;
Wei Wang35732d02017-10-06 12:05:57 -07001702 hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
1703 bucket->depth++;
Wei Wang81eb8442017-10-06 12:06:11 -07001704 net->ipv6.rt6_stats->fib_rt_cache++;
Wei Wang35732d02017-10-06 12:05:57 -07001705
1706 if (bucket->depth > FIB6_MAX_DEPTH)
1707 rt6_exception_remove_oldest(bucket);
1708
1709out:
1710 spin_unlock_bh(&rt6_exception_lock);
1711
1712 /* Update fn->fn_sernum to invalidate all cached dst */
Paolo Abenib886d5f2017-10-19 16:07:10 +02001713 if (!err) {
David Ahern5012f0a2019-04-16 14:36:05 -07001714 spin_lock_bh(&f6i->fib6_table->tb6_lock);
1715 fib6_update_sernum(net, f6i);
1716 spin_unlock_bh(&f6i->fib6_table->tb6_lock);
Paolo Abenib886d5f2017-10-19 16:07:10 +02001717 fib6_force_start_gc(net);
1718 }
Wei Wang35732d02017-10-06 12:05:57 -07001719
1720 return err;
1721}
1722
David Ahernc0b220c2019-05-22 20:27:57 -07001723static void fib6_nh_flush_exceptions(struct fib6_nh *nh, struct fib6_info *from)
Wei Wang35732d02017-10-06 12:05:57 -07001724{
1725 struct rt6_exception_bucket *bucket;
1726 struct rt6_exception *rt6_ex;
1727 struct hlist_node *tmp;
1728 int i;
1729
1730 spin_lock_bh(&rt6_exception_lock);
Wei Wang35732d02017-10-06 12:05:57 -07001731
David Aherncc5c0732019-05-22 20:27:58 -07001732 bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
Wei Wang35732d02017-10-06 12:05:57 -07001733 if (!bucket)
1734 goto out;
1735
David Aherncc5c0732019-05-22 20:27:58 -07001736 /* Prevent rt6_insert_exception() to recreate the bucket list */
1737 if (!from)
1738 fib6_nh_excptn_bucket_set_flushed(nh, &rt6_exception_lock);
1739
Wei Wang35732d02017-10-06 12:05:57 -07001740 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
David Aherncc5c0732019-05-22 20:27:58 -07001741 hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) {
1742 if (!from ||
1743 rcu_access_pointer(rt6_ex->rt6i->from) == from)
1744 rt6_remove_exception(bucket, rt6_ex);
1745 }
1746 WARN_ON_ONCE(!from && bucket->depth);
Wei Wang35732d02017-10-06 12:05:57 -07001747 bucket++;
1748 }
Wei Wang35732d02017-10-06 12:05:57 -07001749out:
1750 spin_unlock_bh(&rt6_exception_lock);
1751}
1752
David Aherne659ba32019-06-08 14:53:28 -07001753static int rt6_nh_flush_exceptions(struct fib6_nh *nh, void *arg)
1754{
1755 struct fib6_info *f6i = arg;
1756
1757 fib6_nh_flush_exceptions(nh, f6i);
1758
1759 return 0;
1760}
1761
David Ahernc0b220c2019-05-22 20:27:57 -07001762void rt6_flush_exceptions(struct fib6_info *f6i)
1763{
David Aherne659ba32019-06-08 14:53:28 -07001764 if (f6i->nh)
1765 nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_flush_exceptions,
1766 f6i);
1767 else
1768 fib6_nh_flush_exceptions(f6i->fib6_nh, f6i);
David Ahernc0b220c2019-05-22 20:27:57 -07001769}
1770
Wei Wang35732d02017-10-06 12:05:57 -07001771/* Find cached rt in the hash table inside passed in rt
1772 * Caller has to hold rcu_read_lock()
1773 */
David Ahern7e4b5122019-04-16 14:36:00 -07001774static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang510e2ce2019-05-16 13:30:54 -07001775 const struct in6_addr *daddr,
1776 const struct in6_addr *saddr)
Wei Wang35732d02017-10-06 12:05:57 -07001777{
Wei Wang510e2ce2019-05-16 13:30:54 -07001778 const struct in6_addr *src_key = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001779 struct rt6_exception_bucket *bucket;
Wei Wang35732d02017-10-06 12:05:57 -07001780 struct rt6_exception *rt6_ex;
David Ahern7e4b5122019-04-16 14:36:00 -07001781 struct rt6_info *ret = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001782
Wei Wang35732d02017-10-06 12:05:57 -07001783#ifdef CONFIG_IPV6_SUBTREES
David Ahern7e4b5122019-04-16 14:36:00 -07001784 /* fib6i_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001785 * and exception table is indexed by a hash of
David Ahern7e4b5122019-04-16 14:36:00 -07001786 * both fib6_dst and fib6_src.
Wei Wang510e2ce2019-05-16 13:30:54 -07001787 * However, the src addr used to create the hash
1788 * might not be exactly the passed in saddr which
1789 * is a /128 addr from the flow.
1790 * So we need to use f6i->fib6_src to redo lookup
1791 * if the passed in saddr does not find anything.
1792 * (See the logic in ip6_rt_cache_alloc() on how
1793 * rt->rt6i_src is updated.)
Wei Wang35732d02017-10-06 12:05:57 -07001794 */
David Ahern7e4b5122019-04-16 14:36:00 -07001795 if (res->f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001796 src_key = saddr;
Wei Wang510e2ce2019-05-16 13:30:54 -07001797find_ex:
Wei Wang35732d02017-10-06 12:05:57 -07001798#endif
David Aherncc5c0732019-05-22 20:27:58 -07001799 bucket = fib6_nh_get_excptn_bucket(res->nh, NULL);
Wei Wang35732d02017-10-06 12:05:57 -07001800 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1801
1802 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
David Ahern7e4b5122019-04-16 14:36:00 -07001803 ret = rt6_ex->rt6i;
Wei Wang35732d02017-10-06 12:05:57 -07001804
Wei Wang510e2ce2019-05-16 13:30:54 -07001805#ifdef CONFIG_IPV6_SUBTREES
1806 /* Use fib6_src as src_key and redo lookup */
1807 if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) {
1808 src_key = &res->f6i->fib6_src.addr;
1809 goto find_ex;
1810 }
1811#endif
1812
David Ahern7e4b5122019-04-16 14:36:00 -07001813 return ret;
Wei Wang35732d02017-10-06 12:05:57 -07001814}
1815
1816/* Remove the passed in cached rt from the hash table that contains it */
David Aherncc5c0732019-05-22 20:27:58 -07001817static int fib6_nh_remove_exception(const struct fib6_nh *nh, int plen,
David Ahernc0b220c2019-05-22 20:27:57 -07001818 const struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001819{
David Ahernc0b220c2019-05-22 20:27:57 -07001820 const struct in6_addr *src_key = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001821 struct rt6_exception_bucket *bucket;
Wei Wang35732d02017-10-06 12:05:57 -07001822 struct rt6_exception *rt6_ex;
1823 int err;
1824
David Aherncc5c0732019-05-22 20:27:58 -07001825 if (!rcu_access_pointer(nh->rt6i_exception_bucket))
Wei Wang35732d02017-10-06 12:05:57 -07001826 return -ENOENT;
1827
1828 spin_lock_bh(&rt6_exception_lock);
David Aherncc5c0732019-05-22 20:27:58 -07001829 bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
1830
Wei Wang35732d02017-10-06 12:05:57 -07001831#ifdef CONFIG_IPV6_SUBTREES
David Aherncc5c0732019-05-22 20:27:58 -07001832 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1833 * and exception table is indexed by a hash of
1834 * both rt6i_dst and rt6i_src.
Wei Wang35732d02017-10-06 12:05:57 -07001835 * Otherwise, the exception table is indexed by
1836 * a hash of only rt6i_dst.
1837 */
David Ahernc0b220c2019-05-22 20:27:57 -07001838 if (plen)
Wei Wang35732d02017-10-06 12:05:57 -07001839 src_key = &rt->rt6i_src.addr;
1840#endif
1841 rt6_ex = __rt6_find_exception_spinlock(&bucket,
1842 &rt->rt6i_dst.addr,
1843 src_key);
1844 if (rt6_ex) {
1845 rt6_remove_exception(bucket, rt6_ex);
1846 err = 0;
1847 } else {
1848 err = -ENOENT;
1849 }
1850
1851 spin_unlock_bh(&rt6_exception_lock);
1852 return err;
1853}
1854
David Aherne659ba32019-06-08 14:53:28 -07001855struct fib6_nh_excptn_arg {
1856 struct rt6_info *rt;
1857 int plen;
1858};
1859
1860static int rt6_nh_remove_exception_rt(struct fib6_nh *nh, void *_arg)
1861{
1862 struct fib6_nh_excptn_arg *arg = _arg;
1863 int err;
1864
1865 err = fib6_nh_remove_exception(nh, arg->plen, arg->rt);
1866 if (err == 0)
1867 return 1;
1868
1869 return 0;
1870}
1871
David Ahernc0b220c2019-05-22 20:27:57 -07001872static int rt6_remove_exception_rt(struct rt6_info *rt)
1873{
1874 struct fib6_info *from;
1875
1876 from = rcu_dereference(rt->from);
David Aherncc5c0732019-05-22 20:27:58 -07001877 if (!from || !(rt->rt6i_flags & RTF_CACHE))
David Ahernc0b220c2019-05-22 20:27:57 -07001878 return -EINVAL;
1879
David Aherne659ba32019-06-08 14:53:28 -07001880 if (from->nh) {
1881 struct fib6_nh_excptn_arg arg = {
1882 .rt = rt,
1883 .plen = from->fib6_src.plen
1884 };
1885 int rc;
1886
1887 /* rc = 1 means an entry was found */
1888 rc = nexthop_for_each_fib6_nh(from->nh,
1889 rt6_nh_remove_exception_rt,
1890 &arg);
1891 return rc ? 0 : -ENOENT;
1892 }
1893
David Ahern1cf844c2019-05-22 20:27:59 -07001894 return fib6_nh_remove_exception(from->fib6_nh,
David Aherncc5c0732019-05-22 20:27:58 -07001895 from->fib6_src.plen, rt);
David Ahernc0b220c2019-05-22 20:27:57 -07001896}
1897
Wei Wang35732d02017-10-06 12:05:57 -07001898/* Find rt6_ex which contains the passed in rt cache and
1899 * refresh its stamp
1900 */
David Aherncc5c0732019-05-22 20:27:58 -07001901static void fib6_nh_update_exception(const struct fib6_nh *nh, int plen,
David Ahernc0b220c2019-05-22 20:27:57 -07001902 const struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001903{
David Ahernc0b220c2019-05-22 20:27:57 -07001904 const struct in6_addr *src_key = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001905 struct rt6_exception_bucket *bucket;
Wei Wang35732d02017-10-06 12:05:57 -07001906 struct rt6_exception *rt6_ex;
Paolo Abeni193f3682019-02-21 11:19:41 +01001907
David Aherncc5c0732019-05-22 20:27:58 -07001908 bucket = fib6_nh_get_excptn_bucket(nh, NULL);
Wei Wang35732d02017-10-06 12:05:57 -07001909#ifdef CONFIG_IPV6_SUBTREES
David Aherncc5c0732019-05-22 20:27:58 -07001910 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1911 * and exception table is indexed by a hash of
1912 * both rt6i_dst and rt6i_src.
Wei Wang35732d02017-10-06 12:05:57 -07001913 * Otherwise, the exception table is indexed by
1914 * a hash of only rt6i_dst.
1915 */
David Ahernc0b220c2019-05-22 20:27:57 -07001916 if (plen)
Wei Wang35732d02017-10-06 12:05:57 -07001917 src_key = &rt->rt6i_src.addr;
1918#endif
David Aherncc5c0732019-05-22 20:27:58 -07001919 rt6_ex = __rt6_find_exception_rcu(&bucket, &rt->rt6i_dst.addr, src_key);
Wei Wang35732d02017-10-06 12:05:57 -07001920 if (rt6_ex)
1921 rt6_ex->stamp = jiffies;
David Ahernc0b220c2019-05-22 20:27:57 -07001922}
Wei Wang35732d02017-10-06 12:05:57 -07001923
David Aherne659ba32019-06-08 14:53:28 -07001924struct fib6_nh_match_arg {
1925 const struct net_device *dev;
1926 const struct in6_addr *gw;
1927 struct fib6_nh *match;
1928};
1929
1930/* determine if fib6_nh has given device and gateway */
1931static int fib6_nh_find_match(struct fib6_nh *nh, void *_arg)
1932{
1933 struct fib6_nh_match_arg *arg = _arg;
1934
1935 if (arg->dev != nh->fib_nh_dev ||
1936 (arg->gw && !nh->fib_nh_gw_family) ||
1937 (!arg->gw && nh->fib_nh_gw_family) ||
1938 (arg->gw && !ipv6_addr_equal(arg->gw, &nh->fib_nh_gw6)))
1939 return 0;
1940
1941 arg->match = nh;
1942
1943 /* found a match, break the loop */
1944 return 1;
1945}
1946
David Ahernc0b220c2019-05-22 20:27:57 -07001947static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1948{
1949 struct fib6_info *from;
David Aherne659ba32019-06-08 14:53:28 -07001950 struct fib6_nh *fib6_nh;
David Ahernc0b220c2019-05-22 20:27:57 -07001951
1952 rcu_read_lock();
1953
1954 from = rcu_dereference(rt->from);
1955 if (!from || !(rt->rt6i_flags & RTF_CACHE))
1956 goto unlock;
1957
David Aherne659ba32019-06-08 14:53:28 -07001958 if (from->nh) {
1959 struct fib6_nh_match_arg arg = {
1960 .dev = rt->dst.dev,
1961 .gw = &rt->rt6i_gateway,
1962 };
1963
1964 nexthop_for_each_fib6_nh(from->nh, fib6_nh_find_match, &arg);
1965
1966 if (!arg.match)
David Aherncff6a322019-08-01 14:36:35 -07001967 goto unlock;
David Aherne659ba32019-06-08 14:53:28 -07001968 fib6_nh = arg.match;
1969 } else {
1970 fib6_nh = from->fib6_nh;
1971 }
1972 fib6_nh_update_exception(fib6_nh, from->fib6_src.plen, rt);
Paolo Abeni193f3682019-02-21 11:19:41 +01001973unlock:
Wei Wang35732d02017-10-06 12:05:57 -07001974 rcu_read_unlock();
1975}
1976
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001977static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1978 struct rt6_info *rt, int mtu)
1979{
1980 /* If the new MTU is lower than the route PMTU, this new MTU will be the
1981 * lowest MTU in the path: always allow updating the route PMTU to
1982 * reflect PMTU decreases.
1983 *
1984 * If the new MTU is higher, and the route PMTU is equal to the local
1985 * MTU, this means the old MTU is the lowest in the path, so allow
1986 * updating it: if other nodes now have lower MTUs, PMTU discovery will
1987 * handle this.
1988 */
1989
1990 if (dst_mtu(&rt->dst) >= mtu)
1991 return true;
1992
1993 if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
1994 return true;
1995
1996 return false;
1997}
1998
1999static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
David Aherncc5c0732019-05-22 20:27:58 -07002000 const struct fib6_nh *nh, int mtu)
Wei Wangf5bbe7e2017-10-06 12:05:59 -07002001{
2002 struct rt6_exception_bucket *bucket;
2003 struct rt6_exception *rt6_ex;
2004 int i;
2005
David Aherncc5c0732019-05-22 20:27:58 -07002006 bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01002007 if (!bucket)
2008 return;
2009
2010 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
2011 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
2012 struct rt6_info *entry = rt6_ex->rt6i;
2013
2014 /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
David Ahernd4ead6b2018-04-17 17:33:16 -07002015 * route), the metrics of its rt->from have already
Stefano Brivioe9fa1492018-03-06 11:10:19 +01002016 * been updated.
2017 */
David Ahernd4ead6b2018-04-17 17:33:16 -07002018 if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
Stefano Brivioe9fa1492018-03-06 11:10:19 +01002019 rt6_mtu_change_route_allowed(idev, entry, mtu))
David Ahernd4ead6b2018-04-17 17:33:16 -07002020 dst_metric_set(&entry->dst, RTAX_MTU, mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07002021 }
Stefano Brivioe9fa1492018-03-06 11:10:19 +01002022 bucket++;
Wei Wangf5bbe7e2017-10-06 12:05:59 -07002023 }
2024}
2025
Wei Wangb16cb452017-10-06 12:06:00 -07002026#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE)
2027
David Aherncc5c0732019-05-22 20:27:58 -07002028static void fib6_nh_exceptions_clean_tohost(const struct fib6_nh *nh,
2029 const struct in6_addr *gateway)
Wei Wangb16cb452017-10-06 12:06:00 -07002030{
2031 struct rt6_exception_bucket *bucket;
2032 struct rt6_exception *rt6_ex;
2033 struct hlist_node *tmp;
2034 int i;
2035
David Aherncc5c0732019-05-22 20:27:58 -07002036 if (!rcu_access_pointer(nh->rt6i_exception_bucket))
Wei Wangb16cb452017-10-06 12:06:00 -07002037 return;
2038
2039 spin_lock_bh(&rt6_exception_lock);
David Aherncc5c0732019-05-22 20:27:58 -07002040 bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
Wei Wangb16cb452017-10-06 12:06:00 -07002041 if (bucket) {
2042 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
2043 hlist_for_each_entry_safe(rt6_ex, tmp,
2044 &bucket->chain, hlist) {
2045 struct rt6_info *entry = rt6_ex->rt6i;
2046
2047 if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
2048 RTF_CACHE_GATEWAY &&
2049 ipv6_addr_equal(gateway,
2050 &entry->rt6i_gateway)) {
2051 rt6_remove_exception(bucket, rt6_ex);
2052 }
2053 }
2054 bucket++;
2055 }
2056 }
2057
2058 spin_unlock_bh(&rt6_exception_lock);
2059}
2060
Wei Wangc757faa2017-10-06 12:06:01 -07002061static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
2062 struct rt6_exception *rt6_ex,
2063 struct fib6_gc_args *gc_args,
2064 unsigned long now)
2065{
2066 struct rt6_info *rt = rt6_ex->rt6i;
2067
Paolo Abeni1859bac2017-10-19 16:07:11 +02002068 /* we are pruning and obsoleting aged-out and non gateway exceptions
2069 * even if others have still references to them, so that on next
2070 * dst_check() such references can be dropped.
2071 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
2072 * expired, independently from their aging, as per RFC 8201 section 4
2073 */
Wei Wang31afeb42018-01-26 11:40:17 -08002074 if (!(rt->rt6i_flags & RTF_EXPIRES)) {
2075 if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
2076 RT6_TRACE("aging clone %p\n", rt);
2077 rt6_remove_exception(bucket, rt6_ex);
2078 return;
2079 }
2080 } else if (time_after(jiffies, rt->dst.expires)) {
2081 RT6_TRACE("purging expired route %p\n", rt);
Wei Wangc757faa2017-10-06 12:06:01 -07002082 rt6_remove_exception(bucket, rt6_ex);
2083 return;
Wei Wang31afeb42018-01-26 11:40:17 -08002084 }
2085
2086 if (rt->rt6i_flags & RTF_GATEWAY) {
Wei Wangc757faa2017-10-06 12:06:01 -07002087 struct neighbour *neigh;
Wei Wangc757faa2017-10-06 12:06:01 -07002088
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07002089 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07002090
Xu Jiab7a320c2021-04-01 11:22:23 +08002091 if (!(neigh && (neigh->flags & NTF_ROUTER))) {
Wei Wangc757faa2017-10-06 12:06:01 -07002092 RT6_TRACE("purging route %p via non-router but gateway\n",
2093 rt);
2094 rt6_remove_exception(bucket, rt6_ex);
2095 return;
2096 }
2097 }
Wei Wang31afeb42018-01-26 11:40:17 -08002098
Wei Wangc757faa2017-10-06 12:06:01 -07002099 gc_args->more++;
2100}
2101
David Aherncc5c0732019-05-22 20:27:58 -07002102static void fib6_nh_age_exceptions(const struct fib6_nh *nh,
David Ahernc0b220c2019-05-22 20:27:57 -07002103 struct fib6_gc_args *gc_args,
2104 unsigned long now)
Wei Wangc757faa2017-10-06 12:06:01 -07002105{
2106 struct rt6_exception_bucket *bucket;
2107 struct rt6_exception *rt6_ex;
2108 struct hlist_node *tmp;
2109 int i;
2110
David Aherncc5c0732019-05-22 20:27:58 -07002111 if (!rcu_access_pointer(nh->rt6i_exception_bucket))
Wei Wangc757faa2017-10-06 12:06:01 -07002112 return;
2113
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07002114 rcu_read_lock_bh();
2115 spin_lock(&rt6_exception_lock);
David Aherncc5c0732019-05-22 20:27:58 -07002116 bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock);
Wei Wangc757faa2017-10-06 12:06:01 -07002117 if (bucket) {
2118 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
2119 hlist_for_each_entry_safe(rt6_ex, tmp,
2120 &bucket->chain, hlist) {
2121 rt6_age_examine_exception(bucket, rt6_ex,
2122 gc_args, now);
2123 }
2124 bucket++;
2125 }
2126 }
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07002127 spin_unlock(&rt6_exception_lock);
2128 rcu_read_unlock_bh();
Wei Wangc757faa2017-10-06 12:06:01 -07002129}
2130
David Aherne659ba32019-06-08 14:53:28 -07002131struct fib6_nh_age_excptn_arg {
2132 struct fib6_gc_args *gc_args;
2133 unsigned long now;
2134};
2135
2136static int rt6_nh_age_exceptions(struct fib6_nh *nh, void *_arg)
2137{
2138 struct fib6_nh_age_excptn_arg *arg = _arg;
2139
2140 fib6_nh_age_exceptions(nh, arg->gc_args, arg->now);
2141 return 0;
2142}
2143
David Aherncc5c0732019-05-22 20:27:58 -07002144void rt6_age_exceptions(struct fib6_info *f6i,
David Ahernc0b220c2019-05-22 20:27:57 -07002145 struct fib6_gc_args *gc_args,
2146 unsigned long now)
2147{
David Aherne659ba32019-06-08 14:53:28 -07002148 if (f6i->nh) {
2149 struct fib6_nh_age_excptn_arg arg = {
2150 .gc_args = gc_args,
2151 .now = now
2152 };
2153
2154 nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_age_exceptions,
2155 &arg);
2156 } else {
2157 fib6_nh_age_exceptions(f6i->fib6_nh, gc_args, now);
2158 }
David Ahernc0b220c2019-05-22 20:27:57 -07002159}
2160
David Ahern1d053da2018-05-09 20:34:21 -07002161/* must be called with rcu lock held */
David Aherneffda4d2019-04-16 14:36:10 -07002162int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
2163 struct flowi6 *fl6, struct fib6_result *res, int strict)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164{
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07002165 struct fib6_node *fn, *saved_fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166
David Ahern64547432018-05-09 20:34:19 -07002167 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07002168 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169
David Ahernca254492015-10-12 11:47:10 -07002170 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
2171 oif = 0;
2172
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002173redo_rt6_select:
David Aherneffda4d2019-04-16 14:36:10 -07002174 rt6_select(net, fn, oif, res, strict);
2175 if (res->f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002176 fn = fib6_backtrack(fn, &fl6->saddr);
2177 if (fn)
2178 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07002179 else if (strict & RT6_LOOKUP_F_REACHABLE) {
2180 /* also consider unreachable route */
2181 strict &= ~RT6_LOOKUP_F_REACHABLE;
2182 fn = saved_fn;
2183 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07002184 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002185 }
2186
David Aherneffda4d2019-04-16 14:36:10 -07002187 trace_fib6_table_lookup(net, res, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08002188
David Aherneffda4d2019-04-16 14:36:10 -07002189 return 0;
David Ahern1d053da2018-05-09 20:34:21 -07002190}
2191
2192struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
2193 int oif, struct flowi6 *fl6,
2194 const struct sk_buff *skb, int flags)
2195{
David Ahernb1d40992019-04-16 14:35:59 -07002196 struct fib6_result res = {};
Wei Wang0e09edc2019-06-20 17:36:37 -07002197 struct rt6_info *rt = NULL;
David Ahern1d053da2018-05-09 20:34:21 -07002198 int strict = 0;
2199
Wei Wang0e09edc2019-06-20 17:36:37 -07002200 WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) &&
2201 !rcu_read_lock_held());
2202
David Ahern1d053da2018-05-09 20:34:21 -07002203 strict |= flags & RT6_LOOKUP_F_IFACE;
2204 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
2205 if (net->ipv6.devconf_all->forwarding == 0)
2206 strict |= RT6_LOOKUP_F_REACHABLE;
2207
2208 rcu_read_lock();
2209
David Aherneffda4d2019-04-16 14:36:10 -07002210 fib6_table_lookup(net, table, oif, fl6, &res, strict);
Wei Wang0e09edc2019-06-20 17:36:37 -07002211 if (res.f6i == net->ipv6.fib6_null_entry)
2212 goto out;
David Ahern23fb93a2018-04-17 17:33:23 -07002213
David Ahernb1d40992019-04-16 14:35:59 -07002214 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07002215
David Ahern23fb93a2018-04-17 17:33:23 -07002216 /*Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07002217 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07002218 if (rt) {
Wei Wang0e09edc2019-06-20 17:36:37 -07002219 goto out;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002220 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07002221 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002222 /* Create a RTF_CACHE clone which will not be
2223 * owned by the fib6 tree. It is for the special case where
2224 * the daddr in the skb during the neighbor look-up is different
2225 * from the fl6->daddr used to look-up route here.
2226 */
Wei Wang0e09edc2019-06-20 17:36:37 -07002227 rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002228
Wei Wang0e09edc2019-06-20 17:36:37 -07002229 if (rt) {
2230 /* 1 refcnt is taken during ip6_rt_cache_alloc().
2231 * As rt6_uncached_list_add() does not consume refcnt,
2232 * this refcnt is always returned to the caller even
2233 * if caller sets RT6_LOOKUP_F_DST_NOREF flag.
Wei Wang1cfb71e2017-06-17 10:42:33 -07002234 */
Wei Wang0e09edc2019-06-20 17:36:37 -07002235 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002236 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang0e09edc2019-06-20 17:36:37 -07002237 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08002238
Wei Wang0e09edc2019-06-20 17:36:37 -07002239 return rt;
2240 }
Martin KaFai Laud52d3992015-05-22 20:56:06 -07002241 } else {
2242 /* Get a percpu copy */
Eric Dumazet951f7882017-10-08 21:07:18 -07002243 local_bh_disable();
Wei Wang0e09edc2019-06-20 17:36:37 -07002244 rt = rt6_get_pcpu_route(&res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07002245
Wei Wang0e09edc2019-06-20 17:36:37 -07002246 if (!rt)
2247 rt = rt6_make_pcpu_route(net, &res);
David Ahern93531c62018-04-17 17:33:25 -07002248
Eric Dumazet951f7882017-10-08 21:07:18 -07002249 local_bh_enable();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07002250 }
Wei Wang0e09edc2019-06-20 17:36:37 -07002251out:
2252 if (!rt)
2253 rt = net->ipv6.ip6_null_entry;
2254 if (!(flags & RT6_LOOKUP_F_DST_NOREF))
2255 ip6_hold_safe(net, &rt);
2256 rcu_read_unlock();
2257
2258 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07002259}
David Ahern9ff74382016-06-13 13:44:19 -07002260EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07002261
Brian Vazquez55cced42020-06-23 09:42:32 -07002262INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_input(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002263 struct fib6_table *table,
2264 struct flowi6 *fl6,
2265 const struct sk_buff *skb,
2266 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07002267{
David Ahernb75cc8f2018-03-02 08:32:17 -08002268 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07002269}
2270
Mahesh Bandeward409b842016-09-16 12:59:08 -07002271struct dst_entry *ip6_route_input_lookup(struct net *net,
2272 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08002273 struct flowi6 *fl6,
2274 const struct sk_buff *skb,
2275 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002276{
2277 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
2278 flags |= RT6_LOOKUP_F_IFACE;
2279
David Ahernb75cc8f2018-03-02 08:32:17 -08002280 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002281}
Mahesh Bandeward409b842016-09-16 12:59:08 -07002282EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00002283
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002284static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002285 struct flow_keys *keys,
2286 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002287{
2288 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
2289 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002290 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002291 const struct ipv6hdr *inner_iph;
2292 const struct icmp6hdr *icmph;
2293 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07002294 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002295
2296 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
2297 goto out;
2298
Eric Dumazetcea67a22018-04-29 09:54:59 -07002299 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
2300 sizeof(_icmph), &_icmph);
2301 if (!icmph)
2302 goto out;
2303
Matteo Croce54074f12019-11-02 01:12:04 +01002304 if (!icmpv6_is_err(icmph->icmp6_type))
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002305 goto out;
2306
2307 inner_iph = skb_header_pointer(skb,
2308 skb_transport_offset(skb) + sizeof(*icmph),
2309 sizeof(_inner_iph), &_inner_iph);
2310 if (!inner_iph)
2311 goto out;
2312
2313 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002314 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002315out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002316 if (_flkeys) {
2317 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
2318 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
2319 keys->tags.flow_label = _flkeys->tags.flow_label;
2320 keys->basic.ip_proto = _flkeys->basic.ip_proto;
2321 } else {
2322 keys->addrs.v6addrs.src = key_iph->saddr;
2323 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002324 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002325 keys->basic.ip_proto = key_iph->nexthdr;
2326 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002327}
2328
2329/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08002330u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
2331 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002332{
2333 struct flow_keys hash_keys;
Ido Schimmelb95b6e02021-05-17 21:15:21 +03002334 u32 mhash = 0;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002335
David S. Millerbbfa0472018-03-12 11:09:33 -04002336 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002337 case 0:
2338 memset(&hash_keys, 0, sizeof(hash_keys));
2339 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2340 if (skb) {
2341 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2342 } else {
2343 hash_keys.addrs.v6addrs.src = fl6->saddr;
2344 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002345 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002346 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2347 }
Ido Schimmelb95b6e02021-05-17 21:15:21 +03002348 mhash = flow_hash_from_keys(&hash_keys);
David Ahernb4bac172018-03-02 08:32:18 -08002349 break;
2350 case 1:
2351 if (skb) {
2352 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2353 struct flow_keys keys;
2354
2355 /* short-circuit if we already have L4 hash present */
2356 if (skb->l4_hash)
2357 return skb_get_hash_raw(skb) >> 1;
2358
2359 memset(&hash_keys, 0, sizeof(hash_keys));
2360
Shubhankar Kuranagatti13fdb942021-03-11 02:03:14 +05302361 if (!flkeys) {
David Ahernb4bac172018-03-02 08:32:18 -08002362 skb_flow_dissect_flow_keys(skb, &keys, flag);
2363 flkeys = &keys;
2364 }
2365 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2366 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2367 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2368 hash_keys.ports.src = flkeys->ports.src;
2369 hash_keys.ports.dst = flkeys->ports.dst;
2370 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2371 } else {
2372 memset(&hash_keys, 0, sizeof(hash_keys));
2373 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2374 hash_keys.addrs.v6addrs.src = fl6->saddr;
2375 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2376 hash_keys.ports.src = fl6->fl6_sport;
2377 hash_keys.ports.dst = fl6->fl6_dport;
2378 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2379 }
Ido Schimmelb95b6e02021-05-17 21:15:21 +03002380 mhash = flow_hash_from_keys(&hash_keys);
David Ahernb4bac172018-03-02 08:32:18 -08002381 break;
Stephen Suryaputrad8f74f02019-07-06 10:55:18 -04002382 case 2:
2383 memset(&hash_keys, 0, sizeof(hash_keys));
2384 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2385 if (skb) {
2386 struct flow_keys keys;
2387
2388 if (!flkeys) {
2389 skb_flow_dissect_flow_keys(skb, &keys, 0);
2390 flkeys = &keys;
2391 }
2392
2393 /* Inner can be v4 or v6 */
2394 if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
2395 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
2396 hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;
2397 hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;
2398 } else if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
2399 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2400 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2401 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2402 hash_keys.tags.flow_label = flkeys->tags.flow_label;
2403 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2404 } else {
2405 /* Same as case 0 */
2406 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2407 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2408 }
2409 } else {
2410 /* Same as case 0 */
2411 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2412 hash_keys.addrs.v6addrs.src = fl6->saddr;
2413 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2414 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
2415 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2416 }
Ido Schimmelb95b6e02021-05-17 21:15:21 +03002417 mhash = flow_hash_from_keys(&hash_keys);
Stephen Suryaputrad8f74f02019-07-06 10:55:18 -04002418 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002419 }
2420
David Ahern9a2a5372018-03-02 08:32:15 -08002421 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002422}
2423
Wei Wang67f415d2019-06-20 17:36:40 -07002424/* Called with rcu held */
Thomas Grafc71099a2006-08-04 23:20:06 -07002425void ip6_route_input(struct sk_buff *skb)
2426{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002427 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002428 struct net *net = dev_net(skb->dev);
Wei Wang67f415d2019-06-20 17:36:40 -07002429 int flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_DST_NOREF;
Jiri Benc904af042015-08-20 13:56:31 +02002430 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002431 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002432 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002433 .daddr = iph->daddr,
2434 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002435 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002436 .flowi6_mark = skb->mark,
2437 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002438 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002439 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002440
Jiri Benc904af042015-08-20 13:56:31 +02002441 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002442 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002443 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002444
2445 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2446 flkeys = &_flkeys;
2447
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002448 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002449 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002450 skb_dst_drop(skb);
Wei Wang67f415d2019-06-20 17:36:40 -07002451 skb_dst_set_noref(skb, ip6_route_input_lookup(net, skb->dev,
2452 &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002453}
2454
Brian Vazquez55cced42020-06-23 09:42:32 -07002455INDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_output(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002456 struct fib6_table *table,
2457 struct flowi6 *fl6,
2458 const struct sk_buff *skb,
2459 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002460{
David Ahernb75cc8f2018-03-02 08:32:17 -08002461 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002462}
2463
Wei Wang7d9e5f42019-06-20 17:36:41 -07002464struct dst_entry *ip6_route_output_flags_noref(struct net *net,
2465 const struct sock *sk,
2466 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002467{
David Ahernd46a9d62015-10-21 08:42:22 -07002468 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002469
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002470 if (ipv6_addr_type(&fl6->daddr) &
2471 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002472 struct dst_entry *dst;
2473
Wei Wang7d9e5f42019-06-20 17:36:41 -07002474 /* This function does not take refcnt on the dst */
David Ahern4c1feac2016-09-10 12:09:56 -07002475 dst = l3mdev_link_scope_lookup(net, fl6);
2476 if (dst)
2477 return dst;
2478 }
David Ahernca254492015-10-12 11:47:10 -07002479
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002480 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002481
Wei Wang7d9e5f42019-06-20 17:36:41 -07002482 flags |= RT6_LOOKUP_F_DST_NOREF;
David Ahernd46a9d62015-10-21 08:42:22 -07002483 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002484 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002485 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002486 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002487
David Ahernd46a9d62015-10-21 08:42:22 -07002488 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002489 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002490 else if (sk)
2491 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002492
David Ahernb75cc8f2018-03-02 08:32:17 -08002493 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494}
Wei Wang7d9e5f42019-06-20 17:36:41 -07002495EXPORT_SYMBOL_GPL(ip6_route_output_flags_noref);
2496
2497struct dst_entry *ip6_route_output_flags(struct net *net,
2498 const struct sock *sk,
2499 struct flowi6 *fl6,
2500 int flags)
2501{
Shubhankar Kuranagatti13fdb942021-03-11 02:03:14 +05302502 struct dst_entry *dst;
2503 struct rt6_info *rt6;
Wei Wang7d9e5f42019-06-20 17:36:41 -07002504
Shubhankar Kuranagatti13fdb942021-03-11 02:03:14 +05302505 rcu_read_lock();
2506 dst = ip6_route_output_flags_noref(net, sk, fl6, flags);
2507 rt6 = (struct rt6_info *)dst;
2508 /* For dst cached in uncached_list, refcnt is already taken. */
2509 if (list_empty(&rt6->rt6i_uncached) && !dst_hold_safe(dst)) {
2510 dst = &net->ipv6.ip6_null_entry->dst;
2511 dst_hold(dst);
2512 }
2513 rcu_read_unlock();
Wei Wang7d9e5f42019-06-20 17:36:41 -07002514
Shubhankar Kuranagatti13fdb942021-03-11 02:03:14 +05302515 return dst;
Wei Wang7d9e5f42019-06-20 17:36:41 -07002516}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002517EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518
David S. Miller2774c132011-03-01 14:59:04 -08002519struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002520{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002521 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002522 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002523 struct dst_entry *new = NULL;
2524
Wei Wang1dbe32522017-06-17 10:42:26 -07002525 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002526 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002527 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002528 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002529 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002530
Changli Gaod8d1f302010-06-10 23:31:35 -07002531 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002532 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002533 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002534 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002535
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002536 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002537
Wei Wang1dbe32522017-06-17 10:42:26 -07002538 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002539 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002540 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002541
2542 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2543#ifdef CONFIG_IPV6_SUBTREES
2544 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2545#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002546 }
2547
David S. Miller69ead7a2011-03-01 14:45:33 -08002548 dst_release(dst_orig);
2549 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002550}
David S. Miller14e50e52007-05-24 18:17:54 -07002551
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552/*
2553 * Destination cache support functions
2554 */
2555
David Ahern8d1c8022018-04-17 17:33:26 -07002556static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002557{
Steffen Klassert36143642017-08-25 09:05:42 +02002558 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002559
David Ahern8ae86972018-04-20 15:38:03 -07002560 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002561 return false;
2562
2563 if (fib6_check_expired(f6i))
2564 return false;
2565
2566 return true;
2567}
2568
David Aherna68886a2018-04-20 15:38:02 -07002569static struct dst_entry *rt6_check(struct rt6_info *rt,
2570 struct fib6_info *from,
2571 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002572{
Wei Wangc5cff852017-08-21 09:47:10 -07002573 u32 rt_cookie = 0;
2574
David Ahern49d05fe2019-07-17 15:08:43 -07002575 if (!from || !fib6_get_cookie_safe(from, &rt_cookie) ||
David Ahern93531c62018-04-17 17:33:25 -07002576 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002577 return NULL;
2578
2579 if (rt6_check_expired(rt))
2580 return NULL;
2581
2582 return &rt->dst;
2583}
2584
David Aherna68886a2018-04-20 15:38:02 -07002585static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2586 struct fib6_info *from,
2587 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002588{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002589 if (!__rt6_check_expired(rt) &&
2590 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002591 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002592 return &rt->dst;
2593 else
2594 return NULL;
2595}
2596
Brian Vazquezbbd807d2021-02-01 17:41:32 +00002597INDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst,
2598 u32 cookie)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599{
David Aherna87b7dc2018-04-20 15:38:00 -07002600 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002601 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 struct rt6_info *rt;
2603
David Aherna87b7dc2018-04-20 15:38:00 -07002604 rt = container_of(dst, struct rt6_info, dst);
2605
David Ahern8f34e532020-05-01 08:53:08 -06002606 if (rt->sernum)
2607 return rt6_is_valid(rt) ? dst : NULL;
2608
David Aherna87b7dc2018-04-20 15:38:00 -07002609 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002611 /* All IPV6 dsts are created with ->obsolete set to the value
2612 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2613 * into this function always.
2614 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002615
David Aherna68886a2018-04-20 15:38:02 -07002616 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002617
David Aherna68886a2018-04-20 15:38:02 -07002618 if (from && (rt->rt6i_flags & RTF_PCPU ||
2619 unlikely(!list_empty(&rt->rt6i_uncached))))
2620 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002621 else
David Aherna68886a2018-04-20 15:38:02 -07002622 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002623
2624 rcu_read_unlock();
2625
2626 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627}
Brian Vazquez9c979212021-02-04 18:18:39 +00002628EXPORT_INDIRECT_CALLABLE(ip6_dst_check);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629
2630static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2631{
2632 struct rt6_info *rt = (struct rt6_info *) dst;
2633
2634 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002635 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002636 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002637 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002638 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002639 dst = NULL;
2640 }
David Ahernc3c14da2018-04-23 11:32:06 -07002641 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002642 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002644 dst = NULL;
2645 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002647 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648}
2649
2650static void ip6_link_failure(struct sk_buff *skb)
2651{
2652 struct rt6_info *rt;
2653
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002654 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655
Eric Dumazetadf30902009-06-02 05:19:30 +00002656 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002658 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002659 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002660 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002661 } else {
David Aherna68886a2018-04-20 15:38:02 -07002662 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002663 struct fib6_node *fn;
2664
David Aherna68886a2018-04-20 15:38:02 -07002665 from = rcu_dereference(rt->from);
2666 if (from) {
2667 fn = rcu_dereference(from->fib6_node);
2668 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2669 fn->fn_sernum = -1;
2670 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002671 }
David Ahern8a14e462018-04-23 11:32:07 -07002672 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 }
2674}
2675
David Ahern6a3e0302018-04-20 15:37:57 -07002676static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2677{
David Aherna68886a2018-04-20 15:38:02 -07002678 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2679 struct fib6_info *from;
2680
2681 rcu_read_lock();
2682 from = rcu_dereference(rt0->from);
2683 if (from)
2684 rt0->dst.expires = from->expires;
2685 rcu_read_unlock();
2686 }
David Ahern6a3e0302018-04-20 15:37:57 -07002687
2688 dst_set_expires(&rt0->dst, timeout);
2689 rt0->rt6i_flags |= RTF_EXPIRES;
2690}
2691
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002692static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2693{
2694 struct net *net = dev_net(rt->dst.dev);
2695
David Ahernd4ead6b2018-04-17 17:33:16 -07002696 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002697 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002698 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2699}
2700
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002701static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2702{
2703 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002704 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002705}
2706
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002707static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
Hangbin Liubd085ef2019-12-22 10:51:09 +08002708 const struct ipv6hdr *iph, u32 mtu,
2709 bool confirm_neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002711 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002712 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713
Maciej Żenczykowski09454fd2020-05-05 11:57:23 -07002714 /* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU)
2715 * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it.
2716 * [see also comment in rt6_mtu_change_route()]
2717 */
Xin Long19bda362016-10-28 18:18:01 +08002718
Julian Anastasov0dec8792017-02-06 23:14:16 +02002719 if (iph) {
2720 daddr = &iph->daddr;
2721 saddr = &iph->saddr;
2722 } else if (sk) {
2723 daddr = &sk->sk_v6_daddr;
2724 saddr = &inet6_sk(sk)->saddr;
2725 } else {
2726 daddr = NULL;
2727 saddr = NULL;
2728 }
Hangbin Liubd085ef2019-12-22 10:51:09 +08002729
2730 if (confirm_neigh)
2731 dst_confirm_neigh(dst, daddr);
2732
Georg Kohmann4a65dff2020-10-07 14:53:02 +02002733 if (mtu < IPV6_MIN_MTU)
2734 return;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002735 if (mtu >= dst_mtu(dst))
2736 return;
David S. Miller81aded22012-06-15 14:54:11 -07002737
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002738 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002739 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002740 /* update rt6_ex->stamp for cache */
2741 if (rt6->rt6i_flags & RTF_CACHE)
2742 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002743 } else if (daddr) {
David Ahern85bd05d2019-04-16 14:36:01 -07002744 struct fib6_result res = {};
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002745 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002746
David Ahern4d85cd02018-04-20 15:37:59 -07002747 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07002748 res.f6i = rcu_dereference(rt6->from);
David Ahern43a4b602019-08-01 15:18:08 -07002749 if (!res.f6i)
2750 goto out_unlock;
2751
David Ahern7d21fec2019-04-16 14:36:11 -07002752 res.fib6_flags = res.f6i->fib6_flags;
2753 res.fib6_type = res.f6i->fib6_type;
2754
David Ahern2d442342019-06-08 14:53:31 -07002755 if (res.f6i->nh) {
2756 struct fib6_nh_match_arg arg = {
2757 .dev = dst->dev,
2758 .gw = &rt6->rt6i_gateway,
2759 };
2760
2761 nexthop_for_each_fib6_nh(res.f6i->nh,
2762 fib6_nh_find_match, &arg);
2763
2764 /* fib6_info uses a nexthop that does not have fib6_nh
2765 * using the dst->dev + gw. Should be impossible.
2766 */
David Ahern43a4b602019-08-01 15:18:08 -07002767 if (!arg.match)
2768 goto out_unlock;
David Ahern2d442342019-06-08 14:53:31 -07002769
2770 res.nh = arg.match;
2771 } else {
2772 res.nh = res.f6i->fib6_nh;
2773 }
2774
David Ahern85bd05d2019-04-16 14:36:01 -07002775 nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002776 if (nrt6) {
2777 rt6_do_update_pmtu(nrt6, mtu);
David Ahern5012f0a2019-04-16 14:36:05 -07002778 if (rt6_insert_exception(nrt6, &res))
Wei Wang2b760fc2017-10-06 12:06:03 -07002779 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002780 }
David Ahern43a4b602019-08-01 15:18:08 -07002781out_unlock:
David Aherna68886a2018-04-20 15:38:02 -07002782 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 }
2784}
2785
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002786static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
Hangbin Liubd085ef2019-12-22 10:51:09 +08002787 struct sk_buff *skb, u32 mtu,
2788 bool confirm_neigh)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002789{
Hangbin Liubd085ef2019-12-22 10:51:09 +08002790 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu,
2791 confirm_neigh);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002792}
2793
David S. Miller42ae66c2012-06-15 20:01:57 -07002794void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002795 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002796{
2797 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2798 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002799 struct flowi6 fl6 = {
2800 .flowi6_oif = oif,
2801 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2802 .daddr = iph->daddr,
2803 .saddr = iph->saddr,
2804 .flowlabel = ip6_flowinfo(iph),
2805 .flowi6_uid = uid,
2806 };
David S. Miller81aded22012-06-15 14:54:11 -07002807
2808 dst = ip6_route_output(net, NULL, &fl6);
2809 if (!dst->error)
Hangbin Liubd085ef2019-12-22 10:51:09 +08002810 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true);
David S. Miller81aded22012-06-15 14:54:11 -07002811 dst_release(dst);
2812}
2813EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2814
2815void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2816{
David Ahern7ddacfa2018-11-18 10:45:30 -08002817 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002818 struct dst_entry *dst;
2819
David Ahern7ddacfa2018-11-18 10:45:30 -08002820 if (!oif && skb->dev)
2821 oif = l3mdev_master_ifindex(skb->dev);
2822
2823 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002824
2825 dst = __sk_dst_get(sk);
2826 if (!dst || !dst->obsolete ||
2827 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2828 return;
2829
2830 bh_lock_sock(sk);
2831 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2832 ip6_datagram_dst_update(sk, false);
2833 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002834}
2835EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2836
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002837void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2838 const struct flowi6 *fl6)
2839{
2840#ifdef CONFIG_IPV6_SUBTREES
2841 struct ipv6_pinfo *np = inet6_sk(sk);
2842#endif
2843
2844 ip6_dst_store(sk, dst,
2845 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2846 &sk->sk_v6_daddr : NULL,
2847#ifdef CONFIG_IPV6_SUBTREES
2848 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2849 &np->saddr :
2850#endif
2851 NULL);
2852}
2853
David Ahern9b6b35a2019-04-16 14:36:02 -07002854static bool ip6_redirect_nh_match(const struct fib6_result *res,
David Ahern0b34eb02019-04-09 14:41:19 -07002855 struct flowi6 *fl6,
2856 const struct in6_addr *gw,
2857 struct rt6_info **ret)
2858{
David Ahern9b6b35a2019-04-16 14:36:02 -07002859 const struct fib6_nh *nh = res->nh;
2860
David Ahern0b34eb02019-04-09 14:41:19 -07002861 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2862 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2863 return false;
2864
2865 /* rt_cache's gateway might be different from its 'parent'
2866 * in the case of an ip redirect.
2867 * So we keep searching in the exception table if the gateway
2868 * is different.
2869 */
2870 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
2871 struct rt6_info *rt_cache;
2872
David Ahern9b6b35a2019-04-16 14:36:02 -07002873 rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
David Ahern0b34eb02019-04-09 14:41:19 -07002874 if (rt_cache &&
2875 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2876 *ret = rt_cache;
2877 return true;
2878 }
2879 return false;
2880 }
2881 return true;
2882}
2883
David Ahernc55c8982019-06-08 14:53:29 -07002884struct fib6_nh_rd_arg {
2885 struct fib6_result *res;
2886 struct flowi6 *fl6;
2887 const struct in6_addr *gw;
2888 struct rt6_info **ret;
2889};
2890
2891static int fib6_nh_redirect_match(struct fib6_nh *nh, void *_arg)
2892{
2893 struct fib6_nh_rd_arg *arg = _arg;
2894
2895 arg->res->nh = nh;
2896 return ip6_redirect_nh_match(arg->res, arg->fl6, arg->gw, arg->ret);
2897}
2898
Duan Jiongb55b76b2013-09-04 19:44:21 +08002899/* Handle redirects */
2900struct ip6rd_flowi {
2901 struct flowi6 fl6;
2902 struct in6_addr gateway;
2903};
2904
Brian Vazquez55cced42020-06-23 09:42:32 -07002905INDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002906 struct fib6_table *table,
2907 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002908 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002909 int flags)
2910{
2911 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002912 struct rt6_info *ret = NULL;
David Ahern9b6b35a2019-04-16 14:36:02 -07002913 struct fib6_result res = {};
David Ahernc55c8982019-06-08 14:53:29 -07002914 struct fib6_nh_rd_arg arg = {
2915 .res = &res,
2916 .fl6 = fl6,
2917 .gw = &rdfl->gateway,
2918 .ret = &ret
2919 };
David Ahern8d1c8022018-04-17 17:33:26 -07002920 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002921 struct fib6_node *fn;
2922
David Ahern31680ac2019-05-22 15:12:18 -07002923 /* l3mdev_update_flow overrides oif if the device is enslaved; in
2924 * this case we must match on the real ingress device, so reset it
2925 */
2926 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
2927 fl6->flowi6_oif = skb->dev->ifindex;
2928
Duan Jiongb55b76b2013-09-04 19:44:21 +08002929 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002930 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002931 *
2932 * RFC 4861 specifies that redirects should only be
2933 * accepted if they come from the nexthop to the target.
2934 * Due to the way the routes are chosen, this notion
2935 * is a bit fuzzy and one might need to check all possible
2936 * routes.
2937 */
2938
Wei Wang66f5d6c2017-10-06 12:06:10 -07002939 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002940 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002941restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002942 for_each_fib6_node_rt_rcu(fn) {
David Ahern9b6b35a2019-04-16 14:36:02 -07002943 res.f6i = rt;
David Ahern14895682018-04-17 17:33:17 -07002944 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002945 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002946 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002947 break;
David Ahernc55c8982019-06-08 14:53:29 -07002948 if (unlikely(rt->nh)) {
2949 if (nexthop_is_blackhole(rt->nh))
2950 continue;
2951 /* on match, res->nh is filled in and potentially ret */
2952 if (nexthop_for_each_fib6_nh(rt->nh,
2953 fib6_nh_redirect_match,
2954 &arg))
2955 goto out;
2956 } else {
2957 res.nh = rt->fib6_nh;
2958 if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway,
2959 &ret))
2960 goto out;
2961 }
Duan Jiongb55b76b2013-09-04 19:44:21 +08002962 }
2963
2964 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002965 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002966 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002967 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002968 goto out;
2969 }
2970
David Ahern421842e2018-04-17 17:33:18 -07002971 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002972 fn = fib6_backtrack(fn, &fl6->saddr);
2973 if (fn)
2974 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002975 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002976
David Ahern9b6b35a2019-04-16 14:36:02 -07002977 res.f6i = rt;
David Ahern1cf844c2019-05-22 20:27:59 -07002978 res.nh = rt->fib6_nh;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002979out:
David Ahern7d21fec2019-04-16 14:36:11 -07002980 if (ret) {
David Ahern10585b42019-03-20 09:24:50 -07002981 ip6_hold_safe(net, &ret);
David Ahern7d21fec2019-04-16 14:36:11 -07002982 } else {
2983 res.fib6_flags = res.f6i->fib6_flags;
2984 res.fib6_type = res.f6i->fib6_type;
David Ahern9b6b35a2019-04-16 14:36:02 -07002985 ret = ip6_create_rt_rcu(&res);
David Ahern7d21fec2019-04-16 14:36:11 -07002986 }
Duan Jiongb55b76b2013-09-04 19:44:21 +08002987
Wei Wang66f5d6c2017-10-06 12:06:10 -07002988 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002989
David Ahern8ff2e5b2019-04-16 14:36:09 -07002990 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002991 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002992};
2993
2994static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002995 const struct flowi6 *fl6,
2996 const struct sk_buff *skb,
2997 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002998{
2999 int flags = RT6_LOOKUP_F_HAS_SADDR;
3000 struct ip6rd_flowi rdfl;
3001
3002 rdfl.fl6 = *fl6;
3003 rdfl.gateway = *gateway;
3004
David Ahernb75cc8f2018-03-02 08:32:17 -08003005 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08003006 flags, __ip6_route_redirect);
3007}
3008
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09003009void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
3010 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07003011{
3012 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
3013 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07003014 struct flowi6 fl6 = {
3015 .flowi6_iif = LOOPBACK_IFINDEX,
3016 .flowi6_oif = oif,
3017 .flowi6_mark = mark,
3018 .daddr = iph->daddr,
3019 .saddr = iph->saddr,
3020 .flowlabel = ip6_flowinfo(iph),
3021 .flowi6_uid = uid,
3022 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07003023
David Ahernb75cc8f2018-03-02 08:32:17 -08003024 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08003025 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07003026 dst_release(dst);
3027}
3028EXPORT_SYMBOL_GPL(ip6_redirect);
3029
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07003030void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08003031{
3032 const struct ipv6hdr *iph = ipv6_hdr(skb);
3033 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
3034 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07003035 struct flowi6 fl6 = {
3036 .flowi6_iif = LOOPBACK_IFINDEX,
3037 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07003038 .daddr = msg->dest,
3039 .saddr = iph->daddr,
3040 .flowi6_uid = sock_net_uid(net, NULL),
3041 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08003042
David Ahernb75cc8f2018-03-02 08:32:17 -08003043 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08003044 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08003045 dst_release(dst);
3046}
3047
David S. Miller3a5ad2e2012-07-12 00:08:07 -07003048void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
3049{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09003050 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
3051 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07003052}
3053EXPORT_SYMBOL_GPL(ip6_sk_redirect);
3054
David S. Miller0dbaee32010-12-13 12:52:14 -08003055static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003056{
David S. Miller0dbaee32010-12-13 12:52:14 -08003057 struct net_device *dev = dst->dev;
3058 unsigned int mtu = dst_mtu(dst);
3059 struct net *net = dev_net(dev);
3060
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
3062
Daniel Lezcano55786892008-03-04 13:47:47 -08003063 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
3064 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003065
3066 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003067 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
3068 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
3069 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003070 * rely only on pmtu discovery"
3071 */
3072 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
3073 mtu = IPV6_MAXPLEN;
3074 return mtu;
3075}
3076
Brian Vazquezf67fbea2021-02-01 17:41:31 +00003077INDIRECT_CALLABLE_SCOPE unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08003078{
David S. Millerd33e4552010-12-14 13:01:14 -08003079 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07003080 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00003081
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07003082 mtu = dst_metric_raw(dst, RTAX_MTU);
3083 if (mtu)
3084 goto out;
3085
Steffen Klassert618f9bc2011-11-23 02:13:31 +00003086 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08003087
3088 rcu_read_lock();
3089 idev = __in6_dev_get(dst->dev);
3090 if (idev)
3091 mtu = idev->cnf.mtu6;
3092 rcu_read_unlock();
3093
Eric Dumazet30f78d82014-04-10 21:23:36 -07003094out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07003095 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
3096
3097 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08003098}
Brian Vazquez9c979212021-02-04 18:18:39 +00003099EXPORT_INDIRECT_CALLABLE(ip6_mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08003100
David Ahern901731b2018-05-21 09:08:14 -07003101/* MTU selection:
3102 * 1. mtu on route is locked - use it
3103 * 2. mtu from nexthop exception
3104 * 3. mtu from egress device
3105 *
3106 * based on ip6_dst_mtu_forward and exception logic of
3107 * rt6_find_cached_rt; called with rcu_read_lock
3108 */
David Ahernb748f262019-04-16 14:36:06 -07003109u32 ip6_mtu_from_fib6(const struct fib6_result *res,
3110 const struct in6_addr *daddr,
3111 const struct in6_addr *saddr)
David Ahern901731b2018-05-21 09:08:14 -07003112{
David Ahernb748f262019-04-16 14:36:06 -07003113 const struct fib6_nh *nh = res->nh;
3114 struct fib6_info *f6i = res->f6i;
David Ahern901731b2018-05-21 09:08:14 -07003115 struct inet6_dev *idev;
Wei Wang510e2ce2019-05-16 13:30:54 -07003116 struct rt6_info *rt;
David Ahern901731b2018-05-21 09:08:14 -07003117 u32 mtu = 0;
3118
3119 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
3120 mtu = f6i->fib6_pmtu;
3121 if (mtu)
3122 goto out;
3123 }
3124
Wei Wang510e2ce2019-05-16 13:30:54 -07003125 rt = rt6_find_cached_rt(res, daddr, saddr);
3126 if (unlikely(rt)) {
3127 mtu = dst_metric_raw(&rt->dst, RTAX_MTU);
3128 } else {
David Ahernb748f262019-04-16 14:36:06 -07003129 struct net_device *dev = nh->fib_nh_dev;
David Ahern901731b2018-05-21 09:08:14 -07003130
3131 mtu = IPV6_MIN_MTU;
3132 idev = __in6_dev_get(dev);
3133 if (idev && idev->cnf.mtu6 > mtu)
3134 mtu = idev->cnf.mtu6;
3135 }
3136
3137 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
3138out:
David Ahernb748f262019-04-16 14:36:06 -07003139 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahern901731b2018-05-21 09:08:14 -07003140}
3141
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08003142struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05003143 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144{
David S. Miller87a11572011-12-06 17:04:13 -05003145 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146 struct rt6_info *rt;
3147 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09003148 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149
David S. Miller38308472011-12-03 18:02:47 -05003150 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00003151 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152
Martin KaFai Lauad706862015-08-14 11:05:52 -07003153 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05003154 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003155 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05003156 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157 goto out;
3158 }
3159
Brendan McGrath588753f2017-12-13 22:14:57 +11003160 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00003161 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03003162 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05003163 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00003164 rt->rt6i_dst.plen = 128;
3165 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08003166 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003167
Ido Schimmel4c981e22018-01-07 12:45:04 +02003168 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07003169 * do proper release of the net_device
3170 */
3171 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07003172 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173
David S. Miller87a11572011-12-06 17:04:13 -05003174 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
3175
Linus Torvalds1da177e2005-04-16 15:20:36 -07003176out:
David S. Miller87a11572011-12-06 17:04:13 -05003177 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178}
3179
Daniel Lezcano569d3642008-01-18 03:56:57 -08003180static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003181{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00003182 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08003183 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
3184 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
3185 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
3186 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
3187 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00003188 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189
Eric Dumazetfc66f952010-10-08 06:37:34 +00003190 entries = dst_entries_get_fast(ops);
Eric Dumazetcf86a082020-05-07 18:58:10 -07003191 if (entries > rt_max_size)
3192 entries = dst_entries_get_slow(ops);
3193
Michal Kubeček49a18d82013-08-01 10:04:24 +02003194 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00003195 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003196 goto out;
3197
Benjamin Thery6891a342008-03-04 13:49:47 -08003198 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08003199 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00003200 entries = dst_entries_get_slow(ops);
3201 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08003202 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003203out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08003204 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00003205 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206}
3207
David Ahernb2c709c2019-06-24 13:44:51 -07003208static int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg,
3209 const struct in6_addr *gw_addr, u32 tbid,
3210 int flags, struct fib6_result *res)
David Ahern8c145862016-04-24 21:26:04 -07003211{
3212 struct flowi6 fl6 = {
3213 .flowi6_oif = cfg->fc_ifindex,
3214 .daddr = *gw_addr,
3215 .saddr = cfg->fc_prefsrc,
3216 };
3217 struct fib6_table *table;
David Ahernb2c709c2019-06-24 13:44:51 -07003218 int err;
David Ahern8c145862016-04-24 21:26:04 -07003219
David Ahernf4797b32018-01-25 16:55:08 -08003220 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07003221 if (!table)
David Ahernb2c709c2019-06-24 13:44:51 -07003222 return -EINVAL;
David Ahern8c145862016-04-24 21:26:04 -07003223
3224 if (!ipv6_addr_any(&cfg->fc_prefsrc))
3225 flags |= RT6_LOOKUP_F_HAS_SADDR;
3226
David Ahernf4797b32018-01-25 16:55:08 -08003227 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahern8c145862016-04-24 21:26:04 -07003228
David Ahernb2c709c2019-06-24 13:44:51 -07003229 err = fib6_table_lookup(net, table, cfg->fc_ifindex, &fl6, res, flags);
3230 if (!err && res->f6i != net->ipv6.fib6_null_entry)
3231 fib6_select_path(net, res, &fl6, cfg->fc_ifindex,
3232 cfg->fc_ifindex != 0, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07003233
David Ahernb2c709c2019-06-24 13:44:51 -07003234 return err;
David Ahern8c145862016-04-24 21:26:04 -07003235}
3236
David Ahernfc1e64e2018-01-25 16:55:09 -08003237static int ip6_route_check_nh_onlink(struct net *net,
3238 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07003239 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08003240 struct netlink_ext_ack *extack)
3241{
David Ahernb2c709c2019-06-24 13:44:51 -07003242 u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08003243 const struct in6_addr *gw_addr = &cfg->fc_gateway;
David Ahernb2c709c2019-06-24 13:44:51 -07003244 struct fib6_result res = {};
David Ahernfc1e64e2018-01-25 16:55:09 -08003245 int err;
3246
David Ahernb2c709c2019-06-24 13:44:51 -07003247 err = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0, &res);
3248 if (!err && !(res.fib6_flags & RTF_REJECT) &&
3249 /* ignore match if it is the default route */
3250 !ipv6_addr_any(&res.f6i->fib6_dst.addr) &&
3251 (res.fib6_type != RTN_UNICAST || dev != res.nh->fib_nh_dev)) {
3252 NL_SET_ERR_MSG(extack,
3253 "Nexthop has invalid gateway or device mismatch");
3254 err = -EINVAL;
David Ahernfc1e64e2018-01-25 16:55:09 -08003255 }
3256
3257 return err;
3258}
3259
David Ahern1edce992018-01-25 16:55:07 -08003260static int ip6_route_check_nh(struct net *net,
3261 struct fib6_config *cfg,
3262 struct net_device **_dev,
3263 struct inet6_dev **idev)
3264{
3265 const struct in6_addr *gw_addr = &cfg->fc_gateway;
3266 struct net_device *dev = _dev ? *_dev : NULL;
David Ahernb2c709c2019-06-24 13:44:51 -07003267 int flags = RT6_LOOKUP_F_IFACE;
3268 struct fib6_result res = {};
David Ahern1edce992018-01-25 16:55:07 -08003269 int err = -EHOSTUNREACH;
3270
3271 if (cfg->fc_table) {
David Ahernb2c709c2019-06-24 13:44:51 -07003272 err = ip6_nh_lookup_table(net, cfg, gw_addr,
3273 cfg->fc_table, flags, &res);
3274 /* gw_addr can not require a gateway or resolve to a reject
3275 * route. If a device is given, it must match the result.
3276 */
3277 if (err || res.fib6_flags & RTF_REJECT ||
3278 res.nh->fib_nh_gw_family ||
3279 (dev && dev != res.nh->fib_nh_dev))
3280 err = -EHOSTUNREACH;
David Ahern1edce992018-01-25 16:55:07 -08003281 }
3282
David Ahernb2c709c2019-06-24 13:44:51 -07003283 if (err < 0) {
3284 struct flowi6 fl6 = {
3285 .flowi6_oif = cfg->fc_ifindex,
3286 .daddr = *gw_addr,
3287 };
David Ahern1edce992018-01-25 16:55:07 -08003288
David Ahernb2c709c2019-06-24 13:44:51 -07003289 err = fib6_lookup(net, cfg->fc_ifindex, &fl6, &res, flags);
3290 if (err || res.fib6_flags & RTF_REJECT ||
3291 res.nh->fib_nh_gw_family)
3292 err = -EHOSTUNREACH;
David Ahern1edce992018-01-25 16:55:07 -08003293
David Ahernb2c709c2019-06-24 13:44:51 -07003294 if (err)
3295 return err;
3296
3297 fib6_select_path(net, &res, &fl6, cfg->fc_ifindex,
3298 cfg->fc_ifindex != 0, NULL, flags);
3299 }
3300
3301 err = 0;
David Ahern1edce992018-01-25 16:55:07 -08003302 if (dev) {
David Ahernb2c709c2019-06-24 13:44:51 -07003303 if (dev != res.nh->fib_nh_dev)
3304 err = -EHOSTUNREACH;
David Ahern1edce992018-01-25 16:55:07 -08003305 } else {
David Ahernb2c709c2019-06-24 13:44:51 -07003306 *_dev = dev = res.nh->fib_nh_dev;
David Ahern1edce992018-01-25 16:55:07 -08003307 dev_hold(dev);
David Ahernb2c709c2019-06-24 13:44:51 -07003308 *idev = in6_dev_get(dev);
David Ahern1edce992018-01-25 16:55:07 -08003309 }
3310
David Ahern1edce992018-01-25 16:55:07 -08003311 return err;
3312}
3313
David Ahern9fbb7042018-03-13 08:29:36 -07003314static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
3315 struct net_device **_dev, struct inet6_dev **idev,
3316 struct netlink_ext_ack *extack)
3317{
3318 const struct in6_addr *gw_addr = &cfg->fc_gateway;
3319 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07003320 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07003321 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07003322 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07003323 int err = -EINVAL;
3324
3325 /* if gw_addr is local we will fail to detect this in case
3326 * address is still TENTATIVE (DAD in progress). rt6_lookup()
3327 * will return already-added prefix route via interface that
3328 * prefix route was assigned to, which might be non-loopback.
3329 */
David Ahern232378e2018-03-13 08:29:37 -07003330 if (dev &&
3331 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
3332 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07003333 goto out;
3334 }
3335
3336 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
3337 /* IPv6 strictly inhibits using not link-local
3338 * addresses as nexthop address.
3339 * Otherwise, router will not able to send redirects.
3340 * It is very good, but in some (rare!) circumstances
3341 * (SIT, PtP, NBMA NOARP links) it is handy to allow
3342 * some exceptions. --ANK
3343 * We allow IPv4-mapped nexthops to support RFC4798-type
3344 * addressing
3345 */
3346 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
3347 NL_SET_ERR_MSG(extack, "Invalid gateway address");
3348 goto out;
3349 }
3350
David Ahernb2c709c2019-06-24 13:44:51 -07003351 rcu_read_lock();
3352
David Ahern9fbb7042018-03-13 08:29:36 -07003353 if (cfg->fc_flags & RTNH_F_ONLINK)
3354 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
3355 else
3356 err = ip6_route_check_nh(net, cfg, _dev, idev);
3357
David Ahernb2c709c2019-06-24 13:44:51 -07003358 rcu_read_unlock();
3359
David Ahern9fbb7042018-03-13 08:29:36 -07003360 if (err)
3361 goto out;
3362 }
3363
3364 /* reload in case device was changed */
3365 dev = *_dev;
3366
3367 err = -EINVAL;
3368 if (!dev) {
3369 NL_SET_ERR_MSG(extack, "Egress device not specified");
3370 goto out;
3371 } else if (dev->flags & IFF_LOOPBACK) {
3372 NL_SET_ERR_MSG(extack,
3373 "Egress device can not be loopback device for this route");
3374 goto out;
3375 }
David Ahern232378e2018-03-13 08:29:37 -07003376
3377 /* if we did not check gw_addr above, do so now that the
3378 * egress device has been resolved.
3379 */
3380 if (need_addr_check &&
3381 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
3382 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
3383 goto out;
3384 }
3385
David Ahern9fbb7042018-03-13 08:29:36 -07003386 err = 0;
3387out:
3388 return err;
3389}
3390
David Ahern83c442512019-03-27 20:53:50 -07003391static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
3392{
3393 if ((flags & RTF_REJECT) ||
3394 (dev && (dev->flags & IFF_LOOPBACK) &&
3395 !(addr_type & IPV6_ADDR_LOOPBACK) &&
David Ahernaea23c322020-07-07 07:39:24 -06003396 !(flags & (RTF_ANYCAST | RTF_LOCAL))))
David Ahern83c442512019-03-27 20:53:50 -07003397 return true;
3398
3399 return false;
3400}
3401
3402int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
3403 struct fib6_config *cfg, gfp_t gfp_flags,
3404 struct netlink_ext_ack *extack)
3405{
3406 struct net_device *dev = NULL;
3407 struct inet6_dev *idev = NULL;
3408 int addr_type;
3409 int err;
3410
David Ahernf1741732019-03-27 20:53:57 -07003411 fib6_nh->fib_nh_family = AF_INET6;
Eric Dumazet1bef4c22019-11-07 09:26:19 -08003412#ifdef CONFIG_IPV6_ROUTER_PREF
3413 fib6_nh->last_probe = jiffies;
3414#endif
Roopa Prabhu38428d62020-05-21 22:26:13 -07003415 if (cfg->fc_is_fdb) {
3416 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
3417 fib6_nh->fib_nh_gw_family = AF_INET6;
3418 return 0;
3419 }
David Ahernf1741732019-03-27 20:53:57 -07003420
David Ahern83c442512019-03-27 20:53:50 -07003421 err = -ENODEV;
3422 if (cfg->fc_ifindex) {
3423 dev = dev_get_by_index(net, cfg->fc_ifindex);
3424 if (!dev)
3425 goto out;
3426 idev = in6_dev_get(dev);
3427 if (!idev)
3428 goto out;
3429 }
3430
3431 if (cfg->fc_flags & RTNH_F_ONLINK) {
3432 if (!dev) {
3433 NL_SET_ERR_MSG(extack,
3434 "Nexthop device required for onlink");
3435 goto out;
3436 }
3437
3438 if (!(dev->flags & IFF_UP)) {
3439 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3440 err = -ENETDOWN;
3441 goto out;
3442 }
3443
David Ahernad1601a2019-03-27 20:53:56 -07003444 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07003445 }
3446
David Ahernad1601a2019-03-27 20:53:56 -07003447 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07003448
3449 /* We cannot add true routes via loopback here,
3450 * they would result in kernel looping; promote them to reject routes
3451 */
3452 addr_type = ipv6_addr_type(&cfg->fc_dst);
3453 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
3454 /* hold loopback dev/idev if we haven't done so. */
3455 if (dev != net->loopback_dev) {
3456 if (dev) {
3457 dev_put(dev);
3458 in6_dev_put(idev);
3459 }
3460 dev = net->loopback_dev;
3461 dev_hold(dev);
3462 idev = in6_dev_get(dev);
3463 if (!idev) {
3464 err = -ENODEV;
3465 goto out;
3466 }
3467 }
David Ahern7dd73162019-06-03 18:37:03 -07003468 goto pcpu_alloc;
David Ahern83c442512019-03-27 20:53:50 -07003469 }
3470
3471 if (cfg->fc_flags & RTF_GATEWAY) {
3472 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
3473 if (err)
3474 goto out;
3475
David Ahernad1601a2019-03-27 20:53:56 -07003476 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07003477 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07003478 }
3479
3480 err = -ENODEV;
3481 if (!dev)
3482 goto out;
3483
3484 if (idev->cnf.disable_ipv6) {
3485 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3486 err = -EACCES;
3487 goto out;
3488 }
3489
3490 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3491 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3492 err = -ENETDOWN;
3493 goto out;
3494 }
3495
3496 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3497 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003498 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003499
Alexander Aringfaee6762020-03-27 18:00:21 -04003500 err = fib_nh_common_init(net, &fib6_nh->nh_common, cfg->fc_encap,
David Ahern7dd73162019-06-03 18:37:03 -07003501 cfg->fc_encap_type, cfg, gfp_flags, extack);
3502 if (err)
3503 goto out;
3504
3505pcpu_alloc:
David Ahernf40b6ae2019-05-22 20:27:55 -07003506 fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
3507 if (!fib6_nh->rt6i_pcpu) {
3508 err = -ENOMEM;
3509 goto out;
3510 }
3511
David Ahernad1601a2019-03-27 20:53:56 -07003512 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003513 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003514 err = 0;
3515out:
3516 if (idev)
3517 in6_dev_put(idev);
3518
3519 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003520 lwtstate_put(fib6_nh->fib_nh_lws);
3521 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003522 if (dev)
3523 dev_put(dev);
3524 }
3525
3526 return err;
3527}
3528
David Aherndac7d0f2019-03-27 20:53:51 -07003529void fib6_nh_release(struct fib6_nh *fib6_nh)
3530{
David Aherncc5c0732019-05-22 20:27:58 -07003531 struct rt6_exception_bucket *bucket;
3532
3533 rcu_read_lock();
3534
3535 fib6_nh_flush_exceptions(fib6_nh, NULL);
3536 bucket = fib6_nh_get_excptn_bucket(fib6_nh, NULL);
3537 if (bucket) {
3538 rcu_assign_pointer(fib6_nh->rt6i_exception_bucket, NULL);
3539 kfree(bucket);
3540 }
3541
3542 rcu_read_unlock();
3543
David Ahernf40b6ae2019-05-22 20:27:55 -07003544 if (fib6_nh->rt6i_pcpu) {
3545 int cpu;
3546
3547 for_each_possible_cpu(cpu) {
3548 struct rt6_info **ppcpu_rt;
3549 struct rt6_info *pcpu_rt;
3550
3551 ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
3552 pcpu_rt = *ppcpu_rt;
3553 if (pcpu_rt) {
3554 dst_dev_put(&pcpu_rt->dst);
3555 dst_release(&pcpu_rt->dst);
3556 *ppcpu_rt = NULL;
3557 }
3558 }
3559
3560 free_percpu(fib6_nh->rt6i_pcpu);
3561 }
3562
David Ahern979e2762019-03-27 20:53:58 -07003563 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003564}
3565
David Ahern8d1c8022018-04-17 17:33:26 -07003566static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003567 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003568 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003569{
Daniel Lezcano55786892008-03-04 13:47:47 -08003570 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003571 struct fib6_info *rt = NULL;
David Ahernf88d8ea2019-06-03 20:19:52 -07003572 struct nexthop *nh = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003573 struct fib6_table *table;
David Ahernf88d8ea2019-06-03 20:19:52 -07003574 struct fib6_nh *fib6_nh;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003575 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003576 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003577
David Ahern557c44b2017-04-19 14:19:43 -07003578 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003579 if (cfg->fc_flags & RTF_PCPU) {
3580 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003581 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003582 }
David Ahern557c44b2017-04-19 14:19:43 -07003583
Wei Wang2ea23522017-10-27 17:30:12 -07003584 /* RTF_CACHE is an internal flag; can not be set by userspace */
3585 if (cfg->fc_flags & RTF_CACHE) {
3586 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3587 goto out;
3588 }
3589
David Aherne8478e82018-04-17 17:33:13 -07003590 if (cfg->fc_type > RTN_MAX) {
3591 NL_SET_ERR_MSG(extack, "Invalid route type");
3592 goto out;
3593 }
3594
David Ahernd5d531c2017-05-21 10:12:05 -06003595 if (cfg->fc_dst_len > 128) {
3596 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003597 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003598 }
3599 if (cfg->fc_src_len > 128) {
3600 NL_SET_ERR_MSG(extack, "Invalid source address length");
3601 goto out;
3602 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003604 if (cfg->fc_src_len) {
3605 NL_SET_ERR_MSG(extack,
3606 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003607 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003608 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003609#endif
David Ahern5b983242019-06-08 14:53:34 -07003610 if (cfg->fc_nh_id) {
3611 nh = nexthop_find_by_id(net, cfg->fc_nh_id);
3612 if (!nh) {
3613 NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
3614 goto out;
3615 }
3616 err = fib6_check_nexthop(nh, cfg, extack);
3617 if (err)
3618 goto out;
3619 }
David Ahernfc1e64e2018-01-25 16:55:09 -08003620
Matti Vaittinend71314b2011-11-14 00:14:49 +00003621 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003622 if (cfg->fc_nlinfo.nlh &&
3623 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003624 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003625 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003626 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003627 table = fib6_new_table(net, cfg->fc_table);
3628 }
3629 } else {
3630 table = fib6_new_table(net, cfg->fc_table);
3631 }
David S. Miller38308472011-12-03 18:02:47 -05003632
3633 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003634 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003635
David Ahern93531c62018-04-17 17:33:25 -07003636 err = -ENOMEM;
David Ahernf88d8ea2019-06-03 20:19:52 -07003637 rt = fib6_info_alloc(gfp_flags, !nh);
David Ahern93531c62018-04-17 17:33:25 -07003638 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003640
David Ahernd7e774f2018-11-06 12:51:15 -08003641 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3642 extack);
David Ahern767a2212018-10-04 20:07:51 -07003643 if (IS_ERR(rt->fib6_metrics)) {
3644 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003645 /* Do not leave garbage there. */
3646 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003647 goto out;
3648 }
3649
David Ahern93531c62018-04-17 17:33:25 -07003650 if (cfg->fc_flags & RTF_ADDRCONF)
3651 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652
Gao feng1716a962012-04-06 00:13:10 +00003653 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003654 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003655 clock_t_to_jiffies(cfg->fc_expires));
3656 else
David Ahern14895682018-04-17 17:33:17 -07003657 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003658
Thomas Graf86872cb2006-08-22 00:01:08 -07003659 if (cfg->fc_protocol == RTPROT_UNSPEC)
3660 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003661 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003662
David Ahern83c442512019-03-27 20:53:50 -07003663 rt->fib6_table = table;
3664 rt->fib6_metric = cfg->fc_metric;
David Ahernc7036d92019-06-19 10:50:24 -07003665 rt->fib6_type = cfg->fc_type ? : RTN_UNICAST;
David Ahern2b2450c2019-03-27 20:53:52 -07003666 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003667
David Ahern93c2fb22018-04-18 15:38:59 -07003668 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3669 rt->fib6_dst.plen = cfg->fc_dst_len;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003670
Linus Torvalds1da177e2005-04-16 15:20:36 -07003671#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003672 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3673 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003674#endif
David Ahernf88d8ea2019-06-03 20:19:52 -07003675 if (nh) {
David Ahernf88d8ea2019-06-03 20:19:52 -07003676 if (rt->fib6_src.plen) {
Colin Ian King4daa95a2019-06-06 09:40:39 +01003677 NL_SET_ERR_MSG(extack, "Nexthops can not be used with source routing");
David Ahernf88d8ea2019-06-03 20:19:52 -07003678 goto out;
3679 }
Xiyu Yang706ec912020-07-25 16:02:18 +08003680 if (!nexthop_get(nh)) {
3681 NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
3682 goto out;
3683 }
David Ahernf88d8ea2019-06-03 20:19:52 -07003684 rt->nh = nh;
3685 fib6_nh = nexthop_fib6_nh(rt->nh);
3686 } else {
3687 err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack);
3688 if (err)
3689 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003690
David Ahernf88d8ea2019-06-03 20:19:52 -07003691 fib6_nh = rt->fib6_nh;
3692
3693 /* We cannot add true routes via loopback here, they would
3694 * result in kernel looping; promote them to reject routes
3695 */
3696 addr_type = ipv6_addr_type(&cfg->fc_dst);
3697 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev,
3698 addr_type))
3699 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
3700 }
David Ahern955ec4c2018-01-24 19:45:29 -08003701
Daniel Walterc3968a82011-04-13 21:10:57 +00003702 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahernf88d8ea2019-06-03 20:19:52 -07003703 struct net_device *dev = fib6_nh->fib_nh_dev;
David Ahern83c442512019-03-27 20:53:50 -07003704
Daniel Walterc3968a82011-04-13 21:10:57 +00003705 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003706 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003707 err = -EINVAL;
3708 goto out;
3709 }
David Ahern93c2fb22018-04-18 15:38:59 -07003710 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3711 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003712 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003713 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003714
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003715 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003716out:
David Ahern93531c62018-04-17 17:33:25 -07003717 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003718 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003719}
3720
David Ahernacb54e32018-04-17 17:33:22 -07003721int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003722 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003723{
David Ahern8d1c8022018-04-17 17:33:26 -07003724 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003725 int err;
3726
David Ahernacb54e32018-04-17 17:33:22 -07003727 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003728 if (IS_ERR(rt))
3729 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003730
David Ahernd4ead6b2018-04-17 17:33:16 -07003731 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003732 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003733
Linus Torvalds1da177e2005-04-16 15:20:36 -07003734 return err;
3735}
3736
David Ahern8d1c8022018-04-17 17:33:26 -07003737static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003738{
David Ahernafb1d4b52018-04-17 17:33:11 -07003739 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003740 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003741 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742
David Ahern421842e2018-04-17 17:33:18 -07003743 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003744 err = -ENOENT;
3745 goto out;
3746 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003747
David Ahern93c2fb22018-04-18 15:38:59 -07003748 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003749 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003750 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003751 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752
Gao feng6825a262012-09-19 19:25:34 +00003753out:
David Ahern93531c62018-04-17 17:33:25 -07003754 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003755 return err;
3756}
3757
Roopa Prabhu11dd74b2020-04-27 13:56:45 -07003758int ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003759{
Roopa Prabhu11dd74b2020-04-27 13:56:45 -07003760 struct nl_info info = {
3761 .nl_net = net,
3762 .skip_notify = skip_notify
3763 };
David Ahernafb1d4b52018-04-17 17:33:11 -07003764
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003765 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003766}
3767
David Ahern8d1c8022018-04-17 17:33:26 -07003768static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003769{
3770 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003771 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003772 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003773 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003774 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003775
David Ahern421842e2018-04-17 17:33:18 -07003776 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003777 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003778 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003779 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003780
David Ahern93c2fb22018-04-18 15:38:59 -07003781 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003782 struct fib6_info *sibling, *next_sibling;
Ido Schimmel02846962019-12-23 15:28:18 +02003783 struct fib6_node *fn;
David Ahern0ae81332017-02-02 12:37:08 -08003784
David Ahern16a16cd2017-02-02 12:37:11 -08003785 /* prefer to send a single notification with all hops */
3786 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3787 if (skb) {
3788 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3789
David Ahernd4ead6b2018-04-17 17:33:16 -07003790 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003791 NULL, NULL, 0, RTM_DELROUTE,
3792 info->portid, seq, 0) < 0) {
3793 kfree_skb(skb);
3794 skb = NULL;
3795 } else
3796 info->skip_notify = 1;
3797 }
3798
Ido Schimmel02846962019-12-23 15:28:18 +02003799 /* 'rt' points to the first sibling route. If it is not the
3800 * leaf, then we do not need to send a notification. Otherwise,
3801 * we need to check if the last sibling has a next route or not
3802 * and emit a replace or delete notification, respectively.
3803 */
Ido Schimmel2881fd62019-06-18 18:12:49 +03003804 info->skip_notify_kernel = 1;
Ido Schimmel02846962019-12-23 15:28:18 +02003805 fn = rcu_dereference_protected(rt->fib6_node,
3806 lockdep_is_held(&table->tb6_lock));
3807 if (rcu_access_pointer(fn->leaf) == rt) {
3808 struct fib6_info *last_sibling, *replace_rt;
3809
3810 last_sibling = list_last_entry(&rt->fib6_siblings,
3811 struct fib6_info,
3812 fib6_siblings);
3813 replace_rt = rcu_dereference_protected(
3814 last_sibling->fib6_next,
3815 lockdep_is_held(&table->tb6_lock));
3816 if (replace_rt)
3817 call_fib6_entry_notifiers_replace(net,
3818 replace_rt);
3819 else
3820 call_fib6_multipath_entry_notifiers(net,
Ido Schimmelcaafb252019-12-23 15:28:20 +02003821 FIB_EVENT_ENTRY_DEL,
Ido Schimmel02846962019-12-23 15:28:18 +02003822 rt, rt->fib6_nsiblings,
3823 NULL);
3824 }
David Ahern0ae81332017-02-02 12:37:08 -08003825 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003826 &rt->fib6_siblings,
3827 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003828 err = fib6_del(sibling, info);
3829 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003830 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003831 }
3832 }
3833
3834 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003835out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003836 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003837out_put:
David Ahern93531c62018-04-17 17:33:25 -07003838 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003839
3840 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003841 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003842 info->nlh, gfp_any());
3843 }
David Ahern0ae81332017-02-02 12:37:08 -08003844 return err;
3845}
3846
David Ahern0fa6efc2019-05-22 20:28:00 -07003847static int __ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
David Ahern23fb93a2018-04-17 17:33:23 -07003848{
3849 int rc = -ESRCH;
3850
3851 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3852 goto out;
3853
3854 if (cfg->fc_flags & RTF_GATEWAY &&
3855 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3856 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003857
3858 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003859out:
3860 return rc;
3861}
3862
David Ahern0fa6efc2019-05-22 20:28:00 -07003863static int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt,
3864 struct fib6_nh *nh)
3865{
3866 struct fib6_result res = {
3867 .f6i = rt,
3868 .nh = nh,
3869 };
3870 struct rt6_info *rt_cache;
3871
3872 rt_cache = rt6_find_cached_rt(&res, &cfg->fc_dst, &cfg->fc_src);
3873 if (rt_cache)
3874 return __ip6_del_cached_rt(rt_cache, cfg);
3875
3876 return 0;
3877}
3878
David Ahern5b983242019-06-08 14:53:34 -07003879struct fib6_nh_del_cached_rt_arg {
3880 struct fib6_config *cfg;
3881 struct fib6_info *f6i;
3882};
3883
3884static int fib6_nh_del_cached_rt(struct fib6_nh *nh, void *_arg)
3885{
3886 struct fib6_nh_del_cached_rt_arg *arg = _arg;
3887 int rc;
3888
3889 rc = ip6_del_cached_rt(arg->cfg, arg->f6i, nh);
3890 return rc != -ESRCH ? rc : 0;
3891}
3892
3893static int ip6_del_cached_rt_nh(struct fib6_config *cfg, struct fib6_info *f6i)
3894{
3895 struct fib6_nh_del_cached_rt_arg arg = {
3896 .cfg = cfg,
3897 .f6i = f6i
3898 };
3899
3900 return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_del_cached_rt, &arg);
3901}
3902
David Ahern333c4302017-05-21 10:12:04 -06003903static int ip6_route_del(struct fib6_config *cfg,
3904 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003905{
Thomas Grafc71099a2006-08-04 23:20:06 -07003906 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003907 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003908 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003909 int err = -ESRCH;
3910
Daniel Lezcano55786892008-03-04 13:47:47 -08003911 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003912 if (!table) {
3913 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003914 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003915 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003916
Wei Wang66f5d6c2017-10-06 12:06:10 -07003917 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003918
3919 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003920 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003921 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003922 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003923
Linus Torvalds1da177e2005-04-16 15:20:36 -07003924 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003925 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003926 struct fib6_nh *nh;
3927
Stefano Brivio3401bfb2019-06-21 17:45:25 +02003928 if (rt->nh && cfg->fc_nh_id &&
3929 rt->nh->id != cfg->fc_nh_id)
David Ahern5b983242019-06-08 14:53:34 -07003930 continue;
David Ahern23fb93a2018-04-17 17:33:23 -07003931
David Ahern5b983242019-06-08 14:53:34 -07003932 if (cfg->fc_flags & RTF_CACHE) {
3933 int rc = 0;
3934
3935 if (rt->nh) {
3936 rc = ip6_del_cached_rt_nh(cfg, rt);
3937 } else if (cfg->fc_nh_id) {
3938 continue;
3939 } else {
3940 nh = rt->fib6_nh;
3941 rc = ip6_del_cached_rt(cfg, rt, nh);
3942 }
David Ahern0fa6efc2019-05-22 20:28:00 -07003943 if (rc != -ESRCH) {
3944 rcu_read_unlock();
3945 return rc;
David Ahern23fb93a2018-04-17 17:33:23 -07003946 }
3947 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003948 }
David Ahernad1601a2019-03-27 20:53:56 -07003949
David Ahern5b983242019-06-08 14:53:34 -07003950 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
3951 continue;
3952 if (cfg->fc_protocol &&
3953 cfg->fc_protocol != rt->fib6_protocol)
3954 continue;
3955
3956 if (rt->nh) {
3957 if (!fib6_info_hold_safe(rt))
3958 continue;
3959 rcu_read_unlock();
3960
3961 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3962 }
3963 if (cfg->fc_nh_id)
3964 continue;
3965
3966 nh = rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003967 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003968 (!nh->fib_nh_dev ||
3969 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003970 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003971 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003972 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003973 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003974 if (!fib6_info_hold_safe(rt))
3975 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003976 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003977
David Ahern0ae81332017-02-02 12:37:08 -08003978 /* if gateway was specified only delete the one hop */
3979 if (cfg->fc_flags & RTF_GATEWAY)
3980 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3981
3982 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003983 }
3984 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003985 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003986
3987 return err;
3988}
3989
David S. Miller6700c272012-07-17 03:29:28 -07003990static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003991{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003992 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003993 struct rt6_info *rt, *nrt = NULL;
David Ahern85bd05d2019-04-16 14:36:01 -07003994 struct fib6_result res = {};
David S. Millere8599ff2012-07-11 23:43:53 -07003995 struct ndisc_options ndopts;
3996 struct inet6_dev *in6_dev;
3997 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003998 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003999 int optlen, on_link;
4000 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07004001
Simon Horman29a3cad2013-05-28 20:34:26 +00004002 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00004003 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07004004
4005 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07004006 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07004007 return;
4008 }
4009
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00004010 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07004011
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00004012 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07004013 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07004014 return;
4015 }
4016
David S. Miller6e157b62012-07-12 00:05:02 -07004017 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00004018 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07004019 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00004020 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07004021 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07004022 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07004023 return;
4024 }
4025
4026 in6_dev = __in6_dev_get(skb->dev);
4027 if (!in6_dev)
4028 return;
4029 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
4030 return;
4031
4032 /* RFC2461 8.1:
4033 * The IP source address of the Redirect MUST be the same as the current
4034 * first-hop router for the specified ICMP Destination Address.
4035 */
4036
Alexander Aringf997c552016-06-15 21:20:23 +02004037 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07004038 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
4039 return;
4040 }
David S. Miller6e157b62012-07-12 00:05:02 -07004041
4042 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07004043 if (ndopts.nd_opts_tgt_lladdr) {
4044 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
4045 skb->dev);
4046 if (!lladdr) {
4047 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
4048 return;
4049 }
4050 }
4051
David S. Miller6e157b62012-07-12 00:05:02 -07004052 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01004053 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07004054 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
4055 return;
4056 }
4057
4058 /* Redirect received -> path was valid.
4059 * Look, redirects are sent only in response to data packets,
4060 * so that this nexthop apparently is reachable. --ANK
4061 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02004062 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07004063
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00004064 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07004065 if (!neigh)
4066 return;
4067
Linus Torvalds1da177e2005-04-16 15:20:36 -07004068 /*
4069 * We have finally decided to accept it.
4070 */
4071
Alexander Aringf997c552016-06-15 21:20:23 +02004072 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004073 NEIGH_UPDATE_F_WEAK_OVERRIDE|
4074 NEIGH_UPDATE_F_OVERRIDE|
4075 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02004076 NEIGH_UPDATE_F_ISROUTER)),
4077 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004078
David Ahern4d85cd02018-04-20 15:37:59 -07004079 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07004080 res.f6i = rcu_dereference(rt->from);
David S. Millerff24e492019-05-02 22:14:21 -04004081 if (!res.f6i)
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07004082 goto out;
David Ahern8a14e462018-04-23 11:32:07 -07004083
David Ahern49d5b8e2019-06-08 14:53:30 -07004084 if (res.f6i->nh) {
4085 struct fib6_nh_match_arg arg = {
4086 .dev = dst->dev,
4087 .gw = &rt->rt6i_gateway,
4088 };
4089
4090 nexthop_for_each_fib6_nh(res.f6i->nh,
4091 fib6_nh_find_match, &arg);
4092
4093 /* fib6_info uses a nexthop that does not have fib6_nh
4094 * using the dst->dev. Should be impossible
4095 */
4096 if (!arg.match)
4097 goto out;
4098 res.nh = arg.match;
4099 } else {
4100 res.nh = res.f6i->fib6_nh;
4101 }
4102
David Ahern7d21fec2019-04-16 14:36:11 -07004103 res.fib6_flags = res.f6i->fib6_flags;
4104 res.fib6_type = res.f6i->fib6_type;
David Ahern85bd05d2019-04-16 14:36:01 -07004105 nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05004106 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004107 goto out;
4108
4109 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
4110 if (on_link)
4111 nrt->rt6i_flags &= ~RTF_GATEWAY;
4112
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004113 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004114
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07004115 /* rt6_insert_exception() will take care of duplicated exceptions */
David Ahern5012f0a2019-04-16 14:36:05 -07004116 if (rt6_insert_exception(nrt, &res)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07004117 dst_release_immediate(&nrt->dst);
4118 goto out;
4119 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004120
Changli Gaod8d1f302010-06-10 23:31:35 -07004121 netevent.old = &rt->dst;
4122 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00004123 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00004124 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07004125 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
4126
Linus Torvalds1da177e2005-04-16 15:20:36 -07004127out:
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07004128 rcu_read_unlock();
David S. Millere8599ff2012-07-11 23:43:53 -07004129 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07004130}
4131
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004132#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07004133static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00004134 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07004135 const struct in6_addr *gwaddr,
4136 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004137{
David Ahern830218c2016-10-24 10:52:35 -07004138 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
4139 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004140 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07004141 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07004142 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004143
David Ahern830218c2016-10-24 10:52:35 -07004144 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05004145 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07004146 return NULL;
4147
Wei Wang66f5d6c2017-10-06 12:06:10 -07004148 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07004149 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004150 if (!fn)
4151 goto out;
4152
Wei Wang66f5d6c2017-10-06 12:06:10 -07004153 for_each_fib6_node_rt_rcu(fn) {
David Ahernf88d8ea2019-06-03 20:19:52 -07004154 /* these routes do not use nexthops */
4155 if (rt->nh)
4156 continue;
David Ahern1cf844c2019-05-22 20:27:59 -07004157 if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004158 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07004159 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahern1cf844c2019-05-22 20:27:59 -07004160 !rt->fib6_nh->fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004161 continue;
David Ahern1cf844c2019-05-22 20:27:59 -07004162 if (!ipv6_addr_equal(&rt->fib6_nh->fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004163 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07004164 if (!fib6_info_hold_safe(rt))
4165 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004166 break;
4167 }
4168out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07004169 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004170 return rt;
4171}
4172
David Ahern8d1c8022018-04-17 17:33:26 -07004173static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00004174 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07004175 const struct in6_addr *gwaddr,
4176 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00004177 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004178{
Thomas Graf86872cb2006-08-22 00:01:08 -07004179 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08004180 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07004181 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07004182 .fc_dst_len = prefixlen,
4183 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
4184 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08004185 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07004186 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004187 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08004188 .fc_nlinfo.nlh = NULL,
4189 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07004190 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004191
Xu Wang91b2c9a2020-09-21 06:38:56 +00004192 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004193 cfg.fc_dst = *prefix;
4194 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07004195
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08004196 /* We should treat it as a default route if prefix length is 0. */
4197 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07004198 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004199
David Ahernacb54e32018-04-17 17:33:22 -07004200 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004201
David Ahern830218c2016-10-24 10:52:35 -07004202 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08004203}
4204#endif
4205
David Ahern8d1c8022018-04-17 17:33:26 -07004206struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07004207 const struct in6_addr *addr,
4208 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004209{
David Ahern830218c2016-10-24 10:52:35 -07004210 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07004211 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07004212 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004213
David Ahernafb1d4b52018-04-17 17:33:11 -07004214 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05004215 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07004216 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004217
Wei Wang66f5d6c2017-10-06 12:06:10 -07004218 rcu_read_lock();
4219 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernf88d8ea2019-06-03 20:19:52 -07004220 struct fib6_nh *nh;
David Ahernad1601a2019-03-27 20:53:56 -07004221
David Ahernf88d8ea2019-06-03 20:19:52 -07004222 /* RA routes do not use nexthops */
4223 if (rt->nh)
4224 continue;
4225
4226 nh = rt->fib6_nh;
David Ahernad1601a2019-03-27 20:53:56 -07004227 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07004228 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07004229 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004230 break;
4231 }
Wei Wange873e4b2018-07-21 20:56:32 -07004232 if (rt && !fib6_info_hold_safe(rt))
4233 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07004234 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004235 return rt;
4236}
4237
David Ahern8d1c8022018-04-17 17:33:26 -07004238struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07004239 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08004240 struct net_device *dev,
Praveen Chaudhary6b2e04b2021-01-25 13:44:30 -08004241 unsigned int pref,
4242 u32 defrtr_usr_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004243{
Thomas Graf86872cb2006-08-22 00:01:08 -07004244 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07004245 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Praveen Chaudhary6b2e04b2021-01-25 13:44:30 -08004246 .fc_metric = defrtr_usr_metric,
Thomas Graf86872cb2006-08-22 00:01:08 -07004247 .fc_ifindex = dev->ifindex,
4248 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
4249 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08004250 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07004251 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004252 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08004253 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07004254 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07004255 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004256
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004257 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004258
David Ahernacb54e32018-04-17 17:33:22 -07004259 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07004260 struct fib6_table *table;
4261
4262 table = fib6_get_table(dev_net(dev), cfg.fc_table);
4263 if (table)
4264 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
4265 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004266
David Ahernafb1d4b52018-04-17 17:33:11 -07004267 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004268}
4269
David Ahernafb1d4b52018-04-17 17:33:11 -07004270static void __rt6_purge_dflt_routers(struct net *net,
4271 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004272{
David Ahern8d1c8022018-04-17 17:33:26 -07004273 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004274
4275restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07004276 rcu_read_lock();
4277 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07004278 struct net_device *dev = fib6_info_nh_dev(rt);
4279 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
4280
David Ahern93c2fb22018-04-18 15:38:59 -07004281 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07004282 (!idev || idev->cnf.accept_ra != 2) &&
4283 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07004284 rcu_read_unlock();
Roopa Prabhu11dd74b2020-04-27 13:56:45 -07004285 ip6_del_rt(net, rt, false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004286 goto restart;
4287 }
4288 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07004289 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07004290
4291 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
4292}
4293
4294void rt6_purge_dflt_routers(struct net *net)
4295{
4296 struct fib6_table *table;
4297 struct hlist_head *head;
4298 unsigned int h;
4299
4300 rcu_read_lock();
4301
4302 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
4303 head = &net->ipv6.fib_table_hash[h];
4304 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
4305 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07004306 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07004307 }
4308 }
4309
4310 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004311}
4312
Daniel Lezcano55786892008-03-04 13:47:47 -08004313static void rtmsg_to_fib6_config(struct net *net,
4314 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07004315 struct fib6_config *cfg)
4316{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07004317 *cfg = (struct fib6_config){
4318 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
4319 : RT6_TABLE_MAIN,
4320 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07004321 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07004322 .fc_expires = rtmsg->rtmsg_info,
4323 .fc_dst_len = rtmsg->rtmsg_dst_len,
4324 .fc_src_len = rtmsg->rtmsg_src_len,
4325 .fc_flags = rtmsg->rtmsg_flags,
4326 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07004327
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07004328 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07004329
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07004330 .fc_dst = rtmsg->rtmsg_dst,
4331 .fc_src = rtmsg->rtmsg_src,
4332 .fc_gateway = rtmsg->rtmsg_gateway,
4333 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004334}
4335
Christoph Hellwig7c1552da2020-05-18 08:28:05 +02004336int ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004337{
Thomas Graf86872cb2006-08-22 00:01:08 -07004338 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004339 int err;
4340
Christoph Hellwig7c1552da2020-05-18 08:28:05 +02004341 if (cmd != SIOCADDRT && cmd != SIOCDELRT)
4342 return -EINVAL;
4343 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
4344 return -EPERM;
4345
4346 rtmsg_to_fib6_config(net, rtmsg, &cfg);
4347
4348 rtnl_lock();
Ian Morris67ba4152014-08-24 21:53:10 +01004349 switch (cmd) {
Christoph Hellwig7c1552da2020-05-18 08:28:05 +02004350 case SIOCADDRT:
4351 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
4352 break;
4353 case SIOCDELRT:
4354 err = ip6_route_del(&cfg, NULL);
4355 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07004356 }
Christoph Hellwig7c1552da2020-05-18 08:28:05 +02004357 rtnl_unlock();
4358 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004359}
4360
4361/*
4362 * Drop the packet on the floor
4363 */
4364
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07004365static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004366{
Eric Dumazetadf30902009-06-02 05:19:30 +00004367 struct dst_entry *dst = skb_dst(skb);
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04004368 struct net *net = dev_net(dst->dev);
4369 struct inet6_dev *idev;
4370 int type;
4371
4372 if (netif_is_l3_master(skb->dev) &&
4373 dst->dev == net->loopback_dev)
4374 idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
4375 else
4376 idev = ip6_dst_idev(dst);
4377
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004378 switch (ipstats_mib_noroutes) {
4379 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07004380 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00004381 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04004382 IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004383 break;
4384 }
Joe Perchesa8eceea2020-03-12 15:50:22 -07004385 fallthrough;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004386 case IPSTATS_MIB_OUTNOROUTES:
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04004387 IP6_INC_STATS(net, idev, ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004388 break;
4389 }
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04004390
4391 /* Start over by dropping the dst for l3mdev case */
4392 if (netif_is_l3_master(skb->dev))
4393 skb_dst_drop(skb);
4394
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00004395 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004396 kfree_skb(skb);
4397 return 0;
4398}
4399
Thomas Graf9ce8ade2006-10-18 20:46:54 -07004400static int ip6_pkt_discard(struct sk_buff *skb)
4401{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004402 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07004403}
4404
Eric W. Biedermanede20592015-10-07 16:48:47 -05004405static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004406{
Eric Dumazetadf30902009-06-02 05:19:30 +00004407 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004408 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004409}
4410
Thomas Graf9ce8ade2006-10-18 20:46:54 -07004411static int ip6_pkt_prohibit(struct sk_buff *skb)
4412{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004413 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07004414}
4415
Eric W. Biedermanede20592015-10-07 16:48:47 -05004416static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07004417{
Eric Dumazetadf30902009-06-02 05:19:30 +00004418 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07004419 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07004420}
4421
Linus Torvalds1da177e2005-04-16 15:20:36 -07004422/*
4423 * Allocate a dst for local (unicast / anycast) address.
4424 */
4425
David Ahern360a9882018-04-18 15:39:00 -07004426struct fib6_info *addrconf_f6i_alloc(struct net *net,
4427 struct inet6_dev *idev,
4428 const struct in6_addr *addr,
4429 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004430{
David Ahernc7a1ce32019-03-21 05:21:35 -07004431 struct fib6_config cfg = {
4432 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
4433 .fc_ifindex = idev->dev->ifindex,
Maciej Żenczykowskid55a2e32019-09-02 09:23:36 -07004434 .fc_flags = RTF_UP | RTF_NONEXTHOP,
David Ahernc7a1ce32019-03-21 05:21:35 -07004435 .fc_dst = *addr,
4436 .fc_dst_len = 128,
4437 .fc_protocol = RTPROT_KERNEL,
4438 .fc_nlinfo.nl_net = net,
4439 .fc_ignore_dev_down = true,
4440 };
Maciej Żenczykowskid55a2e32019-09-02 09:23:36 -07004441 struct fib6_info *f6i;
David Ahern5f02ce242016-09-10 12:09:54 -07004442
David Aherne8478e82018-04-17 17:33:13 -07004443 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07004444 cfg.fc_type = RTN_ANYCAST;
4445 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07004446 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07004447 cfg.fc_type = RTN_LOCAL;
4448 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07004449 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004450
Maciej Żenczykowskid55a2e32019-09-02 09:23:36 -07004451 f6i = ip6_route_info_create(&cfg, gfp_flags, NULL);
Maciej Żenczykowski8652f172019-09-05 20:56:37 -07004452 if (!IS_ERR(f6i))
Maciej Żenczykowskid55a2e32019-09-02 09:23:36 -07004453 f6i->dst_nocount = true;
4454 return f6i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004455}
4456
Daniel Walterc3968a82011-04-13 21:10:57 +00004457/* remove deleted ip from prefsrc entries */
4458struct arg_dev_net_ip {
4459 struct net_device *dev;
4460 struct net *net;
4461 struct in6_addr *addr;
4462};
4463
David Ahern8d1c8022018-04-17 17:33:26 -07004464static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00004465{
4466 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
4467 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
4468 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
4469
David Ahernf88d8ea2019-06-03 20:19:52 -07004470 if (!rt->nh &&
4471 ((void *)rt->fib6_nh->fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07004472 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07004473 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07004474 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00004475 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07004476 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07004477 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00004478 }
4479 return 0;
4480}
4481
4482void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
4483{
4484 struct net *net = dev_net(ifp->idev->dev);
4485 struct arg_dev_net_ip adni = {
4486 .dev = ifp->idev->dev,
4487 .net = net,
4488 .addr = &ifp->addr,
4489 };
Li RongQing0c3584d2013-12-27 16:32:38 +08004490 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00004491}
4492
David Ahern2b2450c2019-03-27 20:53:52 -07004493#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08004494
4495/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07004496static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08004497{
4498 struct in6_addr *gateway = (struct in6_addr *)arg;
David Ahernf88d8ea2019-06-03 20:19:52 -07004499 struct fib6_nh *nh;
Duan Jiongbe7a0102014-05-15 15:56:14 +08004500
David Ahernf88d8ea2019-06-03 20:19:52 -07004501 /* RA routes do not use nexthops */
4502 if (rt->nh)
4503 return 0;
4504
4505 nh = rt->fib6_nh;
David Ahern93c2fb22018-04-18 15:38:59 -07004506 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Aherncc5c0732019-05-22 20:27:58 -07004507 nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6))
Duan Jiongbe7a0102014-05-15 15:56:14 +08004508 return -1;
Wei Wangb16cb452017-10-06 12:06:00 -07004509
4510 /* Further clean up cached routes in exception table.
4511 * This is needed because cached route may have a different
4512 * gateway than its 'parent' in the case of an ip redirect.
4513 */
David Aherncc5c0732019-05-22 20:27:58 -07004514 fib6_nh_exceptions_clean_tohost(nh, gateway);
Wei Wangb16cb452017-10-06 12:06:00 -07004515
Duan Jiongbe7a0102014-05-15 15:56:14 +08004516 return 0;
4517}
4518
4519void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
4520{
4521 fib6_clean_all(net, fib6_clean_tohost, gateway);
4522}
4523
Ido Schimmel2127d952018-01-07 12:45:03 +02004524struct arg_netdev_event {
4525 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02004526 union {
David Ahernecc56632019-04-23 08:48:09 -07004527 unsigned char nh_flags;
Ido Schimmel4c981e22018-01-07 12:45:04 +02004528 unsigned long event;
4529 };
Ido Schimmel2127d952018-01-07 12:45:03 +02004530};
4531
David Ahern8d1c8022018-04-17 17:33:26 -07004532static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004533{
David Ahern8d1c8022018-04-17 17:33:26 -07004534 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004535 struct fib6_node *fn;
4536
David Ahern93c2fb22018-04-18 15:38:59 -07004537 fn = rcu_dereference_protected(rt->fib6_node,
4538 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004539 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07004540 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004541 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07004542 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07004543 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004544 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07004545 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07004546 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004547 }
4548
4549 return NULL;
4550}
4551
David Ahernf88d8ea2019-06-03 20:19:52 -07004552/* only called for fib entries with builtin fib6_nh */
David Ahern8d1c8022018-04-17 17:33:26 -07004553static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004554{
David Ahern1cf844c2019-05-22 20:27:59 -07004555 if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD ||
4556 (rt->fib6_nh->fib_nh_flags & RTNH_F_LINKDOWN &&
4557 ip6_ignore_linkdown(rt->fib6_nh->fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004558 return true;
4559
4560 return false;
4561}
4562
David Ahern8d1c8022018-04-17 17:33:26 -07004563static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004564{
David Ahern8d1c8022018-04-17 17:33:26 -07004565 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004566 int total = 0;
4567
4568 if (!rt6_is_dead(rt))
David Ahern1cf844c2019-05-22 20:27:59 -07004569 total += rt->fib6_nh->fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004570
David Ahern93c2fb22018-04-18 15:38:59 -07004571 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004572 if (!rt6_is_dead(iter))
David Ahern1cf844c2019-05-22 20:27:59 -07004573 total += iter->fib6_nh->fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004574 }
4575
4576 return total;
4577}
4578
David Ahern8d1c8022018-04-17 17:33:26 -07004579static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004580{
4581 int upper_bound = -1;
4582
4583 if (!rt6_is_dead(rt)) {
David Ahern1cf844c2019-05-22 20:27:59 -07004584 *weight += rt->fib6_nh->fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004585 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
4586 total) - 1;
4587 }
David Ahern1cf844c2019-05-22 20:27:59 -07004588 atomic_set(&rt->fib6_nh->fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004589}
4590
David Ahern8d1c8022018-04-17 17:33:26 -07004591static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004592{
David Ahern8d1c8022018-04-17 17:33:26 -07004593 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004594 int weight = 0;
4595
4596 rt6_upper_bound_set(rt, &weight, total);
4597
David Ahern93c2fb22018-04-18 15:38:59 -07004598 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004599 rt6_upper_bound_set(iter, &weight, total);
4600}
4601
David Ahern8d1c8022018-04-17 17:33:26 -07004602void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004603{
David Ahern8d1c8022018-04-17 17:33:26 -07004604 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004605 int total;
4606
4607 /* In case the entire multipath route was marked for flushing,
4608 * then there is no need to rebalance upon the removal of every
4609 * sibling route.
4610 */
David Ahern93c2fb22018-04-18 15:38:59 -07004611 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004612 return;
4613
4614 /* During lookup routes are evaluated in order, so we need to
4615 * make sure upper bounds are assigned from the first sibling
4616 * onwards.
4617 */
4618 first = rt6_multipath_first_sibling(rt);
4619 if (WARN_ON_ONCE(!first))
4620 return;
4621
4622 total = rt6_multipath_total_weight(first);
4623 rt6_multipath_upper_bound_set(first, total);
4624}
4625
David Ahern8d1c8022018-04-17 17:33:26 -07004626static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02004627{
4628 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07004629 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02004630
David Ahernf88d8ea2019-06-03 20:19:52 -07004631 if (rt != net->ipv6.fib6_null_entry && !rt->nh &&
David Ahern1cf844c2019-05-22 20:27:59 -07004632 rt->fib6_nh->fib_nh_dev == arg->dev) {
4633 rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07004634 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004635 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004636 }
Ido Schimmel2127d952018-01-07 12:45:03 +02004637
4638 return 0;
4639}
4640
David Ahernecc56632019-04-23 08:48:09 -07004641void rt6_sync_up(struct net_device *dev, unsigned char nh_flags)
Ido Schimmel2127d952018-01-07 12:45:03 +02004642{
4643 struct arg_netdev_event arg = {
4644 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004645 {
4646 .nh_flags = nh_flags,
4647 },
Ido Schimmel2127d952018-01-07 12:45:03 +02004648 };
4649
4650 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
4651 arg.nh_flags |= RTNH_F_LINKDOWN;
4652
4653 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
4654}
4655
David Ahernf88d8ea2019-06-03 20:19:52 -07004656/* only called for fib entries with inline fib6_nh */
David Ahern8d1c8022018-04-17 17:33:26 -07004657static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004658 const struct net_device *dev)
4659{
David Ahern8d1c8022018-04-17 17:33:26 -07004660 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004661
David Ahern1cf844c2019-05-22 20:27:59 -07004662 if (rt->fib6_nh->fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004663 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07004664 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahern1cf844c2019-05-22 20:27:59 -07004665 if (iter->fib6_nh->fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004666 return true;
4667
4668 return false;
4669}
4670
David Ahern8d1c8022018-04-17 17:33:26 -07004671static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004672{
David Ahern8d1c8022018-04-17 17:33:26 -07004673 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004674
4675 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004676 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004677 iter->should_flush = 1;
4678}
4679
David Ahern8d1c8022018-04-17 17:33:26 -07004680static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004681 const struct net_device *down_dev)
4682{
David Ahern8d1c8022018-04-17 17:33:26 -07004683 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004684 unsigned int dead = 0;
4685
David Ahern1cf844c2019-05-22 20:27:59 -07004686 if (rt->fib6_nh->fib_nh_dev == down_dev ||
4687 rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004688 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004689 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahern1cf844c2019-05-22 20:27:59 -07004690 if (iter->fib6_nh->fib_nh_dev == down_dev ||
4691 iter->fib6_nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004692 dead++;
4693
4694 return dead;
4695}
4696
David Ahern8d1c8022018-04-17 17:33:26 -07004697static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004698 const struct net_device *dev,
David Ahernecc56632019-04-23 08:48:09 -07004699 unsigned char nh_flags)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004700{
David Ahern8d1c8022018-04-17 17:33:26 -07004701 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004702
David Ahern1cf844c2019-05-22 20:27:59 -07004703 if (rt->fib6_nh->fib_nh_dev == dev)
4704 rt->fib6_nh->fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004705 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahern1cf844c2019-05-22 20:27:59 -07004706 if (iter->fib6_nh->fib_nh_dev == dev)
4707 iter->fib6_nh->fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004708}
4709
David Aherna1a22c12017-01-18 07:40:36 -08004710/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004711static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004712{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004713 const struct arg_netdev_event *arg = p_arg;
4714 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004715 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004716
David Ahernf88d8ea2019-06-03 20:19:52 -07004717 if (rt == net->ipv6.fib6_null_entry || rt->nh)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004718 return 0;
4719
4720 switch (arg->event) {
4721 case NETDEV_UNREGISTER:
David Ahern1cf844c2019-05-22 20:27:59 -07004722 return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004723 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004724 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004725 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004726 if (!rt->fib6_nsiblings)
David Ahern1cf844c2019-05-22 20:27:59 -07004727 return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004728 if (rt6_multipath_uses_dev(rt, dev)) {
4729 unsigned int count;
4730
4731 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004732 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004733 rt6_multipath_flush(rt);
4734 return -1;
4735 }
4736 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4737 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004738 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004739 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004740 }
4741 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004742 case NETDEV_CHANGE:
David Ahern1cf844c2019-05-22 20:27:59 -07004743 if (rt->fib6_nh->fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004744 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004745 break;
David Ahern1cf844c2019-05-22 20:27:59 -07004746 rt->fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004747 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004748 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004749 }
David S. Millerc159d302011-12-26 15:24:36 -05004750
Linus Torvalds1da177e2005-04-16 15:20:36 -07004751 return 0;
4752}
4753
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004754void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004755{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004756 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004757 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004758 {
4759 .event = event,
4760 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004761 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004762 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004763
David Ahern7c6bb7d2018-10-11 20:17:21 -07004764 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4765 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4766 else
4767 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004768}
4769
4770void rt6_disable_ip(struct net_device *dev, unsigned long event)
4771{
4772 rt6_sync_down_dev(dev, event);
4773 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4774 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004775}
4776
Eric Dumazet95c96172012-04-15 05:58:06 +00004777struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004778 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004779 unsigned int mtu;
David Ahernc0b220c2019-05-22 20:27:57 -07004780 struct fib6_info *f6i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004781};
4782
David Aherncc5c0732019-05-22 20:27:58 -07004783static int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg)
David Ahernc0b220c2019-05-22 20:27:57 -07004784{
4785 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *)_arg;
David Aherncc5c0732019-05-22 20:27:58 -07004786 struct fib6_info *f6i = arg->f6i;
David Ahernc0b220c2019-05-22 20:27:57 -07004787
4788 /* For administrative MTU increase, there is no way to discover
4789 * IPv6 PMTU increase, so PMTU increase should be updated here.
4790 * Since RFC 1981 doesn't include administrative MTU increase
4791 * update PMTU increase is a MUST. (i.e. jumbo frame)
4792 */
4793 if (nh->fib_nh_dev == arg->dev) {
4794 struct inet6_dev *idev = __in6_dev_get(arg->dev);
4795 u32 mtu = f6i->fib6_pmtu;
4796
4797 if (mtu >= arg->mtu ||
4798 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4799 fib6_metric_set(f6i, RTAX_MTU, arg->mtu);
4800
4801 spin_lock_bh(&rt6_exception_lock);
David Aherncc5c0732019-05-22 20:27:58 -07004802 rt6_exceptions_update_pmtu(idev, nh, arg->mtu);
David Ahernc0b220c2019-05-22 20:27:57 -07004803 spin_unlock_bh(&rt6_exception_lock);
4804 }
4805
4806 return 0;
4807}
4808
4809static int rt6_mtu_change_route(struct fib6_info *f6i, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004810{
4811 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4812 struct inet6_dev *idev;
4813
4814 /* In IPv6 pmtu discovery is not optional,
4815 so that RTAX_MTU lock cannot disable it.
4816 We still use this lock to block changes
4817 caused by addrconf/ndisc.
4818 */
4819
4820 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004821 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004822 return 0;
4823
David Ahernc0b220c2019-05-22 20:27:57 -07004824 if (fib6_metric_locked(f6i, RTAX_MTU))
4825 return 0;
David Ahernd4ead6b2018-04-17 17:33:16 -07004826
David Ahernc0b220c2019-05-22 20:27:57 -07004827 arg->f6i = f6i;
David Ahern2d442342019-06-08 14:53:31 -07004828 if (f6i->nh) {
4829 /* fib6_nh_mtu_change only returns 0, so this is safe */
4830 return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_mtu_change,
4831 arg);
4832 }
4833
David Ahern1cf844c2019-05-22 20:27:59 -07004834 return fib6_nh_mtu_change(f6i->fib6_nh, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004835}
4836
Eric Dumazet95c96172012-04-15 05:58:06 +00004837void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004838{
Thomas Grafc71099a2006-08-04 23:20:06 -07004839 struct rt6_mtu_change_arg arg = {
4840 .dev = dev,
4841 .mtu = mtu,
4842 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004843
Li RongQing0c3584d2013-12-27 16:32:38 +08004844 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004845}
4846
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004847static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
David Ahern75425652019-05-22 12:07:43 -07004848 [RTA_UNSPEC] = { .strict_start_type = RTA_DPORT + 1 },
Thomas Graf5176f912006-08-26 20:13:18 -07004849 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004850 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004851 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004852 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004853 [RTA_PRIORITY] = { .type = NLA_U32 },
4854 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004855 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004856 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004857 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4858 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004859 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004860 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004861 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004862 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004863 [RTA_IP_PROTO] = { .type = NLA_U8 },
4864 [RTA_SPORT] = { .type = NLA_U16 },
4865 [RTA_DPORT] = { .type = NLA_U16 },
David Ahern5b983242019-06-08 14:53:34 -07004866 [RTA_NH_ID] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004867};
4868
4869static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004870 struct fib6_config *cfg,
4871 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004872{
Thomas Graf86872cb2006-08-22 00:01:08 -07004873 struct rtmsg *rtm;
4874 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004875 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004876 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877
Johannes Berg8cb08172019-04-26 14:07:28 +02004878 err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
4879 rtm_ipv6_policy, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004880 if (err < 0)
4881 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004882
Thomas Graf86872cb2006-08-22 00:01:08 -07004883 err = -EINVAL;
4884 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004885
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004886 *cfg = (struct fib6_config){
4887 .fc_table = rtm->rtm_table,
4888 .fc_dst_len = rtm->rtm_dst_len,
4889 .fc_src_len = rtm->rtm_src_len,
4890 .fc_flags = RTF_UP,
4891 .fc_protocol = rtm->rtm_protocol,
4892 .fc_type = rtm->rtm_type,
4893
4894 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4895 .fc_nlinfo.nlh = nlh,
4896 .fc_nlinfo.nl_net = sock_net(skb->sk),
4897 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004898
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004899 if (rtm->rtm_type == RTN_UNREACHABLE ||
4900 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004901 rtm->rtm_type == RTN_PROHIBIT ||
4902 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004903 cfg->fc_flags |= RTF_REJECT;
4904
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004905 if (rtm->rtm_type == RTN_LOCAL)
4906 cfg->fc_flags |= RTF_LOCAL;
4907
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004908 if (rtm->rtm_flags & RTM_F_CLONED)
4909 cfg->fc_flags |= RTF_CACHE;
4910
David Ahernfc1e64e2018-01-25 16:55:09 -08004911 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4912
David Ahern5b983242019-06-08 14:53:34 -07004913 if (tb[RTA_NH_ID]) {
4914 if (tb[RTA_GATEWAY] || tb[RTA_OIF] ||
4915 tb[RTA_MULTIPATH] || tb[RTA_ENCAP]) {
4916 NL_SET_ERR_MSG(extack,
4917 "Nexthop specification and nexthop id are mutually exclusive");
4918 goto errout;
4919 }
4920 cfg->fc_nh_id = nla_get_u32(tb[RTA_NH_ID]);
4921 }
4922
Thomas Graf86872cb2006-08-22 00:01:08 -07004923 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004924 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004925 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004926 }
David Aherne3818542019-02-26 09:00:03 -08004927 if (tb[RTA_VIA]) {
4928 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4929 goto errout;
4930 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004931
4932 if (tb[RTA_DST]) {
4933 int plen = (rtm->rtm_dst_len + 7) >> 3;
4934
4935 if (nla_len(tb[RTA_DST]) < plen)
4936 goto errout;
4937
4938 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004939 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004940
4941 if (tb[RTA_SRC]) {
4942 int plen = (rtm->rtm_src_len + 7) >> 3;
4943
4944 if (nla_len(tb[RTA_SRC]) < plen)
4945 goto errout;
4946
4947 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004948 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004949
Daniel Walterc3968a82011-04-13 21:10:57 +00004950 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004951 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004952
Thomas Graf86872cb2006-08-22 00:01:08 -07004953 if (tb[RTA_OIF])
4954 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4955
4956 if (tb[RTA_PRIORITY])
4957 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4958
4959 if (tb[RTA_METRICS]) {
4960 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4961 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004962 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004963
4964 if (tb[RTA_TABLE])
4965 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4966
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004967 if (tb[RTA_MULTIPATH]) {
4968 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4969 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004970
4971 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004972 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004973 if (err < 0)
4974 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004975 }
4976
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004977 if (tb[RTA_PREF]) {
4978 pref = nla_get_u8(tb[RTA_PREF]);
4979 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4980 pref != ICMPV6_ROUTER_PREF_HIGH)
4981 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4982 cfg->fc_flags |= RTF_PREF(pref);
4983 }
4984
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004985 if (tb[RTA_ENCAP])
4986 cfg->fc_encap = tb[RTA_ENCAP];
4987
David Ahern9ed59592017-01-17 14:57:36 -08004988 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004989 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4990
David Ahernc255bd62017-05-27 16:19:27 -06004991 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004992 if (err < 0)
4993 goto errout;
4994 }
4995
Xin Long32bc2012015-12-16 17:50:11 +08004996 if (tb[RTA_EXPIRES]) {
4997 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4998
4999 if (addrconf_finite_timeout(timeout)) {
5000 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
5001 cfg->fc_flags |= RTF_EXPIRES;
5002 }
5003 }
5004
Thomas Graf86872cb2006-08-22 00:01:08 -07005005 err = 0;
5006errout:
5007 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005008}
5009
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005010struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07005011 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005012 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005013 struct list_head next;
5014};
5015
David Ahernd4ead6b2018-04-17 17:33:16 -07005016static int ip6_route_info_append(struct net *net,
5017 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07005018 struct fib6_info *rt,
5019 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005020{
5021 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005022 int err = -EEXIST;
5023
5024 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07005025 /* check if fib6_info already exists */
5026 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005027 return err;
5028 }
5029
5030 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
5031 if (!nh)
5032 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07005033 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005034 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
5035 list_add_tail(&nh->next, rt6_nh_list);
5036
5037 return 0;
5038}
5039
David Ahern8d1c8022018-04-17 17:33:26 -07005040static void ip6_route_mpath_notify(struct fib6_info *rt,
5041 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08005042 struct nl_info *info,
5043 __u16 nlflags)
5044{
5045 /* if this is an APPEND route, then rt points to the first route
5046 * inserted and rt_last points to last route inserted. Userspace
5047 * wants a consistent dump of the route which starts at the first
5048 * nexthop. Since sibling routes are always added at the end of
5049 * the list, find the first sibling of the last route appended
5050 */
David Ahern93c2fb22018-04-18 15:38:59 -07005051 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
5052 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07005053 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07005054 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08005055 }
5056
5057 if (rt)
5058 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
5059}
5060
Ido Schimmel0ee0f472019-12-23 15:28:15 +02005061static bool ip6_route_mpath_should_notify(const struct fib6_info *rt)
5062{
5063 bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
5064 bool should_notify = false;
5065 struct fib6_info *leaf;
5066 struct fib6_node *fn;
5067
5068 rcu_read_lock();
5069 fn = rcu_dereference(rt->fib6_node);
5070 if (!fn)
5071 goto out;
5072
5073 leaf = rcu_dereference(fn->leaf);
5074 if (!leaf)
5075 goto out;
5076
5077 if (rt == leaf ||
5078 (rt_can_ecmp && rt->fib6_metric == leaf->fib6_metric &&
5079 rt6_qualify_for_ecmp(leaf)))
5080 should_notify = true;
5081out:
5082 rcu_read_unlock();
5083
5084 return should_notify;
5085}
5086
David Ahern333c4302017-05-21 10:12:04 -06005087static int ip6_route_multipath_add(struct fib6_config *cfg,
5088 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005089{
David Ahern8d1c8022018-04-17 17:33:26 -07005090 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08005091 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005092 struct fib6_config r_cfg;
5093 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07005094 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005095 struct rt6_nh *err_nh;
5096 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08005097 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005098 int remaining;
5099 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005100 int err = 1;
5101 int nhn = 0;
5102 int replace = (cfg->fc_nlinfo.nlh &&
5103 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
5104 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005105
David Ahern3b1137f2017-02-02 12:37:10 -08005106 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
5107 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
5108 nlflags |= NLM_F_APPEND;
5109
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02005110 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005111 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005112
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005113 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07005114 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005115 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005116 while (rtnh_ok(rtnh, remaining)) {
5117 memcpy(&r_cfg, cfg, sizeof(*cfg));
5118 if (rtnh->rtnh_ifindex)
5119 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
5120
5121 attrlen = rtnh_attrlen(rtnh);
5122 if (attrlen > 0) {
5123 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
5124
5125 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
5126 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02005127 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005128 r_cfg.fc_flags |= RTF_GATEWAY;
5129 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005130 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
5131 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
5132 if (nla)
5133 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005134 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005135
David Ahern68e2ffd2018-03-20 10:06:59 -07005136 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07005137 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07005138 if (IS_ERR(rt)) {
5139 err = PTR_ERR(rt);
5140 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005141 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07005142 }
David Ahernb5d2d752018-07-15 09:35:19 -07005143 if (!rt6_qualify_for_ecmp(rt)) {
5144 err = -EINVAL;
5145 NL_SET_ERR_MSG(extack,
5146 "Device only routes can not be added for IPv6 using the multipath API.");
5147 fib6_info_release(rt);
5148 goto cleanup;
5149 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005150
David Ahern1cf844c2019-05-22 20:27:59 -07005151 rt->fib6_nh->fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02005152
David Ahernd4ead6b2018-04-17 17:33:16 -07005153 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
5154 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005155 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07005156 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005157 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005158 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005159
5160 rtnh = rtnh_next(rtnh, &remaining);
5161 }
5162
Ido Schimmel9eee3b42019-06-20 12:10:21 +03005163 if (list_empty(&rt6_nh_list)) {
5164 NL_SET_ERR_MSG(extack,
5165 "Invalid nexthop configuration - no valid nexthops");
5166 return -EINVAL;
5167 }
5168
David Ahern3b1137f2017-02-02 12:37:10 -08005169 /* for add and replace send one notification with all nexthops.
5170 * Skip the notification in fib6_add_rt2node and send one with
5171 * the full route when done
5172 */
5173 info->skip_notify = 1;
5174
Ido Schimmelebee3ca2019-06-18 18:12:48 +03005175 /* For add and replace, send one notification with all nexthops. For
5176 * append, send one notification with all appended nexthops.
5177 */
5178 info->skip_notify_kernel = 1;
5179
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005180 err_nh = NULL;
5181 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07005182 err = __ip6_ins_rt(nh->fib6_info, info, extack);
5183 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08005184
David Ahernf7225172018-06-04 13:41:42 -07005185 if (!err) {
5186 /* save reference to last route successfully inserted */
5187 rt_last = nh->fib6_info;
5188
5189 /* save reference to first route for notification */
5190 if (!rt_notif)
5191 rt_notif = nh->fib6_info;
5192 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005193
David Ahern8d1c8022018-04-17 17:33:26 -07005194 /* nh->fib6_info is used or freed at this point, reset to NULL*/
5195 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005196 if (err) {
5197 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08005198 NL_SET_ERR_MSG_MOD(extack,
5199 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005200 err_nh = nh;
5201 goto add_errout;
5202 }
5203
Nicolas Dichtel1a724182012-11-01 22:58:22 +00005204 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02005205 * these flags after the first nexthop: if there is a collision,
5206 * we have already failed to add the first nexthop:
5207 * fib6_add_rt2node() has rejected it; when replacing, old
5208 * nexthops have been replaced by first new, the rest should
5209 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00005210 */
Muhammad Usama Anjum864db232021-04-09 03:01:29 +05005211 if (cfg->fc_nlinfo.nlh) {
5212 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
5213 NLM_F_REPLACE);
5214 cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_CREATE;
5215 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005216 nhn++;
5217 }
5218
Ido Schimmel0ee0f472019-12-23 15:28:15 +02005219 /* An in-kernel notification should only be sent in case the new
5220 * multipath route is added as the first route in the node, or if
5221 * it was appended to it. We pass 'rt_notif' since it is the first
5222 * sibling and might allow us to skip some checks in the replace case.
5223 */
5224 if (ip6_route_mpath_should_notify(rt_notif)) {
5225 enum fib_event_type fib_event;
5226
5227 if (rt_notif->fib6_nsiblings != nhn - 1)
5228 fib_event = FIB_EVENT_ENTRY_APPEND;
5229 else
Ido Schimmelcaafb252019-12-23 15:28:20 +02005230 fib_event = FIB_EVENT_ENTRY_REPLACE;
Ido Schimmel0ee0f472019-12-23 15:28:15 +02005231
5232 err = call_fib6_multipath_entry_notifiers(info->nl_net,
5233 fib_event, rt_notif,
5234 nhn - 1, extack);
5235 if (err) {
5236 /* Delete all the siblings that were just added */
5237 err_nh = NULL;
5238 goto add_errout;
5239 }
5240 }
Ido Schimmelebee3ca2019-06-18 18:12:48 +03005241
David Ahern3b1137f2017-02-02 12:37:10 -08005242 /* success ... tell user about new route */
5243 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005244 goto cleanup;
5245
5246add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08005247 /* send notification for routes that were added so that
5248 * the delete notifications sent by ip6_route_del are
5249 * coherent
5250 */
5251 if (rt_notif)
5252 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
5253
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005254 /* Delete routes that were already added */
5255 list_for_each_entry(nh, &rt6_nh_list, next) {
5256 if (err_nh == nh)
5257 break;
David Ahern333c4302017-05-21 10:12:04 -06005258 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005259 }
5260
5261cleanup:
5262 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07005263 if (nh->fib6_info)
5264 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005265 list_del(&nh->next);
5266 kfree(nh);
5267 }
5268
5269 return err;
5270}
5271
David Ahern333c4302017-05-21 10:12:04 -06005272static int ip6_route_multipath_del(struct fib6_config *cfg,
5273 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005274{
5275 struct fib6_config r_cfg;
5276 struct rtnexthop *rtnh;
Colin Ian King22912672020-09-11 11:35:09 +01005277 int last_err = 0;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005278 int remaining;
5279 int attrlen;
Colin Ian King22912672020-09-11 11:35:09 +01005280 int err;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005281
5282 remaining = cfg->fc_mp_len;
5283 rtnh = (struct rtnexthop *)cfg->fc_mp;
5284
5285 /* Parse a Multipath Entry */
5286 while (rtnh_ok(rtnh, remaining)) {
5287 memcpy(&r_cfg, cfg, sizeof(*cfg));
5288 if (rtnh->rtnh_ifindex)
5289 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
5290
5291 attrlen = rtnh_attrlen(rtnh);
5292 if (attrlen > 0) {
5293 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
5294
5295 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
5296 if (nla) {
5297 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
5298 r_cfg.fc_flags |= RTF_GATEWAY;
5299 }
5300 }
David Ahern333c4302017-05-21 10:12:04 -06005301 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07005302 if (err)
5303 last_err = err;
5304
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005305 rtnh = rtnh_next(rtnh, &remaining);
5306 }
5307
5308 return last_err;
5309}
5310
David Ahernc21ef3e2017-04-16 09:48:24 -07005311static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
5312 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005313{
Thomas Graf86872cb2006-08-22 00:01:08 -07005314 struct fib6_config cfg;
5315 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005316
David Ahern333c4302017-05-21 10:12:04 -06005317 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07005318 if (err < 0)
5319 return err;
5320
David Ahern5b983242019-06-08 14:53:34 -07005321 if (cfg.fc_nh_id &&
5322 !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) {
5323 NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
5324 return -EINVAL;
5325 }
5326
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005327 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06005328 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08005329 else {
5330 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06005331 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08005332 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005333}
5334
David Ahernc21ef3e2017-04-16 09:48:24 -07005335static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
5336 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005337{
Thomas Graf86872cb2006-08-22 00:01:08 -07005338 struct fib6_config cfg;
5339 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005340
David Ahern333c4302017-05-21 10:12:04 -06005341 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07005342 if (err < 0)
5343 return err;
5344
David Ahern67f69512019-03-21 05:21:34 -07005345 if (cfg.fc_metric == 0)
5346 cfg.fc_metric = IP6_RT_PRIO_USER;
5347
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005348 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06005349 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00005350 else
David Ahernacb54e32018-04-17 17:33:22 -07005351 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005352}
5353
David Aherna1b7a1f2019-06-08 14:53:26 -07005354/* add the overhead of this fib6_nh to nexthop_len */
5355static int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg)
Thomas Graf339bf982006-11-10 14:10:15 -08005356{
David Aherna1b7a1f2019-06-08 14:53:26 -07005357 int *nexthop_len = arg;
David Ahernbeb1afac52017-02-02 12:37:09 -08005358
David Aherna1b7a1f2019-06-08 14:53:26 -07005359 *nexthop_len += nla_total_size(0) /* RTA_MULTIPATH */
5360 + NLA_ALIGN(sizeof(struct rtnexthop))
5361 + nla_total_size(16); /* RTA_GATEWAY */
David Ahernf88d8ea2019-06-03 20:19:52 -07005362
David Aherna1b7a1f2019-06-08 14:53:26 -07005363 if (nh->fib_nh_lws) {
5364 /* RTA_ENCAP_TYPE */
5365 *nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
5366 /* RTA_ENCAP */
5367 *nexthop_len += nla_total_size(2);
5368 }
David Ahernbeb1afac52017-02-02 12:37:09 -08005369
David Aherna1b7a1f2019-06-08 14:53:26 -07005370 return 0;
5371}
5372
5373static size_t rt6_nlmsg_size(struct fib6_info *f6i)
5374{
5375 int nexthop_len;
5376
5377 if (f6i->nh) {
5378 nexthop_len = nla_total_size(4); /* RTA_NH_ID */
5379 nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size,
5380 &nexthop_len);
5381 } else {
5382 struct fib6_nh *nh = f6i->fib6_nh;
5383
5384 nexthop_len = 0;
5385 if (f6i->fib6_nsiblings) {
5386 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
5387 + NLA_ALIGN(sizeof(struct rtnexthop))
5388 + nla_total_size(16) /* RTA_GATEWAY */
5389 + lwtunnel_get_encap_size(nh->fib_nh_lws);
5390
5391 nexthop_len *= f6i->fib6_nsiblings;
5392 }
5393 nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08005394 }
5395
Thomas Graf339bf982006-11-10 14:10:15 -08005396 return NLMSG_ALIGN(sizeof(struct rtmsg))
5397 + nla_total_size(16) /* RTA_SRC */
5398 + nla_total_size(16) /* RTA_DST */
5399 + nla_total_size(16) /* RTA_GATEWAY */
5400 + nla_total_size(16) /* RTA_PREFSRC */
5401 + nla_total_size(4) /* RTA_TABLE */
5402 + nla_total_size(4) /* RTA_IIF */
5403 + nla_total_size(4) /* RTA_OIF */
5404 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08005405 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01005406 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01005407 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005408 + nla_total_size(1) /* RTA_PREF */
David Ahernbeb1afac52017-02-02 12:37:09 -08005409 + nexthop_len;
5410}
5411
David Ahernf88d8ea2019-06-03 20:19:52 -07005412static int rt6_fill_node_nexthop(struct sk_buff *skb, struct nexthop *nh,
5413 unsigned char *flags)
5414{
5415 if (nexthop_is_multipath(nh)) {
5416 struct nlattr *mp;
5417
David Ahern4255ff02019-09-03 15:22:12 -07005418 mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
David Ahernf88d8ea2019-06-03 20:19:52 -07005419 if (!mp)
5420 goto nla_put_failure;
5421
Donald Sharp7bdf4de2019-09-04 10:11:58 -04005422 if (nexthop_mpath_fill_node(skb, nh, AF_INET6))
David Ahernf88d8ea2019-06-03 20:19:52 -07005423 goto nla_put_failure;
5424
5425 nla_nest_end(skb, mp);
5426 } else {
5427 struct fib6_nh *fib6_nh;
5428
5429 fib6_nh = nexthop_fib6_nh(nh);
Donald Sharp7bdf4de2019-09-04 10:11:58 -04005430 if (fib_nexthop_info(skb, &fib6_nh->nh_common, AF_INET6,
David Ahernf88d8ea2019-06-03 20:19:52 -07005431 flags, false) < 0)
5432 goto nla_put_failure;
5433 }
5434
5435 return 0;
5436
5437nla_put_failure:
5438 return -EMSGSIZE;
5439}
5440
David Ahernd4ead6b2018-04-17 17:33:16 -07005441static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07005442 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07005443 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00005444 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08005445 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005446{
Xin Long22d0bd82018-09-11 14:33:58 +08005447 struct rt6_info *rt6 = (struct rt6_info *)dst;
5448 struct rt6key *rt6_dst, *rt6_src;
5449 u32 *pmetrics, table, rt6_flags;
David Ahernf88d8ea2019-06-03 20:19:52 -07005450 unsigned char nh_flags = 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07005451 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08005452 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07005453 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005454
Eric W. Biederman15e47302012-09-07 20:12:54 +00005455 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05005456 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08005457 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07005458
Xin Long22d0bd82018-09-11 14:33:58 +08005459 if (rt6) {
5460 rt6_dst = &rt6->rt6i_dst;
5461 rt6_src = &rt6->rt6i_src;
5462 rt6_flags = rt6->rt6i_flags;
5463 } else {
5464 rt6_dst = &rt->fib6_dst;
5465 rt6_src = &rt->fib6_src;
5466 rt6_flags = rt->fib6_flags;
5467 }
5468
Thomas Graf2d7202b2006-08-22 00:01:27 -07005469 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005470 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08005471 rtm->rtm_dst_len = rt6_dst->plen;
5472 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005473 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07005474 if (rt->fib6_table)
5475 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07005476 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07005477 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08005478 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04005479 if (nla_put_u32(skb, RTA_TABLE, table))
5480 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07005481
5482 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005483 rtm->rtm_flags = 0;
5484 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07005485 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005486
Xin Long22d0bd82018-09-11 14:33:58 +08005487 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005488 rtm->rtm_flags |= RTM_F_CLONED;
5489
David Ahernd4ead6b2018-04-17 17:33:16 -07005490 if (dest) {
5491 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04005492 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005493 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005494 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08005495 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04005496 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005497#ifdef CONFIG_IPV6_SUBTREES
5498 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02005499 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04005500 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005501 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04005502 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08005503 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04005504 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005505#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09005506 if (iif) {
5507#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08005508 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08005509 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02005510
David Ahernfd61c6b2017-01-17 15:51:07 -08005511 if (err == 0)
5512 return 0;
5513 if (err < 0)
5514 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09005515 } else
5516#endif
David S. Millerc78679e2012-04-01 20:27:33 -04005517 if (nla_put_u32(skb, RTA_IIF, iif))
5518 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07005519 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005520 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07005521 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02005522 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04005523 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005524 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07005525
David Ahern93c2fb22018-04-18 15:38:59 -07005526 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00005527 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07005528 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02005529 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04005530 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00005531 }
5532
David Ahernd4ead6b2018-04-17 17:33:16 -07005533 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
5534 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07005535 goto nla_put_failure;
5536
David Ahern93c2fb22018-04-18 15:38:59 -07005537 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04005538 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00005539
David Ahernbeb1afac52017-02-02 12:37:09 -08005540 /* For multipath routes, walk the siblings list and add
5541 * each as a nexthop within RTA_MULTIPATH.
5542 */
Xin Long22d0bd82018-09-11 14:33:58 +08005543 if (rt6) {
5544 if (rt6_flags & RTF_GATEWAY &&
5545 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
5546 goto nla_put_failure;
5547
5548 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
5549 goto nla_put_failure;
Oliver Herms6b13d8f2020-11-19 00:06:51 +01005550
5551 if (dst->lwtstate &&
5552 lwtunnel_fill_encap(skb, dst->lwtstate, RTA_ENCAP, RTA_ENCAP_TYPE) < 0)
5553 goto nla_put_failure;
Xin Long22d0bd82018-09-11 14:33:58 +08005554 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07005555 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08005556 struct nlattr *mp;
5557
Michal Kubecekae0be8d2019-04-26 11:13:06 +02005558 mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
David Ahernbeb1afac52017-02-02 12:37:09 -08005559 if (!mp)
5560 goto nla_put_failure;
5561
David Ahern1cf844c2019-05-22 20:27:59 -07005562 if (fib_add_nexthop(skb, &rt->fib6_nh->nh_common,
Donald Sharp7bdf4de2019-09-04 10:11:58 -04005563 rt->fib6_nh->fib_nh_weight, AF_INET6) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08005564 goto nla_put_failure;
5565
5566 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07005567 &rt->fib6_siblings, fib6_siblings) {
David Ahern1cf844c2019-05-22 20:27:59 -07005568 if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common,
Donald Sharp7bdf4de2019-09-04 10:11:58 -04005569 sibling->fib6_nh->fib_nh_weight,
5570 AF_INET6) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08005571 goto nla_put_failure;
5572 }
5573
5574 nla_nest_end(skb, mp);
David Ahernf88d8ea2019-06-03 20:19:52 -07005575 } else if (rt->nh) {
5576 if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id))
5577 goto nla_put_failure;
David Ahernecc56632019-04-23 08:48:09 -07005578
David Ahernf88d8ea2019-06-03 20:19:52 -07005579 if (nexthop_is_blackhole(rt->nh))
5580 rtm->rtm_type = RTN_BLACKHOLE;
5581
Roopa Prabhu4f801162020-04-27 13:56:46 -07005582 if (net->ipv4.sysctl_nexthop_compat_mode &&
5583 rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0)
David Ahernf88d8ea2019-06-03 20:19:52 -07005584 goto nla_put_failure;
5585
5586 rtm->rtm_flags |= nh_flags;
5587 } else {
Donald Sharp7bdf4de2019-09-04 10:11:58 -04005588 if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, AF_INET6,
David Ahernecc56632019-04-23 08:48:09 -07005589 &nh_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08005590 goto nla_put_failure;
David Ahernecc56632019-04-23 08:48:09 -07005591
5592 rtm->rtm_flags |= nh_flags;
David Ahernbeb1afac52017-02-02 12:37:09 -08005593 }
5594
Xin Long22d0bd82018-09-11 14:33:58 +08005595 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07005596 expires = dst ? dst->expires : rt->expires;
5597 expires -= jiffies;
5598 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07005599
Ido Schimmelbb3c4ab2020-01-14 13:23:12 +02005600 if (!dst) {
5601 if (rt->offload)
5602 rtm->rtm_flags |= RTM_F_OFFLOAD;
5603 if (rt->trap)
5604 rtm->rtm_flags |= RTM_F_TRAP;
Amit Cohen0c5fcf92021-02-07 10:22:52 +02005605 if (rt->offload_failed)
5606 rtm->rtm_flags |= RTM_F_OFFLOAD_FAILED;
Ido Schimmelbb3c4ab2020-01-14 13:23:12 +02005607 }
5608
David Ahernd4ead6b2018-04-17 17:33:16 -07005609 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08005610 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005611
Xin Long22d0bd82018-09-11 14:33:58 +08005612 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01005613 goto nla_put_failure;
5614
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005615
Johannes Berg053c0952015-01-16 22:09:00 +01005616 nlmsg_end(skb, nlh);
5617 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07005618
5619nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08005620 nlmsg_cancel(skb, nlh);
5621 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005622}
5623
David Ahern2c170e02019-06-08 14:53:27 -07005624static int fib6_info_nh_uses_dev(struct fib6_nh *nh, void *arg)
5625{
5626 const struct net_device *dev = arg;
5627
5628 if (nh->fib_nh_dev == dev)
5629 return 1;
5630
5631 return 0;
5632}
5633
David Ahern13e38902018-10-15 18:56:44 -07005634static bool fib6_info_uses_dev(const struct fib6_info *f6i,
5635 const struct net_device *dev)
5636{
David Ahern2c170e02019-06-08 14:53:27 -07005637 if (f6i->nh) {
5638 struct net_device *_dev = (struct net_device *)dev;
5639
5640 return !!nexthop_for_each_fib6_nh(f6i->nh,
5641 fib6_info_nh_uses_dev,
5642 _dev);
5643 }
5644
David Ahern1cf844c2019-05-22 20:27:59 -07005645 if (f6i->fib6_nh->fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07005646 return true;
5647
5648 if (f6i->fib6_nsiblings) {
5649 struct fib6_info *sibling, *next_sibling;
5650
5651 list_for_each_entry_safe(sibling, next_sibling,
5652 &f6i->fib6_siblings, fib6_siblings) {
David Ahern1cf844c2019-05-22 20:27:59 -07005653 if (sibling->fib6_nh->fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07005654 return true;
5655 }
5656 }
5657
5658 return false;
5659}
5660
Stefano Brivio1e47b482019-06-21 17:45:27 +02005661struct fib6_nh_exception_dump_walker {
5662 struct rt6_rtnl_dump_arg *dump;
5663 struct fib6_info *rt;
5664 unsigned int flags;
5665 unsigned int skip;
5666 unsigned int count;
5667};
5668
5669static int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg)
5670{
5671 struct fib6_nh_exception_dump_walker *w = arg;
5672 struct rt6_rtnl_dump_arg *dump = w->dump;
5673 struct rt6_exception_bucket *bucket;
5674 struct rt6_exception *rt6_ex;
5675 int i, err;
5676
5677 bucket = fib6_nh_get_excptn_bucket(nh, NULL);
5678 if (!bucket)
5679 return 0;
5680
5681 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
5682 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
5683 if (w->skip) {
5684 w->skip--;
5685 continue;
5686 }
5687
5688 /* Expiration of entries doesn't bump sernum, insertion
5689 * does. Removal is triggered by insertion, so we can
5690 * rely on the fact that if entries change between two
5691 * partial dumps, this node is scanned again completely,
5692 * see rt6_insert_exception() and fib6_dump_table().
5693 *
5694 * Count expired entries we go through as handled
5695 * entries that we'll skip next time, in case of partial
5696 * node dump. Otherwise, if entries expire meanwhile,
5697 * we'll skip the wrong amount.
5698 */
5699 if (rt6_check_expired(rt6_ex->rt6i)) {
5700 w->count++;
5701 continue;
5702 }
5703
5704 err = rt6_fill_node(dump->net, dump->skb, w->rt,
5705 &rt6_ex->rt6i->dst, NULL, NULL, 0,
5706 RTM_NEWROUTE,
5707 NETLINK_CB(dump->cb->skb).portid,
5708 dump->cb->nlh->nlmsg_seq, w->flags);
5709 if (err)
5710 return err;
5711
5712 w->count++;
5713 }
5714 bucket++;
5715 }
5716
5717 return 0;
5718}
5719
Stefano Briviobf9a8a0612019-06-21 17:45:26 +02005720/* Return -1 if done with node, number of handled routes on partial dump */
Stefano Brivio1e47b482019-06-21 17:45:27 +02005721int rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005722{
5723 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07005724 struct fib_dump_filter *filter = &arg->filter;
5725 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08005726 struct net *net = arg->net;
Stefano Brivio1e47b482019-06-21 17:45:27 +02005727 int count = 0;
David Ahern1f17e2f2017-01-26 13:54:08 -08005728
David Ahern421842e2018-04-17 17:33:18 -07005729 if (rt == net->ipv6.fib6_null_entry)
Stefano Briviobf9a8a0612019-06-21 17:45:26 +02005730 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005731
David Ahern13e38902018-10-15 18:56:44 -07005732 if ((filter->flags & RTM_F_PREFIX) &&
5733 !(rt->fib6_flags & RTF_PREFIX_RT)) {
5734 /* success since this is not a prefix route */
Stefano Briviobf9a8a0612019-06-21 17:45:26 +02005735 return -1;
David Ahern13e38902018-10-15 18:56:44 -07005736 }
Stefano Brivio1e47b482019-06-21 17:45:27 +02005737 if (filter->filter_set &&
5738 ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
5739 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
5740 (filter->protocol && rt->fib6_protocol != filter->protocol))) {
5741 return -1;
5742 }
5743
5744 if (filter->filter_set ||
5745 !filter->dump_routes || !filter->dump_exceptions) {
David Ahern13e38902018-10-15 18:56:44 -07005746 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08005747 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005748
Stefano Brivio1e47b482019-06-21 17:45:27 +02005749 if (filter->dump_routes) {
5750 if (skip) {
5751 skip--;
5752 } else {
5753 if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL,
5754 0, RTM_NEWROUTE,
5755 NETLINK_CB(arg->cb->skb).portid,
5756 arg->cb->nlh->nlmsg_seq, flags)) {
5757 return 0;
5758 }
5759 count++;
5760 }
5761 }
5762
5763 if (filter->dump_exceptions) {
5764 struct fib6_nh_exception_dump_walker w = { .dump = arg,
5765 .rt = rt,
5766 .flags = flags,
5767 .skip = skip,
5768 .count = 0 };
5769 int err;
5770
Eric Dumazet3b525692019-06-26 03:05:28 -07005771 rcu_read_lock();
Stefano Brivio1e47b482019-06-21 17:45:27 +02005772 if (rt->nh) {
5773 err = nexthop_for_each_fib6_nh(rt->nh,
5774 rt6_nh_dump_exceptions,
5775 &w);
5776 } else {
5777 err = rt6_nh_dump_exceptions(rt->fib6_nh, &w);
5778 }
Eric Dumazet3b525692019-06-26 03:05:28 -07005779 rcu_read_unlock();
Stefano Brivio1e47b482019-06-21 17:45:27 +02005780
5781 if (err)
5782 return count += w.count;
5783 }
Stefano Briviobf9a8a0612019-06-21 17:45:26 +02005784
5785 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005786}
5787
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08005788static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
5789 const struct nlmsghdr *nlh,
5790 struct nlattr **tb,
5791 struct netlink_ext_ack *extack)
5792{
5793 struct rtmsg *rtm;
5794 int i, err;
5795
5796 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
5797 NL_SET_ERR_MSG_MOD(extack,
5798 "Invalid header for get route request");
5799 return -EINVAL;
5800 }
5801
5802 if (!netlink_strict_get_check(skb))
Johannes Berg8cb08172019-04-26 14:07:28 +02005803 return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
5804 rtm_ipv6_policy, extack);
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08005805
5806 rtm = nlmsg_data(nlh);
5807 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
5808 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
5809 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
5810 rtm->rtm_type) {
5811 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
5812 return -EINVAL;
5813 }
5814 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
5815 NL_SET_ERR_MSG_MOD(extack,
5816 "Invalid flags for get route request");
5817 return -EINVAL;
5818 }
5819
Johannes Berg8cb08172019-04-26 14:07:28 +02005820 err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
5821 rtm_ipv6_policy, extack);
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08005822 if (err)
5823 return err;
5824
5825 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
5826 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
5827 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
5828 return -EINVAL;
5829 }
5830
5831 for (i = 0; i <= RTA_MAX; i++) {
5832 if (!tb[i])
5833 continue;
5834
5835 switch (i) {
5836 case RTA_SRC:
5837 case RTA_DST:
5838 case RTA_IIF:
5839 case RTA_OIF:
5840 case RTA_MARK:
5841 case RTA_UID:
5842 case RTA_SPORT:
5843 case RTA_DPORT:
5844 case RTA_IP_PROTO:
5845 break;
5846 default:
5847 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
5848 return -EINVAL;
5849 }
5850 }
5851
5852 return 0;
5853}
5854
David Ahernc21ef3e2017-04-16 09:48:24 -07005855static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
5856 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005857{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09005858 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07005859 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005860 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07005861 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005862 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005863 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07005864 struct sk_buff *skb;
5865 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07005866 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005867 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07005868
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08005869 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07005870 if (err < 0)
5871 goto errout;
5872
5873 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02005874 rtm = nlmsg_data(nlh);
5875 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005876 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07005877
5878 if (tb[RTA_SRC]) {
5879 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
5880 goto errout;
5881
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00005882 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07005883 }
5884
5885 if (tb[RTA_DST]) {
5886 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
5887 goto errout;
5888
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00005889 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07005890 }
5891
5892 if (tb[RTA_IIF])
5893 iif = nla_get_u32(tb[RTA_IIF]);
5894
5895 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005896 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07005897
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07005898 if (tb[RTA_MARK])
5899 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
5900
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09005901 if (tb[RTA_UID])
5902 fl6.flowi6_uid = make_kuid(current_user_ns(),
5903 nla_get_u32(tb[RTA_UID]));
5904 else
5905 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
5906
Roopa Prabhueacb9382018-05-22 14:03:28 -07005907 if (tb[RTA_SPORT])
5908 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
5909
5910 if (tb[RTA_DPORT])
5911 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
5912
5913 if (tb[RTA_IP_PROTO]) {
5914 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08005915 &fl6.flowi6_proto, AF_INET6,
5916 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07005917 if (err)
5918 goto errout;
5919 }
5920
Thomas Grafab364a62006-08-22 00:01:47 -07005921 if (iif) {
5922 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005923 int flags = 0;
5924
Florian Westphal121622d2017-08-15 16:34:42 +02005925 rcu_read_lock();
5926
5927 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07005928 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02005929 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07005930 err = -ENODEV;
5931 goto errout;
5932 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005933
5934 fl6.flowi6_iif = iif;
5935
5936 if (!ipv6_addr_any(&fl6.saddr))
5937 flags |= RT6_LOOKUP_F_HAS_SADDR;
5938
David Ahernb75cc8f2018-03-02 08:32:17 -08005939 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02005940
5941 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005942 } else {
5943 fl6.flowi6_oif = oif;
5944
Ido Schimmel58acfd72017-12-20 12:28:25 +02005945 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005946 }
5947
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005948
5949 rt = container_of(dst, struct rt6_info, dst);
5950 if (rt->dst.error) {
5951 err = rt->dst.error;
5952 ip6_rt_put(rt);
5953 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07005954 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005955
WANG Cong9d6acb32017-03-01 20:48:39 -08005956 if (rt == net->ipv6.ip6_null_entry) {
5957 err = rt->dst.error;
5958 ip6_rt_put(rt);
5959 goto errout;
5960 }
5961
Linus Torvalds1da177e2005-04-16 15:20:36 -07005962 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05005963 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00005964 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07005965 err = -ENOBUFS;
5966 goto errout;
5967 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005968
Changli Gaod8d1f302010-06-10 23:31:35 -07005969 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07005970
5971 rcu_read_lock();
5972 from = rcu_dereference(rt->from);
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07005973 if (from) {
5974 if (fibmatch)
5975 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
5976 iif, RTM_NEWROUTE,
5977 NETLINK_CB(in_skb).portid,
5978 nlh->nlmsg_seq, 0);
5979 else
5980 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5981 &fl6.saddr, iif, RTM_NEWROUTE,
5982 NETLINK_CB(in_skb).portid,
5983 nlh->nlmsg_seq, 0);
5984 } else {
5985 err = -ENETUNREACH;
5986 }
David Aherna68886a2018-04-20 15:38:02 -07005987 rcu_read_unlock();
5988
Linus Torvalds1da177e2005-04-16 15:20:36 -07005989 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005990 kfree_skb(skb);
5991 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005992 }
5993
Eric W. Biederman15e47302012-09-07 20:12:54 +00005994 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005995errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005996 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005997}
5998
David Ahern8d1c8022018-04-17 17:33:26 -07005999void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07006000 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006001{
6002 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08006003 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08006004 u32 seq;
6005 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006006
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08006007 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05006008 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07006009
Roopa Prabhu19e42e42015-07-21 10:43:48 +02006010 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05006011 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07006012 goto errout;
6013
David Ahernd4ead6b2018-04-17 17:33:16 -07006014 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
6015 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08006016 if (err < 0) {
6017 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
6018 WARN_ON(err == -EMSGSIZE);
6019 kfree_skb(skb);
6020 goto errout;
6021 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00006022 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08006023 info->nlh, gfp_any());
6024 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07006025errout:
6026 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08006027 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006028}
6029
David Ahern19a3b7e2019-05-22 12:04:41 -07006030void fib6_rt_update(struct net *net, struct fib6_info *rt,
6031 struct nl_info *info)
6032{
6033 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
6034 struct sk_buff *skb;
6035 int err = -ENOBUFS;
6036
David Ahern19a3b7e2019-05-22 12:04:41 -07006037 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
6038 if (!skb)
6039 goto errout;
6040
6041 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
6042 RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE);
6043 if (err < 0) {
6044 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
6045 WARN_ON(err == -EMSGSIZE);
6046 kfree_skb(skb);
6047 goto errout;
6048 }
6049 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
6050 info->nlh, gfp_any());
6051 return;
6052errout:
6053 if (err < 0)
6054 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
6055}
6056
Amit Cohen907eea42021-02-01 21:47:55 +02006057void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
Amit Cohen0c5fcf92021-02-07 10:22:52 +02006058 bool offload, bool trap, bool offload_failed)
Amit Cohen907eea42021-02-01 21:47:55 +02006059{
6060 struct sk_buff *skb;
6061 int err;
6062
Amit Cohen0c5fcf92021-02-07 10:22:52 +02006063 if (f6i->offload == offload && f6i->trap == trap &&
6064 f6i->offload_failed == offload_failed)
Amit Cohen907eea42021-02-01 21:47:55 +02006065 return;
6066
6067 f6i->offload = offload;
6068 f6i->trap = trap;
Amit Cohen6fad3612021-02-07 10:22:53 +02006069
6070 /* 2 means send notifications only if offload_failed was changed. */
6071 if (net->ipv6.sysctl.fib_notify_on_flag_change == 2 &&
6072 f6i->offload_failed == offload_failed)
6073 return;
6074
Amit Cohen0c5fcf92021-02-07 10:22:52 +02006075 f6i->offload_failed = offload_failed;
Amit Cohen907eea42021-02-01 21:47:55 +02006076
6077 if (!rcu_access_pointer(f6i->fib6_node))
6078 /* The route was removed from the tree, do not send
Bhaskar Chowdhury89e83472021-03-27 04:42:41 +05306079 * notification.
Amit Cohen907eea42021-02-01 21:47:55 +02006080 */
6081 return;
6082
6083 if (!net->ipv6.sysctl.fib_notify_on_flag_change)
6084 return;
6085
6086 skb = nlmsg_new(rt6_nlmsg_size(f6i), GFP_KERNEL);
6087 if (!skb) {
6088 err = -ENOBUFS;
6089 goto errout;
6090 }
6091
6092 err = rt6_fill_node(net, skb, f6i, NULL, NULL, NULL, 0, RTM_NEWROUTE, 0,
6093 0, 0);
6094 if (err < 0) {
6095 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
6096 WARN_ON(err == -EMSGSIZE);
6097 kfree_skb(skb);
6098 goto errout;
6099 }
6100
6101 rtnl_notify(skb, net, 0, RTNLGRP_IPV6_ROUTE, NULL, GFP_KERNEL);
6102 return;
6103
6104errout:
6105 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
6106}
6107EXPORT_SYMBOL(fib6_info_hw_flags_set);
6108
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006109static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00006110 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006111{
Jiri Pirko351638e2013-05-28 01:30:21 +00006112 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09006113 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006114
WANG Cong242d3a42017-05-08 10:12:13 -07006115 if (!(dev->flags & IFF_LOOPBACK))
6116 return NOTIFY_OK;
6117
6118 if (event == NETDEV_REGISTER) {
David Ahern1cf844c2019-05-22 20:27:59 -07006119 net->ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07006120 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006121 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
6122#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07006123 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006124 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07006125 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006126 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
6127#endif
WANG Cong76da0702017-06-20 11:42:27 -07006128 } else if (event == NETDEV_UNREGISTER &&
6129 dev->reg_state != NETREG_UNREGISTERED) {
6130 /* NETDEV_UNREGISTER could be fired for multiple times by
6131 * netdev_wait_allrefs(). Make sure we only call this once.
6132 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07006133 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07006134#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07006135 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
6136 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07006137#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006138 }
6139
6140 return NOTIFY_OK;
6141}
6142
Linus Torvalds1da177e2005-04-16 15:20:36 -07006143/*
6144 * /proc
6145 */
6146
6147#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07006148static int rt6_stats_seq_show(struct seq_file *seq, void *v)
6149{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08006150 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006151 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08006152 net->ipv6.rt6_stats->fib_nodes,
6153 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07006154 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08006155 net->ipv6.rt6_stats->fib_rt_entries,
6156 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00006157 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08006158 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006159
6160 return 0;
6161}
Linus Torvalds1da177e2005-04-16 15:20:36 -07006162#endif /* CONFIG_PROC_FS */
6163
6164#ifdef CONFIG_SYSCTL
6165
Christoph Hellwig32927392020-04-24 08:43:38 +02006166static int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
6167 void *buffer, size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006168{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00006169 struct net *net;
6170 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06006171 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00006172 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006173 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00006174
6175 net = (struct net *)ctl->extra1;
6176 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06006177 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
6178 if (ret)
6179 return ret;
6180
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02006181 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00006182 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006183}
6184
David Aherned792e22018-10-08 14:06:34 -07006185static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006186 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006187 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08006188 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006189 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07006190 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006191 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07006192 },
6193 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006194 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08006195 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006196 .maxlen = sizeof(int),
6197 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006198 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006199 },
6200 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006201 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08006202 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006203 .maxlen = sizeof(int),
6204 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006205 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006206 },
6207 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006208 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08006209 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006210 .maxlen = sizeof(int),
6211 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006212 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006213 },
6214 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006215 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08006216 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006217 .maxlen = sizeof(int),
6218 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006219 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006220 },
6221 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006222 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08006223 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006224 .maxlen = sizeof(int),
6225 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006226 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006227 },
6228 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006229 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08006230 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006231 .maxlen = sizeof(int),
6232 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07006233 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006234 },
6235 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006236 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08006237 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006238 .maxlen = sizeof(int),
6239 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006240 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006241 },
6242 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006243 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08006244 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006245 .maxlen = sizeof(int),
6246 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07006247 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006248 },
6249 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006250 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08006251 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006252 .maxlen = sizeof(int),
6253 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08006254 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07006255 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07006256 {
6257 .procname = "skip_notify_on_dev_down",
6258 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
6259 .maxlen = sizeof(int),
6260 .mode = 0644,
Eiichi Tsukatab8e8a862019-06-25 12:08:01 +09006261 .proc_handler = proc_dointvec_minmax,
Matteo Croceeec48442019-07-18 15:58:50 -07006262 .extra1 = SYSCTL_ZERO,
6263 .extra2 = SYSCTL_ONE,
David Ahern7c6bb7d2018-10-11 20:17:21 -07006264 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08006265 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006266};
6267
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00006268struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08006269{
6270 struct ctl_table *table;
6271
6272 table = kmemdup(ipv6_route_table_template,
6273 sizeof(ipv6_route_table_template),
6274 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09006275
6276 if (table) {
6277 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00006278 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00006279 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09006280 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
6281 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
6282 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
6283 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
6284 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
6285 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
6286 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08006287 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07006288 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00006289
6290 /* Don't export sysctls to unprivileged users */
6291 if (net->user_ns != &init_user_ns)
6292 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09006293 }
6294
Daniel Lezcano760f2d02008-01-10 02:53:43 -08006295 return table;
6296}
Linus Torvalds1da177e2005-04-16 15:20:36 -07006297#endif
6298
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00006299static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08006300{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07006301 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006302
Alexey Dobriyan86393e52009-08-29 01:34:49 +00006303 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
6304 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08006305
Eric Dumazetfc66f952010-10-08 06:37:34 +00006306 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
6307 goto out_ip6_dst_ops;
6308
David Ahern1cf844c2019-05-22 20:27:59 -07006309 net->ipv6.fib6_null_entry = fib6_info_alloc(GFP_KERNEL, true);
David Ahern421842e2018-04-17 17:33:18 -07006310 if (!net->ipv6.fib6_null_entry)
6311 goto out_ip6_dst_entries;
David Ahern1cf844c2019-05-22 20:27:59 -07006312 memcpy(net->ipv6.fib6_null_entry, &fib6_null_entry_template,
6313 sizeof(*net->ipv6.fib6_null_entry));
David Ahern421842e2018-04-17 17:33:18 -07006314
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006315 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
6316 sizeof(*net->ipv6.ip6_null_entry),
6317 GFP_KERNEL);
6318 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07006319 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07006320 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08006321 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
6322 ip6_template_metrics, true);
Wei Wang74109212019-06-20 17:36:38 -07006323 INIT_LIST_HEAD(&net->ipv6.ip6_null_entry->rt6i_uncached);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006324
6325#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02006326 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006327 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
6328 sizeof(*net->ipv6.ip6_prohibit_entry),
6329 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07006330 if (!net->ipv6.ip6_prohibit_entry)
6331 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07006332 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08006333 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
6334 ip6_template_metrics, true);
Wei Wang74109212019-06-20 17:36:38 -07006335 INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006336
6337 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
6338 sizeof(*net->ipv6.ip6_blk_hole_entry),
6339 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07006340 if (!net->ipv6.ip6_blk_hole_entry)
6341 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07006342 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08006343 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
6344 ip6_template_metrics, true);
Wei Wang74109212019-06-20 17:36:38 -07006345 INIT_LIST_HEAD(&net->ipv6.ip6_blk_hole_entry->rt6i_uncached);
Paolo Abenib9b33e72019-11-20 13:47:34 +01006346#ifdef CONFIG_IPV6_SUBTREES
6347 net->ipv6.fib6_routes_require_src = 0;
6348#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006349#endif
6350
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07006351 net->ipv6.sysctl.flush_delay = 0;
6352 net->ipv6.sysctl.ip6_rt_max_size = 4096;
6353 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
6354 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
6355 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
6356 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
6357 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
6358 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07006359 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07006360
Benjamin Thery6891a342008-03-04 13:49:47 -08006361 net->ipv6.ip6_rt_gc_expire = 30*HZ;
6362
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006363 ret = 0;
6364out:
6365 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08006366
Peter Zijlstra68fffc62008-10-07 14:12:10 -07006367#ifdef CONFIG_IPV6_MULTIPLE_TABLES
6368out_ip6_prohibit_entry:
6369 kfree(net->ipv6.ip6_prohibit_entry);
6370out_ip6_null_entry:
6371 kfree(net->ipv6.ip6_null_entry);
6372#endif
David Ahern421842e2018-04-17 17:33:18 -07006373out_fib6_null_entry:
6374 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00006375out_ip6_dst_entries:
6376 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08006377out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08006378 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08006379}
6380
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00006381static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08006382{
David Ahern421842e2018-04-17 17:33:18 -07006383 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006384 kfree(net->ipv6.ip6_null_entry);
6385#ifdef CONFIG_IPV6_MULTIPLE_TABLES
6386 kfree(net->ipv6.ip6_prohibit_entry);
6387 kfree(net->ipv6.ip6_blk_hole_entry);
6388#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00006389 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08006390}
6391
Thomas Grafd1896342012-06-18 12:08:33 +00006392static int __net_init ip6_route_net_init_late(struct net *net)
6393{
6394#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02006395 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
6396 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02006397 proc_create_net_single("rt6_stats", 0444, net->proc_net,
6398 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00006399#endif
6400 return 0;
6401}
6402
6403static void __net_exit ip6_route_net_exit_late(struct net *net)
6404{
6405#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00006406 remove_proc_entry("ipv6_route", net->proc_net);
6407 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00006408#endif
6409}
6410
Daniel Lezcanocdb18762008-03-04 13:45:33 -08006411static struct pernet_operations ip6_route_net_ops = {
6412 .init = ip6_route_net_init,
6413 .exit = ip6_route_net_exit,
6414};
6415
David S. Millerc3426b42012-06-09 16:27:05 -07006416static int __net_init ipv6_inetpeer_init(struct net *net)
6417{
6418 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
6419
6420 if (!bp)
6421 return -ENOMEM;
6422 inet_peer_base_init(bp);
6423 net->ipv6.peers = bp;
6424 return 0;
6425}
6426
6427static void __net_exit ipv6_inetpeer_exit(struct net *net)
6428{
6429 struct inet_peer_base *bp = net->ipv6.peers;
6430
6431 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07006432 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07006433 kfree(bp);
6434}
6435
David S. Miller2b823f72012-06-09 19:00:16 -07006436static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07006437 .init = ipv6_inetpeer_init,
6438 .exit = ipv6_inetpeer_exit,
6439};
6440
Thomas Grafd1896342012-06-18 12:08:33 +00006441static struct pernet_operations ip6_route_net_late_ops = {
6442 .init = ip6_route_net_init_late,
6443 .exit = ip6_route_net_exit_late,
6444};
6445
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006446static struct notifier_block ip6_route_dev_notifier = {
6447 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07006448 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006449};
6450
WANG Cong2f460932017-05-03 22:07:31 -07006451void __init ip6_route_init_special_entries(void)
6452{
6453 /* Registering of the loopback is done before this portion of code,
6454 * the loopback reference in rt6_info will not be taken, do it
6455 * manually for init_net */
David Ahern1cf844c2019-05-22 20:27:59 -07006456 init_net.ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07006457 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
6458 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
6459 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
6460 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
6461 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
6462 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
6463 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
6464 #endif
6465}
6466
Yonghong Song138d0be2020-05-09 10:59:10 -07006467#if IS_BUILTIN(CONFIG_IPV6)
6468#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
6469DEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt)
6470
Yonghong Song951cf362020-07-20 09:34:03 -07006471BTF_ID_LIST(btf_fib6_info_id)
6472BTF_ID(struct, fib6_info)
6473
Yonghong Song14fc6bd62020-07-23 11:41:09 -07006474static const struct bpf_iter_seq_info ipv6_route_seq_info = {
Yonghong Song15172a42020-05-13 11:02:19 -07006475 .seq_ops = &ipv6_route_seq_ops,
6476 .init_seq_private = bpf_iter_init_seq_net,
6477 .fini_seq_private = bpf_iter_fini_seq_net,
6478 .seq_priv_size = sizeof(struct ipv6_route_iter),
Yonghong Song14fc6bd62020-07-23 11:41:09 -07006479};
6480
6481static struct bpf_iter_reg ipv6_route_reg_info = {
6482 .target = "ipv6_route",
Yonghong Song3c32cc12020-05-13 11:02:21 -07006483 .ctx_arg_info_size = 1,
6484 .ctx_arg_info = {
6485 { offsetof(struct bpf_iter__ipv6_route, rt),
6486 PTR_TO_BTF_ID_OR_NULL },
6487 },
Yonghong Song14fc6bd62020-07-23 11:41:09 -07006488 .seq_info = &ipv6_route_seq_info,
Yonghong Song15172a42020-05-13 11:02:19 -07006489};
6490
Yonghong Song138d0be2020-05-09 10:59:10 -07006491static int __init bpf_iter_register(void)
6492{
Yonghong Song951cf362020-07-20 09:34:03 -07006493 ipv6_route_reg_info.ctx_arg_info[0].btf_id = *btf_fib6_info_id;
Yonghong Song15172a42020-05-13 11:02:19 -07006494 return bpf_iter_reg_target(&ipv6_route_reg_info);
Yonghong Song138d0be2020-05-09 10:59:10 -07006495}
6496
6497static void bpf_iter_unregister(void)
6498{
Yonghong Songab2ee4f2020-05-13 11:02:20 -07006499 bpf_iter_unreg_target(&ipv6_route_reg_info);
Yonghong Song138d0be2020-05-09 10:59:10 -07006500}
6501#endif
6502#endif
6503
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006504int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006505{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006506 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07006507 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006508
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08006509 ret = -ENOMEM;
6510 ip6_dst_ops_template.kmem_cachep =
6511 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
6512 SLAB_HWCACHE_ALIGN, NULL);
6513 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08006514 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07006515
Eric Dumazetfc66f952010-10-08 06:37:34 +00006516 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006517 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08006518 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08006519
David S. Millerc3426b42012-06-09 16:27:05 -07006520 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
6521 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07006522 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00006523
David S. Miller7e52b332012-06-15 15:51:55 -07006524 ret = register_pernet_subsys(&ip6_route_net_ops);
6525 if (ret)
6526 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07006527
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07006528 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
6529
David S. Millere8803b62012-06-16 01:12:19 -07006530 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006531 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006532 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006533
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006534 ret = xfrm6_init();
6535 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07006536 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08006537
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006538 ret = fib6_rules_init();
6539 if (ret)
6540 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08006541
Thomas Grafd1896342012-06-18 12:08:33 +00006542 ret = register_pernet_subsys(&ip6_route_net_late_ops);
6543 if (ret)
6544 goto fib6_rules_init;
6545
Florian Westphal16feebc2017-12-02 21:44:08 +01006546 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
6547 inet6_rtm_newroute, NULL, 0);
6548 if (ret < 0)
6549 goto out_register_late_subsys;
6550
6551 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
6552 inet6_rtm_delroute, NULL, 0);
6553 if (ret < 0)
6554 goto out_register_late_subsys;
6555
6556 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
6557 inet6_rtm_getroute, NULL,
6558 RTNL_FLAG_DOIT_UNLOCKED);
6559 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00006560 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006561
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006562 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08006563 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00006564 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006565
Yonghong Song138d0be2020-05-09 10:59:10 -07006566#if IS_BUILTIN(CONFIG_IPV6)
6567#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
6568 ret = bpf_iter_register();
6569 if (ret)
6570 goto out_register_late_subsys;
6571#endif
6572#endif
6573
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07006574 for_each_possible_cpu(cpu) {
6575 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
6576
6577 INIT_LIST_HEAD(&ul->head);
6578 spin_lock_init(&ul->lock);
6579 }
6580
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006581out:
6582 return ret;
6583
Thomas Grafd1896342012-06-18 12:08:33 +00006584out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01006585 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00006586 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006587fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006588 fib6_rules_cleanup();
6589xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006590 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00006591out_fib6_init:
6592 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006593out_register_subsys:
6594 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07006595out_register_inetpeer:
6596 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00006597out_dst_entries:
6598 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006599out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08006600 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08006601 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006602}
6603
6604void ip6_route_cleanup(void)
6605{
Yonghong Song138d0be2020-05-09 10:59:10 -07006606#if IS_BUILTIN(CONFIG_IPV6)
6607#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
6608 bpf_iter_unregister();
6609#endif
6610#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006611 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00006612 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07006613 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07006614 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07006615 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07006616 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08006617 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00006618 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08006619 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006620}