blob: 848e944f07df76803289708a1b16ceffcb734624 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Wei Wang35732d02017-10-06 12:05:57 -070047#include <linux/jhash.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020048#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <net/snmp.h>
50#include <net/ipv6.h>
51#include <net/ip6_fib.h>
52#include <net/ip6_route.h>
53#include <net/ndisc.h>
54#include <net/addrconf.h>
55#include <net/tcp.h>
56#include <linux/rtnetlink.h>
57#include <net/dst.h>
Jiri Benc904af042015-08-20 13:56:31 +020058#include <net/dst_metadata.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070060#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070061#include <net/netlink.h>
David Ahern3c618c12019-04-20 09:28:20 -070062#include <net/rtnh.h>
Roopa Prabhu19e42e42015-07-21 10:43:48 +020063#include <net/lwtunnel.h>
Jiri Benc904af042015-08-20 13:56:31 +020064#include <net/ip_tunnels.h>
David Ahernca254492015-10-12 11:47:10 -070065#include <net/l3mdev.h>
Roopa Prabhueacb9382018-05-22 14:03:28 -070066#include <net/ip.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080067#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69#ifdef CONFIG_SYSCTL
70#include <linux/sysctl.h>
71#endif
72
David Ahern30d444d2018-05-23 17:08:48 -070073static int ip6_rt_type_to_error(u8 fib6_type);
74
75#define CREATE_TRACE_POINTS
76#include <trace/events/fib6.h>
77EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
78#undef CREATE_TRACE_POINTS
79
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020080enum rt6_nud_state {
Jiri Benc7e980562013-12-11 13:48:20 +010081 RT6_NUD_FAIL_HARD = -3,
82 RT6_NUD_FAIL_PROBE = -2,
83 RT6_NUD_FAIL_DO_RR = -1,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020084 RT6_NUD_SUCCEED = 1
85};
86
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080088static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000089static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090static struct dst_entry *ip6_negative_advice(struct dst_entry *);
91static void ip6_dst_destroy(struct dst_entry *);
92static void ip6_dst_ifdown(struct dst_entry *,
93 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080094static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
96static int ip6_pkt_discard(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050097static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Kamala R7150aed2013-12-02 19:55:21 +053098static int ip6_pkt_prohibit(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050099static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100static void ip6_link_failure(struct sk_buff *skb);
David S. Miller6700c272012-07-17 03:29:28 -0700101static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
102 struct sk_buff *skb, u32 mtu);
103static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
104 struct sk_buff *skb);
David Ahern702cea52019-04-09 14:41:13 -0700105static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
106 int strict);
David Ahern8d1c8022018-04-17 17:33:26 -0700107static size_t rt6_nlmsg_size(struct fib6_info *rt);
David Ahernd4ead6b2018-04-17 17:33:16 -0700108static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -0700109 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -0700110 struct in6_addr *dest, struct in6_addr *src,
David Ahern16a16cd2017-02-02 12:37:11 -0800111 int iif, int type, u32 portid, u32 seq,
112 unsigned int flags);
David Ahern7e4b5122019-04-16 14:36:00 -0700113static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei 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) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700183 rt->dst.dev = loopback_dev;
184 dev_hold(rt->dst.dev);
185 dev_put(rt_dev);
186 }
187 }
188 spin_unlock_bh(&ul->lock);
189 }
190}
191
David Ahernf8a1b432018-04-17 17:33:21 -0700192static inline const void *choose_neigh_daddr(const struct in6_addr *p,
David S. Millerf894cbf2012-07-02 21:52:24 -0700193 struct sk_buff *skb,
194 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500195{
David S. Millera7563f32012-01-26 16:29:16 -0500196 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500197 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700198 else if (skb)
199 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500200 return daddr;
201}
202
David Ahernf8a1b432018-04-17 17:33:21 -0700203struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
204 struct net_device *dev,
205 struct sk_buff *skb,
206 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700207{
David S. Miller39232972012-01-26 15:22:32 -0500208 struct neighbour *n;
209
David Ahernf8a1b432018-04-17 17:33:21 -0700210 daddr = choose_neigh_daddr(gw, skb, daddr);
211 n = __ipv6_neigh_lookup(dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500212 if (n)
213 return n;
Stefano Brivio7adf3242019-01-02 13:29:27 +0100214
215 n = neigh_create(&nd_tbl, daddr, dev);
216 return IS_ERR(n) ? NULL : n;
David Ahernf8a1b432018-04-17 17:33:21 -0700217}
218
219static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
220 struct sk_buff *skb,
221 const void *daddr)
222{
223 const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
224
225 return ip6_neigh_lookup(&rt->rt6i_gateway, dst->dev, skb, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500226}
227
Julian Anastasov63fca652017-02-06 23:14:15 +0200228static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
229{
230 struct net_device *dev = dst->dev;
231 struct rt6_info *rt = (struct rt6_info *)dst;
232
David Ahernf8a1b432018-04-17 17:33:21 -0700233 daddr = choose_neigh_daddr(&rt->rt6i_gateway, NULL, daddr);
Julian Anastasov63fca652017-02-06 23:14:15 +0200234 if (!daddr)
235 return;
236 if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
237 return;
238 if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
239 return;
240 __ipv6_confirm_neigh(dev, daddr);
241}
242
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800243static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 .gc = ip6_dst_gc,
246 .gc_thresh = 1024,
247 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800248 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000249 .mtu = ip6_mtu,
David Ahernd4ead6b2018-04-17 17:33:16 -0700250 .cow_metrics = dst_cow_metrics_generic,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 .destroy = ip6_dst_destroy,
252 .ifdown = ip6_dst_ifdown,
253 .negative_advice = ip6_negative_advice,
254 .link_failure = ip6_link_failure,
255 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700256 .redirect = rt6_do_redirect,
Eric W. Biederman9f8955c2015-10-07 16:48:39 -0500257 .local_out = __ip6_local_out,
David Ahernf8a1b432018-04-17 17:33:21 -0700258 .neigh_lookup = ip6_dst_neigh_lookup,
Julian Anastasov63fca652017-02-06 23:14:15 +0200259 .confirm_neigh = ip6_confirm_neigh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260};
261
Steffen Klassertebb762f2011-11-23 02:12:51 +0000262static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800263{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000264 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
265
266 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800267}
268
David S. Miller6700c272012-07-17 03:29:28 -0700269static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
270 struct sk_buff *skb, u32 mtu)
David S. Miller14e50e52007-05-24 18:17:54 -0700271{
272}
273
David S. Miller6700c272012-07-17 03:29:28 -0700274static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
275 struct sk_buff *skb)
David S. Millerb587ee32012-07-12 00:39:24 -0700276{
277}
278
David S. Miller14e50e52007-05-24 18:17:54 -0700279static struct dst_ops ip6_dst_blackhole_ops = {
280 .family = AF_INET6,
David S. Miller14e50e52007-05-24 18:17:54 -0700281 .destroy = ip6_dst_destroy,
282 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000283 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800284 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700285 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700286 .redirect = ip6_rt_blackhole_redirect,
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -0700287 .cow_metrics = dst_cow_metrics_generic,
David Ahernf8a1b432018-04-17 17:33:21 -0700288 .neigh_lookup = ip6_dst_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700289};
290
David S. Miller62fa8a82011-01-26 20:51:05 -0800291static const u32 ip6_template_metrics[RTAX_MAX] = {
Li RongQing14edd872012-10-24 14:01:18 +0800292 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800293};
294
David Ahern8d1c8022018-04-17 17:33:26 -0700295static const struct fib6_info fib6_null_entry_template = {
David Ahern93c2fb22018-04-18 15:38:59 -0700296 .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP),
297 .fib6_protocol = RTPROT_KERNEL,
298 .fib6_metric = ~(u32)0,
Eric Dumazetf05713e2019-04-22 18:35:03 -0700299 .fib6_ref = REFCOUNT_INIT(1),
David Ahern421842e2018-04-17 17:33:18 -0700300 .fib6_type = RTN_UNREACHABLE,
301 .fib6_metrics = (struct dst_metrics *)&dst_default_metrics,
302};
303
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000304static const struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700305 .dst = {
306 .__refcnt = ATOMIC_INIT(1),
307 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000308 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700309 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700310 .input = ip6_pkt_discard,
311 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 },
313 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314};
315
Thomas Graf101367c2006-08-04 03:39:02 -0700316#ifdef CONFIG_IPV6_MULTIPLE_TABLES
317
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000318static const struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700319 .dst = {
320 .__refcnt = ATOMIC_INIT(1),
321 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000322 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700323 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700324 .input = ip6_pkt_prohibit,
325 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700326 },
327 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700328};
329
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000330static const struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700331 .dst = {
332 .__refcnt = ATOMIC_INIT(1),
333 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000334 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700335 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700336 .input = dst_discard,
Eric W. Biedermanede20592015-10-07 16:48:47 -0500337 .output = dst_discard_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700338 },
339 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700340};
341
342#endif
343
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700344static void rt6_info_init(struct rt6_info *rt)
345{
346 struct dst_entry *dst = &rt->dst;
347
348 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700349 INIT_LIST_HEAD(&rt->rt6i_uncached);
350}
351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352/* allocate dst with ip6_dst_ops */
David Ahern93531c62018-04-17 17:33:25 -0700353struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
354 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
David S. Miller97bab732012-06-09 22:36:36 -0700356 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
Wei Wangb2a9c0e2017-06-17 10:42:41 -0700357 1, DST_OBSOLETE_FORCE_CHK, flags);
David S. Millercf911662011-04-28 14:31:47 -0700358
Wei Wang81eb8442017-10-06 12:06:11 -0700359 if (rt) {
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700360 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -0700361 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
362 }
Steffen Klassert81048912012-07-05 23:37:09 +0000363
David S. Millercf911662011-04-28 14:31:47 -0700364 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365}
David Ahern9ab179d2016-04-07 11:10:06 -0700366EXPORT_SYMBOL(ip6_dst_alloc);
Martin KaFai Laud52d3992015-05-22 20:56:06 -0700367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368static void ip6_dst_destroy(struct dst_entry *dst)
369{
370 struct rt6_info *rt = (struct rt6_info *)dst;
David Aherna68886a2018-04-20 15:38:02 -0700371 struct fib6_info *from;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700372 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
David Ahern1620a332018-10-04 20:07:54 -0700374 ip_dst_metrics_put(dst);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700375 rt6_uncached_list_del(rt);
376
377 idev = rt->rt6i_idev;
David S. Miller38308472011-12-03 18:02:47 -0500378 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 rt->rt6i_idev = NULL;
380 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900381 }
Gao feng1716a962012-04-06 00:13:10 +0000382
Eric Dumazet0e233872019-04-28 12:22:25 -0700383 from = xchg((__force struct fib6_info **)&rt->from, NULL);
David Ahern93531c62018-04-17 17:33:25 -0700384 fib6_info_release(from);
David S. Millerb3419362010-11-30 12:27:11 -0800385}
386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
388 int how)
389{
390 struct rt6_info *rt = (struct rt6_info *)dst;
391 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800392 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900393 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
Wei Wange5645f52017-08-14 10:44:59 -0700395 if (idev && idev->dev != loopback_dev) {
396 struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev);
397 if (loopback_idev) {
398 rt->rt6i_idev = loopback_idev;
399 in6_dev_put(idev);
David S. Miller97cac082012-07-02 22:43:47 -0700400 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 }
402}
403
Martin KaFai Lau5973fb12015-11-11 11:51:07 -0800404static bool __rt6_check_expired(const struct rt6_info *rt)
405{
406 if (rt->rt6i_flags & RTF_EXPIRES)
407 return time_after(jiffies, rt->dst.expires);
408 else
409 return false;
410}
411
Eric Dumazeta50feda2012-05-18 18:57:34 +0000412static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413{
David Aherna68886a2018-04-20 15:38:02 -0700414 struct fib6_info *from;
415
416 from = rcu_dereference(rt->from);
417
Gao feng1716a962012-04-06 00:13:10 +0000418 if (rt->rt6i_flags & RTF_EXPIRES) {
419 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000420 return true;
David Aherna68886a2018-04-20 15:38:02 -0700421 } else if (from) {
Xin Long1e2ea8a2017-08-26 20:10:10 +0800422 return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
David Aherna68886a2018-04-20 15:38:02 -0700423 fib6_check_expired(from);
Gao feng1716a962012-04-06 00:13:10 +0000424 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000425 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427
David Ahernb1d40992019-04-16 14:35:59 -0700428void fib6_select_path(const struct net *net, struct fib6_result *res,
429 struct flowi6 *fl6, int oif, bool have_oif_match,
430 const struct sk_buff *skb, int strict)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000431{
David Ahern8d1c8022018-04-17 17:33:26 -0700432 struct fib6_info *sibling, *next_sibling;
David Ahernb1d40992019-04-16 14:35:59 -0700433 struct fib6_info *match = res->f6i;
434
435 if (!match->fib6_nsiblings || have_oif_match)
436 goto out;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000437
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200438 /* We might have already computed the hash for ICMPv6 errors. In such
439 * case it will always be non-zero. Otherwise now is the time to do it.
440 */
441 if (!fl6->mp_hash)
David Ahernb4bac172018-03-02 08:32:18 -0800442 fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200443
David Ahernad1601a2019-03-27 20:53:56 -0700444 if (fl6->mp_hash <= atomic_read(&match->fib6_nh.fib_nh_upper_bound))
David Ahernb1d40992019-04-16 14:35:59 -0700445 goto out;
Ido Schimmelbbfcd772017-11-21 09:50:12 +0200446
David Ahern93c2fb22018-04-18 15:38:59 -0700447 list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
448 fib6_siblings) {
David Ahern702cea52019-04-09 14:41:13 -0700449 const struct fib6_nh *nh = &sibling->fib6_nh;
David Ahern5e670d82018-04-17 17:33:14 -0700450 int nh_upper_bound;
451
David Ahern702cea52019-04-09 14:41:13 -0700452 nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
David Ahern5e670d82018-04-17 17:33:14 -0700453 if (fl6->mp_hash > nh_upper_bound)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200454 continue;
David Ahern702cea52019-04-09 14:41:13 -0700455 if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200456 break;
457 match = sibling;
458 break;
459 }
460
David Ahernb1d40992019-04-16 14:35:59 -0700461out:
462 res->f6i = match;
463 res->nh = &match->fib6_nh;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000464}
465
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466/*
Wei Wang66f5d6c2017-10-06 12:06:10 -0700467 * Route lookup. rcu_read_lock() should be held.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 */
469
David Ahern0c59d002019-04-09 14:41:18 -0700470static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
471 const struct in6_addr *saddr, int oif, int flags)
472{
473 const struct net_device *dev;
474
475 if (nh->fib_nh_flags & RTNH_F_DEAD)
476 return false;
477
478 dev = nh->fib_nh_dev;
479 if (oif) {
480 if (dev->ifindex == oif)
481 return true;
482 } else {
483 if (ipv6_chk_addr(net, saddr, dev,
484 flags & RT6_LOOKUP_F_IFACE))
485 return true;
486 }
487
488 return false;
489}
490
David Ahern75ef7382019-04-16 14:36:07 -0700491static void rt6_device_match(struct net *net, struct fib6_result *res,
492 const struct in6_addr *saddr, int oif, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
David Ahern75ef7382019-04-16 14:36:07 -0700494 struct fib6_info *f6i = res->f6i;
495 struct fib6_info *spf6i;
496 struct fib6_nh *nh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
David Ahern75ef7382019-04-16 14:36:07 -0700498 if (!oif && ipv6_addr_any(saddr)) {
499 nh = &f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700500 if (!(nh->fib_nh_flags & RTNH_F_DEAD))
501 goto out;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900502 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
David Ahern75ef7382019-04-16 14:36:07 -0700504 for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
505 nh = &spf6i->fib6_nh;
506 if (__rt6_device_match(net, nh, saddr, oif, flags)) {
507 res->f6i = spf6i;
David Ahern7d21fec2019-04-16 14:36:11 -0700508 goto out;
David Ahern75ef7382019-04-16 14:36:07 -0700509 }
510 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
David Ahern75ef7382019-04-16 14:36:07 -0700512 if (oif && flags & RT6_LOOKUP_F_IFACE) {
513 res->f6i = net->ipv6.fib6_null_entry;
David Ahern7d21fec2019-04-16 14:36:11 -0700514 nh = &res->f6i->fib6_nh;
515 goto out;
David Ahern75ef7382019-04-16 14:36:07 -0700516 }
517
David Ahern7d21fec2019-04-16 14:36:11 -0700518 nh = &f6i->fib6_nh;
519 if (nh->fib_nh_flags & RTNH_F_DEAD) {
David Ahern75ef7382019-04-16 14:36:07 -0700520 res->f6i = net->ipv6.fib6_null_entry;
David Ahern7d21fec2019-04-16 14:36:11 -0700521 nh = &res->f6i->fib6_nh;
David Ahern75ef7382019-04-16 14:36:07 -0700522 }
David Ahern7d21fec2019-04-16 14:36:11 -0700523out:
524 res->nh = nh;
525 res->fib6_type = res->f6i->fib6_type;
526 res->fib6_flags = res->f6i->fib6_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527}
528
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800529#ifdef CONFIG_IPV6_ROUTER_PREF
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200530struct __rt6_probe_work {
531 struct work_struct work;
532 struct in6_addr target;
533 struct net_device *dev;
534};
535
536static void rt6_probe_deferred(struct work_struct *w)
537{
538 struct in6_addr mcaddr;
539 struct __rt6_probe_work *work =
540 container_of(w, struct __rt6_probe_work, work);
541
542 addrconf_addr_solict_mult(&work->target, &mcaddr);
Erik Nordmarkadc176c2016-12-02 14:00:08 -0800543 ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200544 dev_put(work->dev);
Michael Büsch662f5532015-02-08 10:14:07 +0100545 kfree(work);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200546}
547
David Aherncc3a86c2019-04-09 14:41:12 -0700548static void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800549{
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200550 struct __rt6_probe_work *work = NULL;
David Ahern5e670d82018-04-17 17:33:14 -0700551 const struct in6_addr *nh_gw;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000552 struct neighbour *neigh;
David Ahern5e670d82018-04-17 17:33:14 -0700553 struct net_device *dev;
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200554 struct inet6_dev *idev;
David Ahern5e670d82018-04-17 17:33:14 -0700555
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800556 /*
557 * Okay, this does not seem to be appropriate
558 * for now, however, we need to check if it
559 * is really so; aka Router Reachability Probing.
560 *
561 * Router Reachability Probe MUST be rate-limited
562 * to no more than one per minute.
563 */
David Aherncc3a86c2019-04-09 14:41:12 -0700564 if (fib6_nh->fib_nh_gw_family)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000565 return;
David Ahern5e670d82018-04-17 17:33:14 -0700566
David Aherncc3a86c2019-04-09 14:41:12 -0700567 nh_gw = &fib6_nh->fib_nh_gw6;
568 dev = fib6_nh->fib_nh_dev;
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000569 rcu_read_lock_bh();
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200570 idev = __in6_dev_get(dev);
David Ahern5e670d82018-04-17 17:33:14 -0700571 neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000572 if (neigh) {
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700573 if (neigh->nud_state & NUD_VALID)
574 goto out;
575
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000576 write_lock(&neigh->lock);
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700577 if (!(neigh->nud_state & NUD_VALID) &&
578 time_after(jiffies,
David Aherndcd1f572018-04-18 15:39:05 -0700579 neigh->updated + idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700580 work = kmalloc(sizeof(*work), GFP_ATOMIC);
581 if (work)
582 __neigh_set_probe_once(neigh);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200583 }
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000584 write_unlock(&neigh->lock);
David Aherncc3a86c2019-04-09 14:41:12 -0700585 } else if (time_after(jiffies, fib6_nh->last_probe +
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200586 idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700587 work = kmalloc(sizeof(*work), GFP_ATOMIC);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000588 }
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700589
590 if (work) {
David Aherncc3a86c2019-04-09 14:41:12 -0700591 fib6_nh->last_probe = jiffies;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700592 INIT_WORK(&work->work, rt6_probe_deferred);
David Ahern5e670d82018-04-17 17:33:14 -0700593 work->target = *nh_gw;
594 dev_hold(dev);
595 work->dev = dev;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700596 schedule_work(&work->work);
597 }
598
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700599out:
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000600 rcu_read_unlock_bh();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800601}
602#else
David Aherncc3a86c2019-04-09 14:41:12 -0700603static inline void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800604{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800605}
606#endif
607
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800609 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 */
David Ahern1ba9a892019-04-09 14:41:10 -0700611static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200613 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
David Ahern5e670d82018-04-17 17:33:14 -0700614 struct neighbour *neigh;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000615
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000616 rcu_read_lock_bh();
David Ahern1ba9a892019-04-09 14:41:10 -0700617 neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
618 &fib6_nh->fib_nh_gw6);
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000619 if (neigh) {
620 read_lock(&neigh->lock);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800621 if (neigh->nud_state & NUD_VALID)
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200622 ret = RT6_NUD_SUCCEED;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800623#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000624 else if (!(neigh->nud_state & NUD_FAILED))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200625 ret = RT6_NUD_SUCCEED;
Jiri Benc7e980562013-12-11 13:48:20 +0100626 else
627 ret = RT6_NUD_FAIL_PROBE;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800628#endif
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000629 read_unlock(&neigh->lock);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200630 } else {
631 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
Jiri Benc7e980562013-12-11 13:48:20 +0100632 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
Paul Marksa5a81f02012-12-03 10:26:54 +0000633 }
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000634 rcu_read_unlock_bh();
635
Paul Marksa5a81f02012-12-03 10:26:54 +0000636 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800637}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
David Ahern702cea52019-04-09 14:41:13 -0700639static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
640 int strict)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800641{
David Ahern6e1809a2019-04-09 14:41:11 -0700642 int m = 0;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900643
David Ahern6e1809a2019-04-09 14:41:11 -0700644 if (!oif || nh->fib_nh_dev->ifindex == oif)
645 m = 2;
646
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700647 if (!m && (strict & RT6_LOOKUP_F_IFACE))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200648 return RT6_NUD_FAIL_HARD;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800649#ifdef CONFIG_IPV6_ROUTER_PREF
David Ahern702cea52019-04-09 14:41:13 -0700650 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800651#endif
David Ahern1ba9a892019-04-09 14:41:10 -0700652 if ((strict & RT6_LOOKUP_F_REACHABLE) &&
David Ahern702cea52019-04-09 14:41:13 -0700653 !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
David Ahern1ba9a892019-04-09 14:41:10 -0700654 int n = rt6_check_neigh(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200655 if (n < 0)
656 return n;
657 }
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800658 return m;
659}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
David Ahern28679ed2019-04-09 14:41:14 -0700661static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
662 int oif, int strict, int *mpri, bool *do_rr)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800663{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200664 bool match_do_rr = false;
David Ahern28679ed2019-04-09 14:41:14 -0700665 bool rc = false;
666 int m;
Andy Gospodarek35103d12015-08-13 10:39:01 -0400667
David Ahern28679ed2019-04-09 14:41:14 -0700668 if (nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel8067bb82018-01-07 12:45:09 +0200669 goto out;
670
David Ahern28679ed2019-04-09 14:41:14 -0700671 if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
672 nh->fib_nh_flags & RTNH_F_LINKDOWN &&
David Ahernd5d32e42016-10-24 12:27:23 -0700673 !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
Andy Gospodarek35103d12015-08-13 10:39:01 -0400674 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700675
David Ahern28679ed2019-04-09 14:41:14 -0700676 m = rt6_score_route(nh, fib6_flags, oif, strict);
Jiri Benc7e980562013-12-11 13:48:20 +0100677 if (m == RT6_NUD_FAIL_DO_RR) {
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200678 match_do_rr = true;
679 m = 0; /* lowest valid score */
Jiri Benc7e980562013-12-11 13:48:20 +0100680 } else if (m == RT6_NUD_FAIL_HARD) {
David S. Millerf11e6652007-03-24 20:36:25 -0700681 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700682 }
683
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200684 if (strict & RT6_LOOKUP_F_REACHABLE)
David Ahern28679ed2019-04-09 14:41:14 -0700685 rt6_probe(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200686
Jiri Benc7e980562013-12-11 13:48:20 +0100687 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200688 if (m > *mpri) {
689 *do_rr = match_do_rr;
690 *mpri = m;
David Ahern28679ed2019-04-09 14:41:14 -0700691 rc = true;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200692 }
David S. Millerf11e6652007-03-24 20:36:25 -0700693out:
David Ahern28679ed2019-04-09 14:41:14 -0700694 return rc;
David S. Millerf11e6652007-03-24 20:36:25 -0700695}
696
David Ahernb7bc4b62019-04-16 14:36:08 -0700697static void __find_rr_leaf(struct fib6_info *f6i_start,
David Ahern30c15f02019-04-09 14:41:15 -0700698 struct fib6_info *nomatch, u32 metric,
David Ahernb7bc4b62019-04-16 14:36:08 -0700699 struct fib6_result *res, struct fib6_info **cont,
David Ahern30c15f02019-04-09 14:41:15 -0700700 int oif, int strict, bool *do_rr, int *mpri)
David S. Millerf11e6652007-03-24 20:36:25 -0700701{
David Ahernb7bc4b62019-04-16 14:36:08 -0700702 struct fib6_info *f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700703
David Ahernb7bc4b62019-04-16 14:36:08 -0700704 for (f6i = f6i_start;
705 f6i && f6i != nomatch;
706 f6i = rcu_dereference(f6i->fib6_next)) {
David Ahern30c15f02019-04-09 14:41:15 -0700707 struct fib6_nh *nh;
708
David Ahernb7bc4b62019-04-16 14:36:08 -0700709 if (cont && f6i->fib6_metric != metric) {
710 *cont = f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700711 return;
712 }
713
David Ahernb7bc4b62019-04-16 14:36:08 -0700714 if (fib6_check_expired(f6i))
David Ahern30c15f02019-04-09 14:41:15 -0700715 continue;
716
David Ahernb7bc4b62019-04-16 14:36:08 -0700717 nh = &f6i->fib6_nh;
718 if (find_match(nh, f6i->fib6_flags, oif, strict, mpri, do_rr)) {
719 res->f6i = f6i;
720 res->nh = nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700721 res->fib6_flags = f6i->fib6_flags;
722 res->fib6_type = f6i->fib6_type;
David Ahernb7bc4b62019-04-16 14:36:08 -0700723 }
David Ahern30c15f02019-04-09 14:41:15 -0700724 }
725}
726
David Ahernb7bc4b62019-04-16 14:36:08 -0700727static void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
728 struct fib6_info *rr_head, int oif, int strict,
729 bool *do_rr, struct fib6_result *res)
David Ahern30c15f02019-04-09 14:41:15 -0700730{
David Ahernb7bc4b62019-04-16 14:36:08 -0700731 u32 metric = rr_head->fib6_metric;
732 struct fib6_info *cont = NULL;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800733 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
David Ahernb7bc4b62019-04-16 14:36:08 -0700735 __find_rr_leaf(rr_head, NULL, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700736 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700737
David Ahernb7bc4b62019-04-16 14:36:08 -0700738 __find_rr_leaf(leaf, rr_head, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700739 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700740
David Ahernb7bc4b62019-04-16 14:36:08 -0700741 if (res->f6i || !cont)
742 return;
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700743
David Ahernb7bc4b62019-04-16 14:36:08 -0700744 __find_rr_leaf(cont, NULL, metric, res, NULL,
David Ahern30c15f02019-04-09 14:41:15 -0700745 oif, strict, do_rr, &mpri);
David S. Millerf11e6652007-03-24 20:36:25 -0700746}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800747
David Ahernb7bc4b62019-04-16 14:36:08 -0700748static void rt6_select(struct net *net, struct fib6_node *fn, int oif,
749 struct fib6_result *res, int strict)
David S. Millerf11e6652007-03-24 20:36:25 -0700750{
David Ahern8d1c8022018-04-17 17:33:26 -0700751 struct fib6_info *leaf = rcu_dereference(fn->leaf);
David Ahernb7bc4b62019-04-16 14:36:08 -0700752 struct fib6_info *rt0;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200753 bool do_rr = false;
Wei Wang17ecf592017-10-06 12:06:09 -0700754 int key_plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
David Ahernb7bc4b62019-04-16 14:36:08 -0700756 /* make sure this function or its helpers sets f6i */
757 res->f6i = NULL;
758
David Ahern421842e2018-04-17 17:33:18 -0700759 if (!leaf || leaf == net->ipv6.fib6_null_entry)
David Ahernb7bc4b62019-04-16 14:36:08 -0700760 goto out;
Wei Wang8d1040e2017-10-06 12:06:08 -0700761
Wei Wang66f5d6c2017-10-06 12:06:10 -0700762 rt0 = rcu_dereference(fn->rr_ptr);
David S. Millerf11e6652007-03-24 20:36:25 -0700763 if (!rt0)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700764 rt0 = leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Wei Wang17ecf592017-10-06 12:06:09 -0700766 /* Double check to make sure fn is not an intermediate node
767 * and fn->leaf does not points to its child's leaf
768 * (This might happen if all routes under fn are deleted from
769 * the tree and fib6_repair_tree() is called on the node.)
770 */
David Ahern93c2fb22018-04-18 15:38:59 -0700771 key_plen = rt0->fib6_dst.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700772#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -0700773 if (rt0->fib6_src.plen)
774 key_plen = rt0->fib6_src.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700775#endif
776 if (fn->fn_bit != key_plen)
David Ahernb7bc4b62019-04-16 14:36:08 -0700777 goto out;
Wei Wang17ecf592017-10-06 12:06:09 -0700778
David Ahernb7bc4b62019-04-16 14:36:08 -0700779 find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200780 if (do_rr) {
David Ahern8fb11a92018-05-04 13:54:24 -0700781 struct fib6_info *next = rcu_dereference(rt0->fib6_next);
David S. Millerf11e6652007-03-24 20:36:25 -0700782
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800783 /* no entries matched; do round-robin */
David Ahern93c2fb22018-04-18 15:38:59 -0700784 if (!next || next->fib6_metric != rt0->fib6_metric)
Wei Wang8d1040e2017-10-06 12:06:08 -0700785 next = leaf;
David S. Millerf11e6652007-03-24 20:36:25 -0700786
Wei Wang66f5d6c2017-10-06 12:06:10 -0700787 if (next != rt0) {
David Ahern93c2fb22018-04-18 15:38:59 -0700788 spin_lock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700789 /* make sure next is not being deleted from the tree */
David Ahern93c2fb22018-04-18 15:38:59 -0700790 if (next->fib6_node)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700791 rcu_assign_pointer(fn->rr_ptr, next);
David Ahern93c2fb22018-04-18 15:38:59 -0700792 spin_unlock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700793 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 }
795
David Ahernb7bc4b62019-04-16 14:36:08 -0700796out:
797 if (!res->f6i) {
798 res->f6i = net->ipv6.fib6_null_entry;
799 res->nh = &res->f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700800 res->fib6_flags = res->f6i->fib6_flags;
801 res->fib6_type = res->f6i->fib6_type;
David Ahernb7bc4b62019-04-16 14:36:08 -0700802 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803}
804
David Ahern85bd05d2019-04-16 14:36:01 -0700805static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700806{
David Ahern85bd05d2019-04-16 14:36:01 -0700807 return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
808 res->nh->fib_nh_gw_family;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700809}
810
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800811#ifdef CONFIG_IPV6_ROUTE_INFO
812int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000813 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800814{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900815 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800816 struct route_info *rinfo = (struct route_info *) opt;
817 struct in6_addr prefix_buf, *prefix;
818 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900819 unsigned long lifetime;
David Ahern8d1c8022018-04-17 17:33:26 -0700820 struct fib6_info *rt;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800821
822 if (len < sizeof(struct route_info)) {
823 return -EINVAL;
824 }
825
826 /* Sanity check for prefix_len and length */
827 if (rinfo->length > 3) {
828 return -EINVAL;
829 } else if (rinfo->prefix_len > 128) {
830 return -EINVAL;
831 } else if (rinfo->prefix_len > 64) {
832 if (rinfo->length < 2) {
833 return -EINVAL;
834 }
835 } else if (rinfo->prefix_len > 0) {
836 if (rinfo->length < 1) {
837 return -EINVAL;
838 }
839 }
840
841 pref = rinfo->route_pref;
842 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000843 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800844
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900845 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800846
847 if (rinfo->length == 3)
848 prefix = (struct in6_addr *)rinfo->prefix;
849 else {
850 /* this function is safe */
851 ipv6_addr_prefix(&prefix_buf,
852 (struct in6_addr *)rinfo->prefix,
853 rinfo->prefix_len);
854 prefix = &prefix_buf;
855 }
856
Duan Jiongf104a562013-11-08 09:56:53 +0800857 if (rinfo->prefix_len == 0)
David Ahernafb1d4b52018-04-17 17:33:11 -0700858 rt = rt6_get_dflt_router(net, gwaddr, dev);
Duan Jiongf104a562013-11-08 09:56:53 +0800859 else
860 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
David Ahern830218c2016-10-24 10:52:35 -0700861 gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800862
863 if (rt && !lifetime) {
David Ahernafb1d4b52018-04-17 17:33:11 -0700864 ip6_del_rt(net, rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800865 rt = NULL;
866 }
867
868 if (!rt && lifetime)
David Ahern830218c2016-10-24 10:52:35 -0700869 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
870 dev, pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800871 else if (rt)
David Ahern93c2fb22018-04-18 15:38:59 -0700872 rt->fib6_flags = RTF_ROUTEINFO |
873 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800874
875 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000876 if (!addrconf_finite_timeout(lifetime))
David Ahern14895682018-04-17 17:33:17 -0700877 fib6_clean_expires(rt);
Gao feng1716a962012-04-06 00:13:10 +0000878 else
David Ahern14895682018-04-17 17:33:17 -0700879 fib6_set_expires(rt, jiffies + HZ * lifetime);
Gao feng1716a962012-04-06 00:13:10 +0000880
David Ahern93531c62018-04-17 17:33:25 -0700881 fib6_info_release(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800882 }
883 return 0;
884}
885#endif
886
David Ahernae90d862018-04-17 17:33:12 -0700887/*
888 * Misc support functions
889 */
890
891/* called with rcu_lock held */
David Ahern0d161582019-04-16 14:36:04 -0700892static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -0700893{
David Ahern0d161582019-04-16 14:36:04 -0700894 struct net_device *dev = res->nh->fib_nh_dev;
David Ahernae90d862018-04-17 17:33:12 -0700895
David Ahern7d21fec2019-04-16 14:36:11 -0700896 if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
David Ahernae90d862018-04-17 17:33:12 -0700897 /* for copies of local routes, dst->dev needs to be the
898 * device if it is a master device, the master device if
899 * device is enslaved, and the loopback as the default
900 */
901 if (netif_is_l3_slave(dev) &&
David Ahern7d21fec2019-04-16 14:36:11 -0700902 !rt6_need_strict(&res->f6i->fib6_dst.addr))
David Ahernae90d862018-04-17 17:33:12 -0700903 dev = l3mdev_master_dev_rcu(dev);
904 else if (!netif_is_l3_master(dev))
905 dev = dev_net(dev)->loopback_dev;
906 /* last case is netif_is_l3_master(dev) is true in which
907 * case we want dev returned to be dev
908 */
909 }
910
911 return dev;
912}
913
David Ahern6edb3c92018-04-17 17:33:15 -0700914static const int fib6_prop[RTN_MAX + 1] = {
915 [RTN_UNSPEC] = 0,
916 [RTN_UNICAST] = 0,
917 [RTN_LOCAL] = 0,
918 [RTN_BROADCAST] = 0,
919 [RTN_ANYCAST] = 0,
920 [RTN_MULTICAST] = 0,
921 [RTN_BLACKHOLE] = -EINVAL,
922 [RTN_UNREACHABLE] = -EHOSTUNREACH,
923 [RTN_PROHIBIT] = -EACCES,
924 [RTN_THROW] = -EAGAIN,
925 [RTN_NAT] = -EINVAL,
926 [RTN_XRESOLVE] = -EINVAL,
927};
928
929static int ip6_rt_type_to_error(u8 fib6_type)
930{
931 return fib6_prop[fib6_type];
932}
933
David Ahern8d1c8022018-04-17 17:33:26 -0700934static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
David Ahern3b6761d2018-04-17 17:33:20 -0700935{
936 unsigned short flags = 0;
937
938 if (rt->dst_nocount)
939 flags |= DST_NOCOUNT;
940 if (rt->dst_nopolicy)
941 flags |= DST_NOPOLICY;
942 if (rt->dst_host)
943 flags |= DST_HOST;
944
945 return flags;
946}
947
David Ahern7d21fec2019-04-16 14:36:11 -0700948static void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type)
David Ahern6edb3c92018-04-17 17:33:15 -0700949{
David Ahern7d21fec2019-04-16 14:36:11 -0700950 rt->dst.error = ip6_rt_type_to_error(fib6_type);
David Ahern6edb3c92018-04-17 17:33:15 -0700951
David Ahern7d21fec2019-04-16 14:36:11 -0700952 switch (fib6_type) {
David Ahern6edb3c92018-04-17 17:33:15 -0700953 case RTN_BLACKHOLE:
954 rt->dst.output = dst_discard_out;
955 rt->dst.input = dst_discard;
956 break;
957 case RTN_PROHIBIT:
958 rt->dst.output = ip6_pkt_prohibit_out;
959 rt->dst.input = ip6_pkt_prohibit;
960 break;
961 case RTN_THROW:
962 case RTN_UNREACHABLE:
963 default:
964 rt->dst.output = ip6_pkt_discard_out;
965 rt->dst.input = ip6_pkt_discard;
966 break;
967 }
968}
969
David Ahern0d161582019-04-16 14:36:04 -0700970static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
David Ahern6edb3c92018-04-17 17:33:15 -0700971{
David Ahern7d21fec2019-04-16 14:36:11 -0700972 struct fib6_info *f6i = res->f6i;
David Ahern0d161582019-04-16 14:36:04 -0700973
David Ahern7d21fec2019-04-16 14:36:11 -0700974 if (res->fib6_flags & RTF_REJECT) {
975 ip6_rt_init_dst_reject(rt, res->fib6_type);
David Ahern6edb3c92018-04-17 17:33:15 -0700976 return;
977 }
978
979 rt->dst.error = 0;
980 rt->dst.output = ip6_output;
981
David Ahern7d21fec2019-04-16 14:36:11 -0700982 if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700983 rt->dst.input = ip6_input;
David Ahern7d21fec2019-04-16 14:36:11 -0700984 } else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700985 rt->dst.input = ip6_mc_input;
986 } else {
987 rt->dst.input = ip6_forward;
988 }
989
David Ahern0d161582019-04-16 14:36:04 -0700990 if (res->nh->fib_nh_lws) {
991 rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
David Ahern6edb3c92018-04-17 17:33:15 -0700992 lwtunnel_set_redirect(&rt->dst);
993 }
994
995 rt->dst.lastuse = jiffies;
996}
997
Wei Wange873e4b2018-07-21 20:56:32 -0700998/* Caller must already hold reference to @from */
David Ahern8d1c8022018-04-17 17:33:26 -0700999static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
David Ahernae90d862018-04-17 17:33:12 -07001000{
David Ahernae90d862018-04-17 17:33:12 -07001001 rt->rt6i_flags &= ~RTF_EXPIRES;
David Aherna68886a2018-04-20 15:38:02 -07001002 rcu_assign_pointer(rt->from, from);
David Aherne1255ed2018-10-04 20:07:53 -07001003 ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
David Ahernae90d862018-04-17 17:33:12 -07001004}
1005
David Ahern0d161582019-04-16 14:36:04 -07001006/* Caller must already hold reference to f6i in result */
1007static void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -07001008{
David Ahern0d161582019-04-16 14:36:04 -07001009 const struct fib6_nh *nh = res->nh;
1010 const struct net_device *dev = nh->fib_nh_dev;
1011 struct fib6_info *f6i = res->f6i;
David Aherndcd1f572018-04-18 15:39:05 -07001012
David Ahern0d161582019-04-16 14:36:04 -07001013 ip6_rt_init_dst(rt, res);
David Ahern6edb3c92018-04-17 17:33:15 -07001014
David Ahern0d161582019-04-16 14:36:04 -07001015 rt->rt6i_dst = f6i->fib6_dst;
David Aherndcd1f572018-04-18 15:39:05 -07001016 rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
David Ahern7d21fec2019-04-16 14:36:11 -07001017 rt->rt6i_flags = res->fib6_flags;
David Ahern0d161582019-04-16 14:36:04 -07001018 if (nh->fib_nh_gw_family) {
1019 rt->rt6i_gateway = nh->fib_nh_gw6;
David Ahern2b2450c2019-03-27 20:53:52 -07001020 rt->rt6i_flags |= RTF_GATEWAY;
1021 }
David Ahern0d161582019-04-16 14:36:04 -07001022 rt6_set_from(rt, f6i);
David Ahernae90d862018-04-17 17:33:12 -07001023#ifdef CONFIG_IPV6_SUBTREES
David Ahern0d161582019-04-16 14:36:04 -07001024 rt->rt6i_src = f6i->fib6_src;
David Ahernae90d862018-04-17 17:33:12 -07001025#endif
David Ahernae90d862018-04-17 17:33:12 -07001026}
1027
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001028static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1029 struct in6_addr *saddr)
1030{
Wei Wang66f5d6c2017-10-06 12:06:10 -07001031 struct fib6_node *pn, *sn;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001032 while (1) {
1033 if (fn->fn_flags & RTN_TL_ROOT)
1034 return NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001035 pn = rcu_dereference(fn->parent);
1036 sn = FIB6_SUBTREE(pn);
1037 if (sn && sn != fn)
David Ahern64547432018-05-09 20:34:19 -07001038 fn = fib6_node_lookup(sn, NULL, saddr);
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001039 else
1040 fn = pn;
1041 if (fn->fn_flags & RTN_RTINFO)
1042 return fn;
1043 }
1044}
Thomas Grafc71099a2006-08-04 23:20:06 -07001045
David Ahern10585b42019-03-20 09:24:50 -07001046static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
Wei Wangd3843fe2017-10-06 12:06:06 -07001047{
1048 struct rt6_info *rt = *prt;
1049
1050 if (dst_hold_safe(&rt->dst))
1051 return true;
David Ahern10585b42019-03-20 09:24:50 -07001052 if (net) {
Wei Wangd3843fe2017-10-06 12:06:06 -07001053 rt = net->ipv6.ip6_null_entry;
1054 dst_hold(&rt->dst);
1055 } else {
1056 rt = NULL;
1057 }
1058 *prt = rt;
1059 return false;
1060}
1061
David Aherndec9b0e2018-04-17 17:33:19 -07001062/* called with rcu_lock held */
David Ahern9b6b35a2019-04-16 14:36:02 -07001063static struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
David Aherndec9b0e2018-04-17 17:33:19 -07001064{
David Ahern9b6b35a2019-04-16 14:36:02 -07001065 struct net_device *dev = res->nh->fib_nh_dev;
1066 struct fib6_info *f6i = res->f6i;
1067 unsigned short flags;
David Aherndec9b0e2018-04-17 17:33:19 -07001068 struct rt6_info *nrt;
1069
David Ahern9b6b35a2019-04-16 14:36:02 -07001070 if (!fib6_info_hold_safe(f6i))
Xin Long1c87e792019-03-20 14:45:48 +08001071 goto fallback;
Wei Wange873e4b2018-07-21 20:56:32 -07001072
David Ahern9b6b35a2019-04-16 14:36:02 -07001073 flags = fib6_info_dst_flags(f6i);
David Ahern93531c62018-04-17 17:33:25 -07001074 nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
Xin Long1c87e792019-03-20 14:45:48 +08001075 if (!nrt) {
David Ahern9b6b35a2019-04-16 14:36:02 -07001076 fib6_info_release(f6i);
Xin Long1c87e792019-03-20 14:45:48 +08001077 goto fallback;
1078 }
David Aherndec9b0e2018-04-17 17:33:19 -07001079
David Ahern0d161582019-04-16 14:36:04 -07001080 ip6_rt_copy_init(nrt, res);
Xin Long1c87e792019-03-20 14:45:48 +08001081 return nrt;
1082
1083fallback:
1084 nrt = dev_net(dev)->ipv6.ip6_null_entry;
1085 dst_hold(&nrt->dst);
David Aherndec9b0e2018-04-17 17:33:19 -07001086 return nrt;
1087}
1088
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001089static struct rt6_info *ip6_pol_route_lookup(struct net *net,
1090 struct fib6_table *table,
David Ahernb75cc8f2018-03-02 08:32:17 -08001091 struct flowi6 *fl6,
1092 const struct sk_buff *skb,
1093 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094{
David Ahernb1d40992019-04-16 14:35:59 -07001095 struct fib6_result res = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 struct fib6_node *fn;
David Ahern23fb93a2018-04-17 17:33:23 -07001097 struct rt6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
David Ahernb6cdbc82018-03-29 17:44:57 -07001099 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1100 flags &= ~RT6_LOOKUP_F_IFACE;
1101
Wei Wang66f5d6c2017-10-06 12:06:10 -07001102 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07001103 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -07001104restart:
David Ahernb1d40992019-04-16 14:35:59 -07001105 res.f6i = rcu_dereference(fn->leaf);
1106 if (!res.f6i)
1107 res.f6i = net->ipv6.fib6_null_entry;
David Ahernaf52a522019-04-09 14:41:16 -07001108 else
David Ahern75ef7382019-04-16 14:36:07 -07001109 rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
1110 flags);
David Ahernaf52a522019-04-09 14:41:16 -07001111
David Ahernb1d40992019-04-16 14:35:59 -07001112 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001113 fn = fib6_backtrack(fn, &fl6->saddr);
1114 if (fn)
1115 goto restart;
David Ahernaf52a522019-04-09 14:41:16 -07001116
1117 rt = net->ipv6.ip6_null_entry;
1118 dst_hold(&rt->dst);
1119 goto out;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001120 }
Wei Wang2b760fc2017-10-06 12:06:03 -07001121
David Ahernb1d40992019-04-16 14:35:59 -07001122 fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1123 fl6->flowi6_oif != 0, skb, flags);
1124
David S. Miller4c9483b2011-03-12 16:22:43 -05001125 /* Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001126 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001127 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001128 if (ip6_hold_safe(net, &rt))
David Aherndec9b0e2018-04-17 17:33:19 -07001129 dst_use_noref(&rt->dst, jiffies);
David Ahern23fb93a2018-04-17 17:33:23 -07001130 } else {
David Ahern9b6b35a2019-04-16 14:36:02 -07001131 rt = ip6_create_rt_rcu(&res);
David Aherndec9b0e2018-04-17 17:33:19 -07001132 }
Wei Wangd3843fe2017-10-06 12:06:06 -07001133
David Ahernaf52a522019-04-09 14:41:16 -07001134out:
David Ahern8ff2e5b2019-04-16 14:36:09 -07001135 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahernaf52a522019-04-09 14:41:16 -07001136
Wei Wang66f5d6c2017-10-06 12:06:10 -07001137 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08001138
Thomas Grafc71099a2006-08-04 23:20:06 -07001139 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001140}
1141
Ian Morris67ba4152014-08-24 21:53:10 +01001142struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08001143 const struct sk_buff *skb, int flags)
Florian Westphalea6e5742011-09-05 16:05:44 +02001144{
David Ahernb75cc8f2018-03-02 08:32:17 -08001145 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
Florian Westphalea6e5742011-09-05 16:05:44 +02001146}
1147EXPORT_SYMBOL_GPL(ip6_route_lookup);
1148
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001149struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
David Ahernb75cc8f2018-03-02 08:32:17 -08001150 const struct in6_addr *saddr, int oif,
1151 const struct sk_buff *skb, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -07001152{
David S. Miller4c9483b2011-03-12 16:22:43 -05001153 struct flowi6 fl6 = {
1154 .flowi6_oif = oif,
1155 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -07001156 };
1157 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001158 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07001159
Thomas Grafadaa70b2006-10-13 15:01:03 -07001160 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -05001161 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -07001162 flags |= RT6_LOOKUP_F_HAS_SADDR;
1163 }
1164
David Ahernb75cc8f2018-03-02 08:32:17 -08001165 dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -07001166 if (dst->error == 0)
1167 return (struct rt6_info *) dst;
1168
1169 dst_release(dst);
1170
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 return NULL;
1172}
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001173EXPORT_SYMBOL(rt6_lookup);
1174
Thomas Grafc71099a2006-08-04 23:20:06 -07001175/* ip6_ins_rt is called with FREE table->tb6_lock.
Wei Wang1cfb71e2017-06-17 10:42:33 -07001176 * It takes new route entry, the addition fails by any reason the
1177 * route is released.
1178 * Caller must hold dst before calling it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 */
1180
David Ahern8d1c8022018-04-17 17:33:26 -07001181static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
David Ahern333c4302017-05-21 10:12:04 -06001182 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183{
1184 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001185 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186
David Ahern93c2fb22018-04-18 15:38:59 -07001187 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001188 spin_lock_bh(&table->tb6_lock);
David Ahernd4ead6b2018-04-17 17:33:16 -07001189 err = fib6_add(&table->tb6_root, rt, info, extack);
Wei Wang66f5d6c2017-10-06 12:06:10 -07001190 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
1192 return err;
1193}
1194
David Ahern8d1c8022018-04-17 17:33:26 -07001195int ip6_ins_rt(struct net *net, struct fib6_info *rt)
Thomas Graf40e22e82006-08-22 00:00:45 -07001196{
David Ahernafb1d4b52018-04-17 17:33:11 -07001197 struct nl_info info = { .nl_net = net, };
Florian Westphale715b6d2015-01-05 23:57:44 +01001198
David Ahernd4ead6b2018-04-17 17:33:16 -07001199 return __ip6_ins_rt(rt, &info, NULL);
Thomas Graf40e22e82006-08-22 00:00:45 -07001200}
1201
David Ahern85bd05d2019-04-16 14:36:01 -07001202static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001203 const struct in6_addr *daddr,
1204 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205{
David Ahern85bd05d2019-04-16 14:36:01 -07001206 struct fib6_info *f6i = res->f6i;
David Ahern4832c302017-08-17 12:17:20 -07001207 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 struct rt6_info *rt;
1209
1210 /*
1211 * Clone the route.
1212 */
1213
David Ahern85bd05d2019-04-16 14:36:01 -07001214 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001215 return NULL;
1216
David Ahern0d161582019-04-16 14:36:04 -07001217 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001218 rt = ip6_dst_alloc(dev_net(dev), dev, 0);
Wei Wange873e4b2018-07-21 20:56:32 -07001219 if (!rt) {
David Ahern85bd05d2019-04-16 14:36:01 -07001220 fib6_info_release(f6i);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001221 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001222 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001223
David Ahern0d161582019-04-16 14:36:04 -07001224 ip6_rt_copy_init(rt, res);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001225 rt->rt6i_flags |= RTF_CACHE;
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001226 rt->dst.flags |= DST_HOST;
1227 rt->rt6i_dst.addr = *daddr;
1228 rt->rt6i_dst.plen = 128;
1229
David Ahern85bd05d2019-04-16 14:36:01 -07001230 if (!rt6_is_gw_or_nonexthop(res)) {
1231 if (f6i->fib6_dst.plen != 128 &&
1232 ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001233 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234#ifdef CONFIG_IPV6_SUBTREES
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001235 if (rt->rt6i_src.plen && saddr) {
1236 rt->rt6i_src.addr = *saddr;
1237 rt->rt6i_src.plen = 128;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001238 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001239#endif
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001240 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001242 return rt;
1243}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
David Aherndb3fede2019-04-16 14:36:03 -07001245static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001246{
David Aherndb3fede2019-04-16 14:36:03 -07001247 struct fib6_info *f6i = res->f6i;
1248 unsigned short flags = fib6_info_dst_flags(f6i);
David Ahern4832c302017-08-17 12:17:20 -07001249 struct net_device *dev;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001250 struct rt6_info *pcpu_rt;
1251
David Aherndb3fede2019-04-16 14:36:03 -07001252 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001253 return NULL;
1254
David Ahern4832c302017-08-17 12:17:20 -07001255 rcu_read_lock();
David Ahern0d161582019-04-16 14:36:04 -07001256 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001257 pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags);
David Ahern4832c302017-08-17 12:17:20 -07001258 rcu_read_unlock();
Wei Wange873e4b2018-07-21 20:56:32 -07001259 if (!pcpu_rt) {
David Aherndb3fede2019-04-16 14:36:03 -07001260 fib6_info_release(f6i);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001261 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001262 }
David Ahern0d161582019-04-16 14:36:04 -07001263 ip6_rt_copy_init(pcpu_rt, res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001264 pcpu_rt->rt6i_flags |= RTF_PCPU;
1265 return pcpu_rt;
1266}
1267
Wei Wang66f5d6c2017-10-06 12:06:10 -07001268/* It should be called with rcu_read_lock() acquired */
David Aherndb3fede2019-04-16 14:36:03 -07001269static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001270{
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001271 struct rt6_info *pcpu_rt, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001272
David Aherndb3fede2019-04-16 14:36:03 -07001273 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001274 pcpu_rt = *p;
1275
David Ahernd4ead6b2018-04-17 17:33:16 -07001276 if (pcpu_rt)
David Ahern10585b42019-03-20 09:24:50 -07001277 ip6_hold_safe(NULL, &pcpu_rt);
Wei Wangd3843fe2017-10-06 12:06:06 -07001278
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001279 return pcpu_rt;
1280}
1281
David Ahernafb1d4b52018-04-17 17:33:11 -07001282static struct rt6_info *rt6_make_pcpu_route(struct net *net,
David Aherndb3fede2019-04-16 14:36:03 -07001283 const struct fib6_result *res)
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001284{
1285 struct rt6_info *pcpu_rt, *prev, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001286
David Aherndb3fede2019-04-16 14:36:03 -07001287 pcpu_rt = ip6_rt_pcpu_alloc(res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001288 if (!pcpu_rt) {
Martin KaFai Lau9c7370a2015-08-14 11:05:54 -07001289 dst_hold(&net->ipv6.ip6_null_entry->dst);
1290 return net->ipv6.ip6_null_entry;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001291 }
1292
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001293 dst_hold(&pcpu_rt->dst);
David Aherndb3fede2019-04-16 14:36:03 -07001294 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Wei Wanga94b9362017-10-06 12:06:04 -07001295 prev = cmpxchg(p, NULL, pcpu_rt);
Eric Dumazet951f7882017-10-08 21:07:18 -07001296 BUG_ON(prev);
Wei Wanga94b9362017-10-06 12:06:04 -07001297
Eric Dumazet61fb0d02019-05-15 19:39:52 -07001298 if (res->f6i->fib6_destroying) {
1299 struct fib6_info *from;
1300
1301 from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
1302 fib6_info_release(from);
1303 }
1304
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001305 return pcpu_rt;
1306}
1307
Wei Wang35732d02017-10-06 12:05:57 -07001308/* exception hash table implementation
1309 */
1310static DEFINE_SPINLOCK(rt6_exception_lock);
1311
1312/* Remove rt6_ex from hash table and free the memory
1313 * Caller must hold rt6_exception_lock
1314 */
1315static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1316 struct rt6_exception *rt6_ex)
1317{
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001318 struct fib6_info *from;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001319 struct net *net;
Wei Wang81eb8442017-10-06 12:06:11 -07001320
Wei Wang35732d02017-10-06 12:05:57 -07001321 if (!bucket || !rt6_ex)
1322 return;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001323
1324 net = dev_net(rt6_ex->rt6i->dst.dev);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001325 net->ipv6.rt6_stats->fib_rt_cache--;
1326
1327 /* purge completely the exception to allow releasing the held resources:
1328 * some [sk] cache may keep the dst around for unlimited time
1329 */
Eric Dumazet0e233872019-04-28 12:22:25 -07001330 from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001331 fib6_info_release(from);
1332 dst_dev_put(&rt6_ex->rt6i->dst);
1333
Wei Wang35732d02017-10-06 12:05:57 -07001334 hlist_del_rcu(&rt6_ex->hlist);
David Ahern77634cc2018-04-17 17:33:27 -07001335 dst_release(&rt6_ex->rt6i->dst);
Wei Wang35732d02017-10-06 12:05:57 -07001336 kfree_rcu(rt6_ex, rcu);
1337 WARN_ON_ONCE(!bucket->depth);
1338 bucket->depth--;
1339}
1340
1341/* Remove oldest rt6_ex in bucket and free the memory
1342 * Caller must hold rt6_exception_lock
1343 */
1344static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
1345{
1346 struct rt6_exception *rt6_ex, *oldest = NULL;
1347
1348 if (!bucket)
1349 return;
1350
1351 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1352 if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
1353 oldest = rt6_ex;
1354 }
1355 rt6_remove_exception(bucket, oldest);
1356}
1357
1358static u32 rt6_exception_hash(const struct in6_addr *dst,
1359 const struct in6_addr *src)
1360{
1361 static u32 seed __read_mostly;
1362 u32 val;
1363
1364 net_get_random_once(&seed, sizeof(seed));
1365 val = jhash(dst, sizeof(*dst), seed);
1366
1367#ifdef CONFIG_IPV6_SUBTREES
1368 if (src)
1369 val = jhash(src, sizeof(*src), val);
1370#endif
1371 return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
1372}
1373
1374/* Helper function to find the cached rt in the hash table
1375 * and update bucket pointer to point to the bucket for this
1376 * (daddr, saddr) pair
1377 * Caller must hold rt6_exception_lock
1378 */
1379static struct rt6_exception *
1380__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
1381 const struct in6_addr *daddr,
1382 const struct in6_addr *saddr)
1383{
1384 struct rt6_exception *rt6_ex;
1385 u32 hval;
1386
1387 if (!(*bucket) || !daddr)
1388 return NULL;
1389
1390 hval = rt6_exception_hash(daddr, saddr);
1391 *bucket += hval;
1392
1393 hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
1394 struct rt6_info *rt6 = rt6_ex->rt6i;
1395 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1396
1397#ifdef CONFIG_IPV6_SUBTREES
1398 if (matched && saddr)
1399 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1400#endif
1401 if (matched)
1402 return rt6_ex;
1403 }
1404 return NULL;
1405}
1406
1407/* Helper function to find the cached rt in the hash table
1408 * and update bucket pointer to point to the bucket for this
1409 * (daddr, saddr) pair
1410 * Caller must hold rcu_read_lock()
1411 */
1412static struct rt6_exception *
1413__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
1414 const struct in6_addr *daddr,
1415 const struct in6_addr *saddr)
1416{
1417 struct rt6_exception *rt6_ex;
1418 u32 hval;
1419
1420 WARN_ON_ONCE(!rcu_read_lock_held());
1421
1422 if (!(*bucket) || !daddr)
1423 return NULL;
1424
1425 hval = rt6_exception_hash(daddr, saddr);
1426 *bucket += hval;
1427
1428 hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
1429 struct rt6_info *rt6 = rt6_ex->rt6i;
1430 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1431
1432#ifdef CONFIG_IPV6_SUBTREES
1433 if (matched && saddr)
1434 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1435#endif
1436 if (matched)
1437 return rt6_ex;
1438 }
1439 return NULL;
1440}
1441
David Ahernb748f262019-04-16 14:36:06 -07001442static unsigned int fib6_mtu(const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001443{
David Ahernb748f262019-04-16 14:36:06 -07001444 const struct fib6_nh *nh = res->nh;
David Ahernd4ead6b2018-04-17 17:33:16 -07001445 unsigned int mtu;
1446
David Ahernb748f262019-04-16 14:36:06 -07001447 if (res->f6i->fib6_pmtu) {
1448 mtu = res->f6i->fib6_pmtu;
David Aherndcd1f572018-04-18 15:39:05 -07001449 } else {
David Ahernb748f262019-04-16 14:36:06 -07001450 struct net_device *dev = nh->fib_nh_dev;
David Aherndcd1f572018-04-18 15:39:05 -07001451 struct inet6_dev *idev;
1452
1453 rcu_read_lock();
1454 idev = __in6_dev_get(dev);
1455 mtu = idev->cnf.mtu6;
1456 rcu_read_unlock();
1457 }
1458
David Ahernd4ead6b2018-04-17 17:33:16 -07001459 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1460
David Ahernb748f262019-04-16 14:36:06 -07001461 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahernd4ead6b2018-04-17 17:33:16 -07001462}
1463
Wei Wang35732d02017-10-06 12:05:57 -07001464static int rt6_insert_exception(struct rt6_info *nrt,
David Ahern5012f0a2019-04-16 14:36:05 -07001465 const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001466{
David Ahern5e670d82018-04-17 17:33:14 -07001467 struct net *net = dev_net(nrt->dst.dev);
Wei Wang35732d02017-10-06 12:05:57 -07001468 struct rt6_exception_bucket *bucket;
1469 struct in6_addr *src_key = NULL;
1470 struct rt6_exception *rt6_ex;
David Ahern5012f0a2019-04-16 14:36:05 -07001471 struct fib6_info *f6i = res->f6i;
Wei Wang35732d02017-10-06 12:05:57 -07001472 int err = 0;
1473
Wei Wang35732d02017-10-06 12:05:57 -07001474 spin_lock_bh(&rt6_exception_lock);
1475
David Ahern5012f0a2019-04-16 14:36:05 -07001476 if (f6i->exception_bucket_flushed) {
Wei Wang35732d02017-10-06 12:05:57 -07001477 err = -EINVAL;
1478 goto out;
1479 }
1480
David Ahern5012f0a2019-04-16 14:36:05 -07001481 bucket = rcu_dereference_protected(f6i->rt6i_exception_bucket,
Wei Wang35732d02017-10-06 12:05:57 -07001482 lockdep_is_held(&rt6_exception_lock));
1483 if (!bucket) {
1484 bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
1485 GFP_ATOMIC);
1486 if (!bucket) {
1487 err = -ENOMEM;
1488 goto out;
1489 }
David Ahern5012f0a2019-04-16 14:36:05 -07001490 rcu_assign_pointer(f6i->rt6i_exception_bucket, bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001491 }
1492
1493#ifdef CONFIG_IPV6_SUBTREES
David Ahern5012f0a2019-04-16 14:36:05 -07001494 /* fib6_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001495 * and exception table is indexed by a hash of
David Ahern5012f0a2019-04-16 14:36:05 -07001496 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001497 * Otherwise, the exception table is indexed by
David Ahern5012f0a2019-04-16 14:36:05 -07001498 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001499 */
David Ahern5012f0a2019-04-16 14:36:05 -07001500 if (f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001501 src_key = &nrt->rt6i_src.addr;
1502#endif
David Ahern5012f0a2019-04-16 14:36:05 -07001503 /* rt6_mtu_change() might lower mtu on f6i.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001504 * Only insert this exception route if its mtu
David Ahern5012f0a2019-04-16 14:36:05 -07001505 * is less than f6i's mtu value.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001506 */
David Ahernb748f262019-04-16 14:36:06 -07001507 if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001508 err = -EINVAL;
1509 goto out;
1510 }
Wei Wang60006a42017-10-06 12:05:58 -07001511
Wei Wang35732d02017-10-06 12:05:57 -07001512 rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
1513 src_key);
1514 if (rt6_ex)
1515 rt6_remove_exception(bucket, rt6_ex);
1516
1517 rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
1518 if (!rt6_ex) {
1519 err = -ENOMEM;
1520 goto out;
1521 }
1522 rt6_ex->rt6i = nrt;
1523 rt6_ex->stamp = jiffies;
Wei Wang35732d02017-10-06 12:05:57 -07001524 hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
1525 bucket->depth++;
Wei Wang81eb8442017-10-06 12:06:11 -07001526 net->ipv6.rt6_stats->fib_rt_cache++;
Wei Wang35732d02017-10-06 12:05:57 -07001527
1528 if (bucket->depth > FIB6_MAX_DEPTH)
1529 rt6_exception_remove_oldest(bucket);
1530
1531out:
1532 spin_unlock_bh(&rt6_exception_lock);
1533
1534 /* Update fn->fn_sernum to invalidate all cached dst */
Paolo Abenib886d5f2017-10-19 16:07:10 +02001535 if (!err) {
David Ahern5012f0a2019-04-16 14:36:05 -07001536 spin_lock_bh(&f6i->fib6_table->tb6_lock);
1537 fib6_update_sernum(net, f6i);
1538 spin_unlock_bh(&f6i->fib6_table->tb6_lock);
Paolo Abenib886d5f2017-10-19 16:07:10 +02001539 fib6_force_start_gc(net);
1540 }
Wei Wang35732d02017-10-06 12:05:57 -07001541
1542 return err;
1543}
1544
David Ahern8d1c8022018-04-17 17:33:26 -07001545void rt6_flush_exceptions(struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001546{
1547 struct rt6_exception_bucket *bucket;
1548 struct rt6_exception *rt6_ex;
1549 struct hlist_node *tmp;
1550 int i;
1551
1552 spin_lock_bh(&rt6_exception_lock);
1553 /* Prevent rt6_insert_exception() to recreate the bucket list */
1554 rt->exception_bucket_flushed = 1;
1555
1556 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1557 lockdep_is_held(&rt6_exception_lock));
1558 if (!bucket)
1559 goto out;
1560
1561 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1562 hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist)
1563 rt6_remove_exception(bucket, rt6_ex);
1564 WARN_ON_ONCE(bucket->depth);
1565 bucket++;
1566 }
1567
1568out:
1569 spin_unlock_bh(&rt6_exception_lock);
1570}
1571
1572/* Find cached rt in the hash table inside passed in rt
1573 * Caller has to hold rcu_read_lock()
1574 */
David Ahern7e4b5122019-04-16 14:36:00 -07001575static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang510e2ce2019-05-16 13:30:54 -07001576 const struct in6_addr *daddr,
1577 const struct in6_addr *saddr)
Wei Wang35732d02017-10-06 12:05:57 -07001578{
Wei Wang510e2ce2019-05-16 13:30:54 -07001579 const struct in6_addr *src_key = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001580 struct rt6_exception_bucket *bucket;
Wei Wang35732d02017-10-06 12:05:57 -07001581 struct rt6_exception *rt6_ex;
David Ahern7e4b5122019-04-16 14:36:00 -07001582 struct rt6_info *ret = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001583
Wei Wang35732d02017-10-06 12:05:57 -07001584#ifdef CONFIG_IPV6_SUBTREES
David Ahern7e4b5122019-04-16 14:36:00 -07001585 /* fib6i_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001586 * and exception table is indexed by a hash of
David Ahern7e4b5122019-04-16 14:36:00 -07001587 * both fib6_dst and fib6_src.
Wei Wang510e2ce2019-05-16 13:30:54 -07001588 * However, the src addr used to create the hash
1589 * might not be exactly the passed in saddr which
1590 * is a /128 addr from the flow.
1591 * So we need to use f6i->fib6_src to redo lookup
1592 * if the passed in saddr does not find anything.
1593 * (See the logic in ip6_rt_cache_alloc() on how
1594 * rt->rt6i_src is updated.)
Wei Wang35732d02017-10-06 12:05:57 -07001595 */
David Ahern7e4b5122019-04-16 14:36:00 -07001596 if (res->f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001597 src_key = saddr;
Wei Wang510e2ce2019-05-16 13:30:54 -07001598find_ex:
Wei Wang35732d02017-10-06 12:05:57 -07001599#endif
Wei Wang510e2ce2019-05-16 13:30:54 -07001600 bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001601 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1602
1603 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
David Ahern7e4b5122019-04-16 14:36:00 -07001604 ret = rt6_ex->rt6i;
Wei Wang35732d02017-10-06 12:05:57 -07001605
Wei Wang510e2ce2019-05-16 13:30:54 -07001606#ifdef CONFIG_IPV6_SUBTREES
1607 /* Use fib6_src as src_key and redo lookup */
1608 if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) {
1609 src_key = &res->f6i->fib6_src.addr;
1610 goto find_ex;
1611 }
1612#endif
1613
David Ahern7e4b5122019-04-16 14:36:00 -07001614 return ret;
Wei Wang35732d02017-10-06 12:05:57 -07001615}
1616
1617/* Remove the passed in cached rt from the hash table that contains it */
David Ahern23fb93a2018-04-17 17:33:23 -07001618static int rt6_remove_exception_rt(struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001619{
Wei Wang35732d02017-10-06 12:05:57 -07001620 struct rt6_exception_bucket *bucket;
1621 struct in6_addr *src_key = NULL;
1622 struct rt6_exception *rt6_ex;
David Ahern8a14e462018-04-23 11:32:07 -07001623 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001624 int err;
1625
Eric Dumazet091311d2018-04-24 09:22:49 -07001626 from = rcu_dereference(rt->from);
Wei Wang35732d02017-10-06 12:05:57 -07001627 if (!from ||
Colin Ian King442d7132017-10-10 19:10:30 +01001628 !(rt->rt6i_flags & RTF_CACHE))
Wei Wang35732d02017-10-06 12:05:57 -07001629 return -EINVAL;
1630
1631 if (!rcu_access_pointer(from->rt6i_exception_bucket))
1632 return -ENOENT;
1633
1634 spin_lock_bh(&rt6_exception_lock);
1635 bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
1636 lockdep_is_held(&rt6_exception_lock));
1637#ifdef CONFIG_IPV6_SUBTREES
1638 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1639 * and exception table is indexed by a hash of
1640 * both rt6i_dst and rt6i_src.
1641 * Otherwise, the exception table is indexed by
1642 * a hash of only rt6i_dst.
1643 */
David Ahern93c2fb22018-04-18 15:38:59 -07001644 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001645 src_key = &rt->rt6i_src.addr;
1646#endif
1647 rt6_ex = __rt6_find_exception_spinlock(&bucket,
1648 &rt->rt6i_dst.addr,
1649 src_key);
1650 if (rt6_ex) {
1651 rt6_remove_exception(bucket, rt6_ex);
1652 err = 0;
1653 } else {
1654 err = -ENOENT;
1655 }
1656
1657 spin_unlock_bh(&rt6_exception_lock);
1658 return err;
1659}
1660
1661/* Find rt6_ex which contains the passed in rt cache and
1662 * refresh its stamp
1663 */
1664static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1665{
Wei Wang35732d02017-10-06 12:05:57 -07001666 struct rt6_exception_bucket *bucket;
1667 struct in6_addr *src_key = NULL;
1668 struct rt6_exception *rt6_ex;
Paolo Abeni193f3682019-02-21 11:19:41 +01001669 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001670
1671 rcu_read_lock();
Paolo Abeni193f3682019-02-21 11:19:41 +01001672 from = rcu_dereference(rt->from);
1673 if (!from || !(rt->rt6i_flags & RTF_CACHE))
1674 goto unlock;
1675
Wei Wang35732d02017-10-06 12:05:57 -07001676 bucket = rcu_dereference(from->rt6i_exception_bucket);
1677
1678#ifdef CONFIG_IPV6_SUBTREES
1679 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1680 * and exception table is indexed by a hash of
1681 * both rt6i_dst and rt6i_src.
1682 * Otherwise, the exception table is indexed by
1683 * a hash of only rt6i_dst.
1684 */
David Ahern93c2fb22018-04-18 15:38:59 -07001685 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001686 src_key = &rt->rt6i_src.addr;
1687#endif
1688 rt6_ex = __rt6_find_exception_rcu(&bucket,
1689 &rt->rt6i_dst.addr,
1690 src_key);
1691 if (rt6_ex)
1692 rt6_ex->stamp = jiffies;
1693
Paolo Abeni193f3682019-02-21 11:19:41 +01001694unlock:
Wei Wang35732d02017-10-06 12:05:57 -07001695 rcu_read_unlock();
1696}
1697
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001698static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1699 struct rt6_info *rt, int mtu)
1700{
1701 /* If the new MTU is lower than the route PMTU, this new MTU will be the
1702 * lowest MTU in the path: always allow updating the route PMTU to
1703 * reflect PMTU decreases.
1704 *
1705 * If the new MTU is higher, and the route PMTU is equal to the local
1706 * MTU, this means the old MTU is the lowest in the path, so allow
1707 * updating it: if other nodes now have lower MTUs, PMTU discovery will
1708 * handle this.
1709 */
1710
1711 if (dst_mtu(&rt->dst) >= mtu)
1712 return true;
1713
1714 if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
1715 return true;
1716
1717 return false;
1718}
1719
1720static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
David Ahern8d1c8022018-04-17 17:33:26 -07001721 struct fib6_info *rt, int mtu)
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001722{
1723 struct rt6_exception_bucket *bucket;
1724 struct rt6_exception *rt6_ex;
1725 int i;
1726
1727 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1728 lockdep_is_held(&rt6_exception_lock));
1729
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001730 if (!bucket)
1731 return;
1732
1733 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1734 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1735 struct rt6_info *entry = rt6_ex->rt6i;
1736
1737 /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
David Ahernd4ead6b2018-04-17 17:33:16 -07001738 * route), the metrics of its rt->from have already
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001739 * been updated.
1740 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001741 if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001742 rt6_mtu_change_route_allowed(idev, entry, mtu))
David Ahernd4ead6b2018-04-17 17:33:16 -07001743 dst_metric_set(&entry->dst, RTAX_MTU, mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001744 }
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001745 bucket++;
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001746 }
1747}
1748
Wei Wangb16cb452017-10-06 12:06:00 -07001749#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE)
1750
David Ahern8d1c8022018-04-17 17:33:26 -07001751static void rt6_exceptions_clean_tohost(struct fib6_info *rt,
Wei Wangb16cb452017-10-06 12:06:00 -07001752 struct in6_addr *gateway)
1753{
1754 struct rt6_exception_bucket *bucket;
1755 struct rt6_exception *rt6_ex;
1756 struct hlist_node *tmp;
1757 int i;
1758
1759 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1760 return;
1761
1762 spin_lock_bh(&rt6_exception_lock);
1763 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1764 lockdep_is_held(&rt6_exception_lock));
1765
1766 if (bucket) {
1767 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1768 hlist_for_each_entry_safe(rt6_ex, tmp,
1769 &bucket->chain, hlist) {
1770 struct rt6_info *entry = rt6_ex->rt6i;
1771
1772 if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
1773 RTF_CACHE_GATEWAY &&
1774 ipv6_addr_equal(gateway,
1775 &entry->rt6i_gateway)) {
1776 rt6_remove_exception(bucket, rt6_ex);
1777 }
1778 }
1779 bucket++;
1780 }
1781 }
1782
1783 spin_unlock_bh(&rt6_exception_lock);
1784}
1785
Wei Wangc757faa2017-10-06 12:06:01 -07001786static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
1787 struct rt6_exception *rt6_ex,
1788 struct fib6_gc_args *gc_args,
1789 unsigned long now)
1790{
1791 struct rt6_info *rt = rt6_ex->rt6i;
1792
Paolo Abeni1859bac2017-10-19 16:07:11 +02001793 /* we are pruning and obsoleting aged-out and non gateway exceptions
1794 * even if others have still references to them, so that on next
1795 * dst_check() such references can be dropped.
1796 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
1797 * expired, independently from their aging, as per RFC 8201 section 4
1798 */
Wei Wang31afeb42018-01-26 11:40:17 -08001799 if (!(rt->rt6i_flags & RTF_EXPIRES)) {
1800 if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
1801 RT6_TRACE("aging clone %p\n", rt);
1802 rt6_remove_exception(bucket, rt6_ex);
1803 return;
1804 }
1805 } else if (time_after(jiffies, rt->dst.expires)) {
1806 RT6_TRACE("purging expired route %p\n", rt);
Wei Wangc757faa2017-10-06 12:06:01 -07001807 rt6_remove_exception(bucket, rt6_ex);
1808 return;
Wei Wang31afeb42018-01-26 11:40:17 -08001809 }
1810
1811 if (rt->rt6i_flags & RTF_GATEWAY) {
Wei Wangc757faa2017-10-06 12:06:01 -07001812 struct neighbour *neigh;
1813 __u8 neigh_flags = 0;
1814
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001815 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
1816 if (neigh)
Wei Wangc757faa2017-10-06 12:06:01 -07001817 neigh_flags = neigh->flags;
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001818
Wei Wangc757faa2017-10-06 12:06:01 -07001819 if (!(neigh_flags & NTF_ROUTER)) {
1820 RT6_TRACE("purging route %p via non-router but gateway\n",
1821 rt);
1822 rt6_remove_exception(bucket, rt6_ex);
1823 return;
1824 }
1825 }
Wei Wang31afeb42018-01-26 11:40:17 -08001826
Wei Wangc757faa2017-10-06 12:06:01 -07001827 gc_args->more++;
1828}
1829
David Ahern8d1c8022018-04-17 17:33:26 -07001830void rt6_age_exceptions(struct fib6_info *rt,
Wei Wangc757faa2017-10-06 12:06:01 -07001831 struct fib6_gc_args *gc_args,
1832 unsigned long now)
1833{
1834 struct rt6_exception_bucket *bucket;
1835 struct rt6_exception *rt6_ex;
1836 struct hlist_node *tmp;
1837 int i;
1838
1839 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1840 return;
1841
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001842 rcu_read_lock_bh();
1843 spin_lock(&rt6_exception_lock);
Wei Wangc757faa2017-10-06 12:06:01 -07001844 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1845 lockdep_is_held(&rt6_exception_lock));
1846
1847 if (bucket) {
1848 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1849 hlist_for_each_entry_safe(rt6_ex, tmp,
1850 &bucket->chain, hlist) {
1851 rt6_age_examine_exception(bucket, rt6_ex,
1852 gc_args, now);
1853 }
1854 bucket++;
1855 }
1856 }
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001857 spin_unlock(&rt6_exception_lock);
1858 rcu_read_unlock_bh();
Wei Wangc757faa2017-10-06 12:06:01 -07001859}
1860
David Ahern1d053da2018-05-09 20:34:21 -07001861/* must be called with rcu lock held */
David Aherneffda4d2019-04-16 14:36:10 -07001862int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
1863 struct flowi6 *fl6, struct fib6_result *res, int strict)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864{
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001865 struct fib6_node *fn, *saved_fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866
David Ahern64547432018-05-09 20:34:19 -07001867 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001868 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869
David Ahernca254492015-10-12 11:47:10 -07001870 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1871 oif = 0;
1872
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001873redo_rt6_select:
David Aherneffda4d2019-04-16 14:36:10 -07001874 rt6_select(net, fn, oif, res, strict);
1875 if (res->f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001876 fn = fib6_backtrack(fn, &fl6->saddr);
1877 if (fn)
1878 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001879 else if (strict & RT6_LOOKUP_F_REACHABLE) {
1880 /* also consider unreachable route */
1881 strict &= ~RT6_LOOKUP_F_REACHABLE;
1882 fn = saved_fn;
1883 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001884 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001885 }
1886
David Aherneffda4d2019-04-16 14:36:10 -07001887 trace_fib6_table_lookup(net, res, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08001888
David Aherneffda4d2019-04-16 14:36:10 -07001889 return 0;
David Ahern1d053da2018-05-09 20:34:21 -07001890}
1891
1892struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1893 int oif, struct flowi6 *fl6,
1894 const struct sk_buff *skb, int flags)
1895{
David Ahernb1d40992019-04-16 14:35:59 -07001896 struct fib6_result res = {};
David Ahern1d053da2018-05-09 20:34:21 -07001897 struct rt6_info *rt;
1898 int strict = 0;
1899
1900 strict |= flags & RT6_LOOKUP_F_IFACE;
1901 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1902 if (net->ipv6.devconf_all->forwarding == 0)
1903 strict |= RT6_LOOKUP_F_REACHABLE;
1904
1905 rcu_read_lock();
1906
David Aherneffda4d2019-04-16 14:36:10 -07001907 fib6_table_lookup(net, table, oif, fl6, &res, strict);
David Ahernb1d40992019-04-16 14:35:59 -07001908 if (res.f6i == net->ipv6.fib6_null_entry) {
David Ahern421842e2018-04-17 17:33:18 -07001909 rt = net->ipv6.ip6_null_entry;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001910 rcu_read_unlock();
Wei Wangd3843fe2017-10-06 12:06:06 -07001911 dst_hold(&rt->dst);
Wei Wangd3843fe2017-10-06 12:06:06 -07001912 return rt;
David Ahern23fb93a2018-04-17 17:33:23 -07001913 }
1914
David Ahernb1d40992019-04-16 14:35:59 -07001915 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07001916
David Ahern23fb93a2018-04-17 17:33:23 -07001917 /*Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001918 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001919 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001920 if (ip6_hold_safe(net, &rt))
Wei Wangd3843fe2017-10-06 12:06:06 -07001921 dst_use_noref(&rt->dst, jiffies);
David Ahernd4ead6b2018-04-17 17:33:16 -07001922
Wei Wang66f5d6c2017-10-06 12:06:10 -07001923 rcu_read_unlock();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001924 return rt;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001925 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07001926 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001927 /* Create a RTF_CACHE clone which will not be
1928 * owned by the fib6 tree. It is for the special case where
1929 * the daddr in the skb during the neighbor look-up is different
1930 * from the fl6->daddr used to look-up route here.
1931 */
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001932 struct rt6_info *uncached_rt;
1933
David Ahern85bd05d2019-04-16 14:36:01 -07001934 uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001935
David Ahern4d85cd02018-04-20 15:37:59 -07001936 rcu_read_unlock();
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001937
Wei Wang1cfb71e2017-06-17 10:42:33 -07001938 if (uncached_rt) {
1939 /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
1940 * No need for another dst_hold()
1941 */
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07001942 rt6_uncached_list_add(uncached_rt);
Wei Wang81eb8442017-10-06 12:06:11 -07001943 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang1cfb71e2017-06-17 10:42:33 -07001944 } else {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001945 uncached_rt = net->ipv6.ip6_null_entry;
Wei Wang1cfb71e2017-06-17 10:42:33 -07001946 dst_hold(&uncached_rt->dst);
1947 }
David Ahernb8115802015-11-19 12:24:22 -08001948
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001949 return uncached_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001950 } else {
1951 /* Get a percpu copy */
1952
1953 struct rt6_info *pcpu_rt;
1954
Eric Dumazet951f7882017-10-08 21:07:18 -07001955 local_bh_disable();
David Aherndb3fede2019-04-16 14:36:03 -07001956 pcpu_rt = rt6_get_pcpu_route(&res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001957
David Ahern93531c62018-04-17 17:33:25 -07001958 if (!pcpu_rt)
David Aherndb3fede2019-04-16 14:36:03 -07001959 pcpu_rt = rt6_make_pcpu_route(net, &res);
David Ahern93531c62018-04-17 17:33:25 -07001960
Eric Dumazet951f7882017-10-08 21:07:18 -07001961 local_bh_enable();
1962 rcu_read_unlock();
David Ahernd4bea422018-05-09 20:34:24 -07001963
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001964 return pcpu_rt;
1965 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001966}
David Ahern9ff74382016-06-13 13:44:19 -07001967EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07001968
David Ahernb75cc8f2018-03-02 08:32:17 -08001969static struct rt6_info *ip6_pol_route_input(struct net *net,
1970 struct fib6_table *table,
1971 struct flowi6 *fl6,
1972 const struct sk_buff *skb,
1973 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001974{
David Ahernb75cc8f2018-03-02 08:32:17 -08001975 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001976}
1977
Mahesh Bandeward409b842016-09-16 12:59:08 -07001978struct dst_entry *ip6_route_input_lookup(struct net *net,
1979 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08001980 struct flowi6 *fl6,
1981 const struct sk_buff *skb,
1982 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001983{
1984 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
1985 flags |= RT6_LOOKUP_F_IFACE;
1986
David Ahernb75cc8f2018-03-02 08:32:17 -08001987 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001988}
Mahesh Bandeward409b842016-09-16 12:59:08 -07001989EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001990
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001991static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001992 struct flow_keys *keys,
1993 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001994{
1995 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1996 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001997 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001998 const struct ipv6hdr *inner_iph;
1999 const struct icmp6hdr *icmph;
2000 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07002001 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002002
2003 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
2004 goto out;
2005
Eric Dumazetcea67a22018-04-29 09:54:59 -07002006 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
2007 sizeof(_icmph), &_icmph);
2008 if (!icmph)
2009 goto out;
2010
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002011 if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
2012 icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
2013 icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
2014 icmph->icmp6_type != ICMPV6_PARAMPROB)
2015 goto out;
2016
2017 inner_iph = skb_header_pointer(skb,
2018 skb_transport_offset(skb) + sizeof(*icmph),
2019 sizeof(_inner_iph), &_inner_iph);
2020 if (!inner_iph)
2021 goto out;
2022
2023 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002024 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002025out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002026 if (_flkeys) {
2027 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
2028 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
2029 keys->tags.flow_label = _flkeys->tags.flow_label;
2030 keys->basic.ip_proto = _flkeys->basic.ip_proto;
2031 } else {
2032 keys->addrs.v6addrs.src = key_iph->saddr;
2033 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002034 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002035 keys->basic.ip_proto = key_iph->nexthdr;
2036 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002037}
2038
2039/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08002040u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
2041 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002042{
2043 struct flow_keys hash_keys;
David Ahern9a2a5372018-03-02 08:32:15 -08002044 u32 mhash;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002045
David S. Millerbbfa0472018-03-12 11:09:33 -04002046 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002047 case 0:
2048 memset(&hash_keys, 0, sizeof(hash_keys));
2049 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2050 if (skb) {
2051 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2052 } else {
2053 hash_keys.addrs.v6addrs.src = fl6->saddr;
2054 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002055 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002056 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2057 }
2058 break;
2059 case 1:
2060 if (skb) {
2061 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2062 struct flow_keys keys;
2063
2064 /* short-circuit if we already have L4 hash present */
2065 if (skb->l4_hash)
2066 return skb_get_hash_raw(skb) >> 1;
2067
2068 memset(&hash_keys, 0, sizeof(hash_keys));
2069
2070 if (!flkeys) {
2071 skb_flow_dissect_flow_keys(skb, &keys, flag);
2072 flkeys = &keys;
2073 }
2074 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2075 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2076 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2077 hash_keys.ports.src = flkeys->ports.src;
2078 hash_keys.ports.dst = flkeys->ports.dst;
2079 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2080 } else {
2081 memset(&hash_keys, 0, sizeof(hash_keys));
2082 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2083 hash_keys.addrs.v6addrs.src = fl6->saddr;
2084 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2085 hash_keys.ports.src = fl6->fl6_sport;
2086 hash_keys.ports.dst = fl6->fl6_dport;
2087 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2088 }
2089 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002090 }
David Ahern9a2a5372018-03-02 08:32:15 -08002091 mhash = flow_hash_from_keys(&hash_keys);
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002092
David Ahern9a2a5372018-03-02 08:32:15 -08002093 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002094}
2095
Thomas Grafc71099a2006-08-04 23:20:06 -07002096void ip6_route_input(struct sk_buff *skb)
2097{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002098 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002099 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002100 int flags = RT6_LOOKUP_F_HAS_SADDR;
Jiri Benc904af042015-08-20 13:56:31 +02002101 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002102 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002103 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002104 .daddr = iph->daddr,
2105 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002106 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002107 .flowi6_mark = skb->mark,
2108 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002109 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002110 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002111
Jiri Benc904af042015-08-20 13:56:31 +02002112 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002113 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002114 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002115
2116 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2117 flkeys = &_flkeys;
2118
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002119 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002120 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002121 skb_dst_drop(skb);
David Ahernb75cc8f2018-03-02 08:32:17 -08002122 skb_dst_set(skb,
2123 ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002124}
2125
David Ahernb75cc8f2018-03-02 08:32:17 -08002126static struct rt6_info *ip6_pol_route_output(struct net *net,
2127 struct fib6_table *table,
2128 struct flowi6 *fl6,
2129 const struct sk_buff *skb,
2130 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002131{
David Ahernb75cc8f2018-03-02 08:32:17 -08002132 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002133}
2134
Paolo Abeni6f21c962016-01-29 12:30:19 +01002135struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
2136 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002137{
David Ahernd46a9d62015-10-21 08:42:22 -07002138 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002139
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002140 if (ipv6_addr_type(&fl6->daddr) &
2141 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002142 struct dst_entry *dst;
2143
2144 dst = l3mdev_link_scope_lookup(net, fl6);
2145 if (dst)
2146 return dst;
2147 }
David Ahernca254492015-10-12 11:47:10 -07002148
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002149 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002150
David Ahernd46a9d62015-10-21 08:42:22 -07002151 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002152 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002153 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002154 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002155
David Ahernd46a9d62015-10-21 08:42:22 -07002156 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002157 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002158 else if (sk)
2159 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002160
David Ahernb75cc8f2018-03-02 08:32:17 -08002161 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002163EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164
David S. Miller2774c132011-03-01 14:59:04 -08002165struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002166{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002167 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002168 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002169 struct dst_entry *new = NULL;
2170
Wei Wang1dbe32522017-06-17 10:42:26 -07002171 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002172 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002173 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002174 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002175 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002176
Changli Gaod8d1f302010-06-10 23:31:35 -07002177 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002178 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002179 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002180 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002181
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002182 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002183
Wei Wang1dbe32522017-06-17 10:42:26 -07002184 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002185 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002186 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002187
2188 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2189#ifdef CONFIG_IPV6_SUBTREES
2190 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2191#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002192 }
2193
David S. Miller69ead7a2011-03-01 14:45:33 -08002194 dst_release(dst_orig);
2195 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002196}
David S. Miller14e50e52007-05-24 18:17:54 -07002197
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198/*
2199 * Destination cache support functions
2200 */
2201
David Ahern8d1c8022018-04-17 17:33:26 -07002202static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002203{
Steffen Klassert36143642017-08-25 09:05:42 +02002204 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002205
David Ahern8ae86972018-04-20 15:38:03 -07002206 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002207 return false;
2208
2209 if (fib6_check_expired(f6i))
2210 return false;
2211
2212 return true;
2213}
2214
David Aherna68886a2018-04-20 15:38:02 -07002215static struct dst_entry *rt6_check(struct rt6_info *rt,
2216 struct fib6_info *from,
2217 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002218{
Wei Wangc5cff852017-08-21 09:47:10 -07002219 u32 rt_cookie = 0;
2220
David Aherna68886a2018-04-20 15:38:02 -07002221 if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
David Ahern93531c62018-04-17 17:33:25 -07002222 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002223 return NULL;
2224
2225 if (rt6_check_expired(rt))
2226 return NULL;
2227
2228 return &rt->dst;
2229}
2230
David Aherna68886a2018-04-20 15:38:02 -07002231static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2232 struct fib6_info *from,
2233 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002234{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002235 if (!__rt6_check_expired(rt) &&
2236 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002237 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002238 return &rt->dst;
2239 else
2240 return NULL;
2241}
2242
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
2244{
David Aherna87b7dc2018-04-20 15:38:00 -07002245 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002246 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 struct rt6_info *rt;
2248
David Aherna87b7dc2018-04-20 15:38:00 -07002249 rt = container_of(dst, struct rt6_info, dst);
2250
2251 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002253 /* All IPV6 dsts are created with ->obsolete set to the value
2254 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2255 * into this function always.
2256 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002257
David Aherna68886a2018-04-20 15:38:02 -07002258 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002259
David Aherna68886a2018-04-20 15:38:02 -07002260 if (from && (rt->rt6i_flags & RTF_PCPU ||
2261 unlikely(!list_empty(&rt->rt6i_uncached))))
2262 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002263 else
David Aherna68886a2018-04-20 15:38:02 -07002264 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002265
2266 rcu_read_unlock();
2267
2268 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269}
2270
2271static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2272{
2273 struct rt6_info *rt = (struct rt6_info *) dst;
2274
2275 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002276 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002277 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002278 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002279 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002280 dst = NULL;
2281 }
David Ahernc3c14da2018-04-23 11:32:06 -07002282 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002283 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002285 dst = NULL;
2286 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002288 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289}
2290
2291static void ip6_link_failure(struct sk_buff *skb)
2292{
2293 struct rt6_info *rt;
2294
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002295 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296
Eric Dumazetadf30902009-06-02 05:19:30 +00002297 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002299 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002300 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002301 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002302 } else {
David Aherna68886a2018-04-20 15:38:02 -07002303 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002304 struct fib6_node *fn;
2305
David Aherna68886a2018-04-20 15:38:02 -07002306 from = rcu_dereference(rt->from);
2307 if (from) {
2308 fn = rcu_dereference(from->fib6_node);
2309 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2310 fn->fn_sernum = -1;
2311 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002312 }
David Ahern8a14e462018-04-23 11:32:07 -07002313 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314 }
2315}
2316
David Ahern6a3e0302018-04-20 15:37:57 -07002317static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2318{
David Aherna68886a2018-04-20 15:38:02 -07002319 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2320 struct fib6_info *from;
2321
2322 rcu_read_lock();
2323 from = rcu_dereference(rt0->from);
2324 if (from)
2325 rt0->dst.expires = from->expires;
2326 rcu_read_unlock();
2327 }
David Ahern6a3e0302018-04-20 15:37:57 -07002328
2329 dst_set_expires(&rt0->dst, timeout);
2330 rt0->rt6i_flags |= RTF_EXPIRES;
2331}
2332
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002333static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2334{
2335 struct net *net = dev_net(rt->dst.dev);
2336
David Ahernd4ead6b2018-04-17 17:33:16 -07002337 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002338 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002339 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2340}
2341
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002342static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2343{
2344 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002345 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002346}
2347
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002348static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2349 const struct ipv6hdr *iph, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002351 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002352 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353
Xin Long19bda362016-10-28 18:18:01 +08002354 if (dst_metric_locked(dst, RTAX_MTU))
2355 return;
2356
Julian Anastasov0dec8792017-02-06 23:14:16 +02002357 if (iph) {
2358 daddr = &iph->daddr;
2359 saddr = &iph->saddr;
2360 } else if (sk) {
2361 daddr = &sk->sk_v6_daddr;
2362 saddr = &inet6_sk(sk)->saddr;
2363 } else {
2364 daddr = NULL;
2365 saddr = NULL;
2366 }
2367 dst_confirm_neigh(dst, daddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002368 mtu = max_t(u32, mtu, IPV6_MIN_MTU);
2369 if (mtu >= dst_mtu(dst))
2370 return;
David S. Miller81aded22012-06-15 14:54:11 -07002371
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002372 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002373 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002374 /* update rt6_ex->stamp for cache */
2375 if (rt6->rt6i_flags & RTF_CACHE)
2376 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002377 } else if (daddr) {
David Ahern85bd05d2019-04-16 14:36:01 -07002378 struct fib6_result res = {};
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002379 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002380
David Ahern4d85cd02018-04-20 15:37:59 -07002381 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07002382 res.f6i = rcu_dereference(rt6->from);
2383 if (!res.f6i) {
Jonathan Lemon9c69a132019-04-14 14:21:29 -07002384 rcu_read_unlock();
2385 return;
2386 }
David Ahern85bd05d2019-04-16 14:36:01 -07002387 res.nh = &res.f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -07002388 res.fib6_flags = res.f6i->fib6_flags;
2389 res.fib6_type = res.f6i->fib6_type;
2390
David Ahern85bd05d2019-04-16 14:36:01 -07002391 nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002392 if (nrt6) {
2393 rt6_do_update_pmtu(nrt6, mtu);
David Ahern5012f0a2019-04-16 14:36:05 -07002394 if (rt6_insert_exception(nrt6, &res))
Wei Wang2b760fc2017-10-06 12:06:03 -07002395 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002396 }
David Aherna68886a2018-04-20 15:38:02 -07002397 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 }
2399}
2400
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002401static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2402 struct sk_buff *skb, u32 mtu)
2403{
2404 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
2405}
2406
David S. Miller42ae66c2012-06-15 20:01:57 -07002407void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002408 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002409{
2410 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2411 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002412 struct flowi6 fl6 = {
2413 .flowi6_oif = oif,
2414 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2415 .daddr = iph->daddr,
2416 .saddr = iph->saddr,
2417 .flowlabel = ip6_flowinfo(iph),
2418 .flowi6_uid = uid,
2419 };
David S. Miller81aded22012-06-15 14:54:11 -07002420
2421 dst = ip6_route_output(net, NULL, &fl6);
2422 if (!dst->error)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002423 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07002424 dst_release(dst);
2425}
2426EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2427
2428void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2429{
David Ahern7ddacfa2018-11-18 10:45:30 -08002430 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002431 struct dst_entry *dst;
2432
David Ahern7ddacfa2018-11-18 10:45:30 -08002433 if (!oif && skb->dev)
2434 oif = l3mdev_master_ifindex(skb->dev);
2435
2436 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002437
2438 dst = __sk_dst_get(sk);
2439 if (!dst || !dst->obsolete ||
2440 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2441 return;
2442
2443 bh_lock_sock(sk);
2444 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2445 ip6_datagram_dst_update(sk, false);
2446 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002447}
2448EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2449
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002450void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2451 const struct flowi6 *fl6)
2452{
2453#ifdef CONFIG_IPV6_SUBTREES
2454 struct ipv6_pinfo *np = inet6_sk(sk);
2455#endif
2456
2457 ip6_dst_store(sk, dst,
2458 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2459 &sk->sk_v6_daddr : NULL,
2460#ifdef CONFIG_IPV6_SUBTREES
2461 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2462 &np->saddr :
2463#endif
2464 NULL);
2465}
2466
David Ahern9b6b35a2019-04-16 14:36:02 -07002467static bool ip6_redirect_nh_match(const struct fib6_result *res,
David Ahern0b34eb02019-04-09 14:41:19 -07002468 struct flowi6 *fl6,
2469 const struct in6_addr *gw,
2470 struct rt6_info **ret)
2471{
David Ahern9b6b35a2019-04-16 14:36:02 -07002472 const struct fib6_nh *nh = res->nh;
2473
David Ahern0b34eb02019-04-09 14:41:19 -07002474 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2475 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2476 return false;
2477
2478 /* rt_cache's gateway might be different from its 'parent'
2479 * in the case of an ip redirect.
2480 * So we keep searching in the exception table if the gateway
2481 * is different.
2482 */
2483 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
2484 struct rt6_info *rt_cache;
2485
David Ahern9b6b35a2019-04-16 14:36:02 -07002486 rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
David Ahern0b34eb02019-04-09 14:41:19 -07002487 if (rt_cache &&
2488 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2489 *ret = rt_cache;
2490 return true;
2491 }
2492 return false;
2493 }
2494 return true;
2495}
2496
Duan Jiongb55b76b2013-09-04 19:44:21 +08002497/* Handle redirects */
2498struct ip6rd_flowi {
2499 struct flowi6 fl6;
2500 struct in6_addr gateway;
2501};
2502
2503static struct rt6_info *__ip6_route_redirect(struct net *net,
2504 struct fib6_table *table,
2505 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002506 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002507 int flags)
2508{
2509 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002510 struct rt6_info *ret = NULL;
David Ahern9b6b35a2019-04-16 14:36:02 -07002511 struct fib6_result res = {};
David Ahern8d1c8022018-04-17 17:33:26 -07002512 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002513 struct fib6_node *fn;
2514
David Ahern31680ac2019-05-22 15:12:18 -07002515 /* l3mdev_update_flow overrides oif if the device is enslaved; in
2516 * this case we must match on the real ingress device, so reset it
2517 */
2518 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
2519 fl6->flowi6_oif = skb->dev->ifindex;
2520
Duan Jiongb55b76b2013-09-04 19:44:21 +08002521 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002522 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002523 *
2524 * RFC 4861 specifies that redirects should only be
2525 * accepted if they come from the nexthop to the target.
2526 * Due to the way the routes are chosen, this notion
2527 * is a bit fuzzy and one might need to check all possible
2528 * routes.
2529 */
2530
Wei Wang66f5d6c2017-10-06 12:06:10 -07002531 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002532 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002533restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002534 for_each_fib6_node_rt_rcu(fn) {
David Ahern9b6b35a2019-04-16 14:36:02 -07002535 res.f6i = rt;
2536 res.nh = &rt->fib6_nh;
2537
David Ahern14895682018-04-17 17:33:17 -07002538 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002539 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002540 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002541 break;
David Ahern9b6b35a2019-04-16 14:36:02 -07002542 if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, &ret))
David Ahern0b34eb02019-04-09 14:41:19 -07002543 goto out;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002544 }
2545
2546 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002547 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002548 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002549 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002550 goto out;
2551 }
2552
David Ahern421842e2018-04-17 17:33:18 -07002553 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002554 fn = fib6_backtrack(fn, &fl6->saddr);
2555 if (fn)
2556 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002557 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002558
David Ahern9b6b35a2019-04-16 14:36:02 -07002559 res.f6i = rt;
2560 res.nh = &rt->fib6_nh;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002561out:
David Ahern7d21fec2019-04-16 14:36:11 -07002562 if (ret) {
David Ahern10585b42019-03-20 09:24:50 -07002563 ip6_hold_safe(net, &ret);
David Ahern7d21fec2019-04-16 14:36:11 -07002564 } else {
2565 res.fib6_flags = res.f6i->fib6_flags;
2566 res.fib6_type = res.f6i->fib6_type;
David Ahern9b6b35a2019-04-16 14:36:02 -07002567 ret = ip6_create_rt_rcu(&res);
David Ahern7d21fec2019-04-16 14:36:11 -07002568 }
Duan Jiongb55b76b2013-09-04 19:44:21 +08002569
Wei Wang66f5d6c2017-10-06 12:06:10 -07002570 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002571
David Ahern8ff2e5b2019-04-16 14:36:09 -07002572 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002573 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002574};
2575
2576static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002577 const struct flowi6 *fl6,
2578 const struct sk_buff *skb,
2579 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002580{
2581 int flags = RT6_LOOKUP_F_HAS_SADDR;
2582 struct ip6rd_flowi rdfl;
2583
2584 rdfl.fl6 = *fl6;
2585 rdfl.gateway = *gateway;
2586
David Ahernb75cc8f2018-03-02 08:32:17 -08002587 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002588 flags, __ip6_route_redirect);
2589}
2590
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002591void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
2592 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002593{
2594 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2595 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07002596 struct flowi6 fl6 = {
2597 .flowi6_iif = LOOPBACK_IFINDEX,
2598 .flowi6_oif = oif,
2599 .flowi6_mark = mark,
2600 .daddr = iph->daddr,
2601 .saddr = iph->saddr,
2602 .flowlabel = ip6_flowinfo(iph),
2603 .flowi6_uid = uid,
2604 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002605
David Ahernb75cc8f2018-03-02 08:32:17 -08002606 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002607 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002608 dst_release(dst);
2609}
2610EXPORT_SYMBOL_GPL(ip6_redirect);
2611
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07002612void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08002613{
2614 const struct ipv6hdr *iph = ipv6_hdr(skb);
2615 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
2616 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002617 struct flowi6 fl6 = {
2618 .flowi6_iif = LOOPBACK_IFINDEX,
2619 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002620 .daddr = msg->dest,
2621 .saddr = iph->daddr,
2622 .flowi6_uid = sock_net_uid(net, NULL),
2623 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08002624
David Ahernb75cc8f2018-03-02 08:32:17 -08002625 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002626 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08002627 dst_release(dst);
2628}
2629
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002630void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
2631{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002632 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
2633 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002634}
2635EXPORT_SYMBOL_GPL(ip6_sk_redirect);
2636
David S. Miller0dbaee32010-12-13 12:52:14 -08002637static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638{
David S. Miller0dbaee32010-12-13 12:52:14 -08002639 struct net_device *dev = dst->dev;
2640 unsigned int mtu = dst_mtu(dst);
2641 struct net *net = dev_net(dev);
2642
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
2644
Daniel Lezcano55786892008-03-04 13:47:47 -08002645 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
2646 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647
2648 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002649 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
2650 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
2651 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652 * rely only on pmtu discovery"
2653 */
2654 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
2655 mtu = IPV6_MAXPLEN;
2656 return mtu;
2657}
2658
Steffen Klassertebb762f2011-11-23 02:12:51 +00002659static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08002660{
David S. Millerd33e4552010-12-14 13:01:14 -08002661 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07002662 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002663
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002664 mtu = dst_metric_raw(dst, RTAX_MTU);
2665 if (mtu)
2666 goto out;
2667
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002668 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08002669
2670 rcu_read_lock();
2671 idev = __in6_dev_get(dst->dev);
2672 if (idev)
2673 mtu = idev->cnf.mtu6;
2674 rcu_read_unlock();
2675
Eric Dumazet30f78d82014-04-10 21:23:36 -07002676out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07002677 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2678
2679 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08002680}
2681
David Ahern901731b2018-05-21 09:08:14 -07002682/* MTU selection:
2683 * 1. mtu on route is locked - use it
2684 * 2. mtu from nexthop exception
2685 * 3. mtu from egress device
2686 *
2687 * based on ip6_dst_mtu_forward and exception logic of
2688 * rt6_find_cached_rt; called with rcu_read_lock
2689 */
David Ahernb748f262019-04-16 14:36:06 -07002690u32 ip6_mtu_from_fib6(const struct fib6_result *res,
2691 const struct in6_addr *daddr,
2692 const struct in6_addr *saddr)
David Ahern901731b2018-05-21 09:08:14 -07002693{
David Ahernb748f262019-04-16 14:36:06 -07002694 const struct fib6_nh *nh = res->nh;
2695 struct fib6_info *f6i = res->f6i;
David Ahern901731b2018-05-21 09:08:14 -07002696 struct inet6_dev *idev;
Wei Wang510e2ce2019-05-16 13:30:54 -07002697 struct rt6_info *rt;
David Ahern901731b2018-05-21 09:08:14 -07002698 u32 mtu = 0;
2699
2700 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
2701 mtu = f6i->fib6_pmtu;
2702 if (mtu)
2703 goto out;
2704 }
2705
Wei Wang510e2ce2019-05-16 13:30:54 -07002706 rt = rt6_find_cached_rt(res, daddr, saddr);
2707 if (unlikely(rt)) {
2708 mtu = dst_metric_raw(&rt->dst, RTAX_MTU);
2709 } else {
David Ahernb748f262019-04-16 14:36:06 -07002710 struct net_device *dev = nh->fib_nh_dev;
David Ahern901731b2018-05-21 09:08:14 -07002711
2712 mtu = IPV6_MIN_MTU;
2713 idev = __in6_dev_get(dev);
2714 if (idev && idev->cnf.mtu6 > mtu)
2715 mtu = idev->cnf.mtu6;
2716 }
2717
2718 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2719out:
David Ahernb748f262019-04-16 14:36:06 -07002720 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahern901731b2018-05-21 09:08:14 -07002721}
2722
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08002723struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05002724 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725{
David S. Miller87a11572011-12-06 17:04:13 -05002726 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727 struct rt6_info *rt;
2728 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002729 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730
David S. Miller38308472011-12-03 18:02:47 -05002731 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00002732 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
Martin KaFai Lauad706862015-08-14 11:05:52 -07002734 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05002735 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05002737 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738 goto out;
2739 }
2740
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002741 rt->dst.flags |= DST_HOST;
Brendan McGrath588753f2017-12-13 22:14:57 +11002742 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002743 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03002744 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05002745 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002746 rt->rt6i_dst.plen = 128;
2747 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08002748 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749
Ido Schimmel4c981e22018-01-07 12:45:04 +02002750 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07002751 * do proper release of the net_device
2752 */
2753 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002754 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755
David S. Miller87a11572011-12-06 17:04:13 -05002756 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
2757
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758out:
David S. Miller87a11572011-12-06 17:04:13 -05002759 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760}
2761
Daniel Lezcano569d3642008-01-18 03:56:57 -08002762static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002764 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08002765 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
2766 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
2767 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
2768 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
2769 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002770 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771
Eric Dumazetfc66f952010-10-08 06:37:34 +00002772 entries = dst_entries_get_fast(ops);
Michal Kubeček49a18d82013-08-01 10:04:24 +02002773 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00002774 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 goto out;
2776
Benjamin Thery6891a342008-03-04 13:49:47 -08002777 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08002778 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002779 entries = dst_entries_get_slow(ops);
2780 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08002781 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08002783 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002784 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785}
2786
David Ahern8c145862016-04-24 21:26:04 -07002787static struct rt6_info *ip6_nh_lookup_table(struct net *net,
2788 struct fib6_config *cfg,
David Ahernf4797b32018-01-25 16:55:08 -08002789 const struct in6_addr *gw_addr,
2790 u32 tbid, int flags)
David Ahern8c145862016-04-24 21:26:04 -07002791{
2792 struct flowi6 fl6 = {
2793 .flowi6_oif = cfg->fc_ifindex,
2794 .daddr = *gw_addr,
2795 .saddr = cfg->fc_prefsrc,
2796 };
2797 struct fib6_table *table;
2798 struct rt6_info *rt;
David Ahern8c145862016-04-24 21:26:04 -07002799
David Ahernf4797b32018-01-25 16:55:08 -08002800 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07002801 if (!table)
2802 return NULL;
2803
2804 if (!ipv6_addr_any(&cfg->fc_prefsrc))
2805 flags |= RT6_LOOKUP_F_HAS_SADDR;
2806
David Ahernf4797b32018-01-25 16:55:08 -08002807 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahernb75cc8f2018-03-02 08:32:17 -08002808 rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07002809
2810 /* if table lookup failed, fall back to full lookup */
2811 if (rt == net->ipv6.ip6_null_entry) {
2812 ip6_rt_put(rt);
2813 rt = NULL;
2814 }
2815
2816 return rt;
2817}
2818
David Ahernfc1e64e2018-01-25 16:55:09 -08002819static int ip6_route_check_nh_onlink(struct net *net,
2820 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07002821 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08002822 struct netlink_ext_ack *extack)
2823{
David Ahern44750f82018-02-06 13:17:06 -08002824 u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08002825 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2826 u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002827 struct fib6_info *from;
David Ahernfc1e64e2018-01-25 16:55:09 -08002828 struct rt6_info *grt;
2829 int err;
2830
2831 err = 0;
2832 grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
2833 if (grt) {
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002834 rcu_read_lock();
2835 from = rcu_dereference(grt->from);
David Ahern58e354c2018-02-06 12:14:12 -08002836 if (!grt->dst.error &&
David Ahern4ed591c2018-10-24 13:58:39 -07002837 /* ignore match if it is the default route */
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002838 from && !ipv6_addr_any(&from->fib6_dst.addr) &&
David Ahern58e354c2018-02-06 12:14:12 -08002839 (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
David Ahern44750f82018-02-06 13:17:06 -08002840 NL_SET_ERR_MSG(extack,
2841 "Nexthop has invalid gateway or device mismatch");
David Ahernfc1e64e2018-01-25 16:55:09 -08002842 err = -EINVAL;
2843 }
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002844 rcu_read_unlock();
David Ahernfc1e64e2018-01-25 16:55:09 -08002845
2846 ip6_rt_put(grt);
2847 }
2848
2849 return err;
2850}
2851
David Ahern1edce992018-01-25 16:55:07 -08002852static int ip6_route_check_nh(struct net *net,
2853 struct fib6_config *cfg,
2854 struct net_device **_dev,
2855 struct inet6_dev **idev)
2856{
2857 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2858 struct net_device *dev = _dev ? *_dev : NULL;
2859 struct rt6_info *grt = NULL;
2860 int err = -EHOSTUNREACH;
2861
2862 if (cfg->fc_table) {
David Ahernf4797b32018-01-25 16:55:08 -08002863 int flags = RT6_LOOKUP_F_IFACE;
2864
2865 grt = ip6_nh_lookup_table(net, cfg, gw_addr,
2866 cfg->fc_table, flags);
David Ahern1edce992018-01-25 16:55:07 -08002867 if (grt) {
2868 if (grt->rt6i_flags & RTF_GATEWAY ||
2869 (dev && dev != grt->dst.dev)) {
2870 ip6_rt_put(grt);
2871 grt = NULL;
2872 }
2873 }
2874 }
2875
2876 if (!grt)
David Ahernb75cc8f2018-03-02 08:32:17 -08002877 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1);
David Ahern1edce992018-01-25 16:55:07 -08002878
2879 if (!grt)
2880 goto out;
2881
2882 if (dev) {
2883 if (dev != grt->dst.dev) {
2884 ip6_rt_put(grt);
2885 goto out;
2886 }
2887 } else {
2888 *_dev = dev = grt->dst.dev;
2889 *idev = grt->rt6i_idev;
2890 dev_hold(dev);
2891 in6_dev_hold(grt->rt6i_idev);
2892 }
2893
2894 if (!(grt->rt6i_flags & RTF_GATEWAY))
2895 err = 0;
2896
2897 ip6_rt_put(grt);
2898
2899out:
2900 return err;
2901}
2902
David Ahern9fbb7042018-03-13 08:29:36 -07002903static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
2904 struct net_device **_dev, struct inet6_dev **idev,
2905 struct netlink_ext_ack *extack)
2906{
2907 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2908 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07002909 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07002910 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07002911 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07002912 int err = -EINVAL;
2913
2914 /* if gw_addr is local we will fail to detect this in case
2915 * address is still TENTATIVE (DAD in progress). rt6_lookup()
2916 * will return already-added prefix route via interface that
2917 * prefix route was assigned to, which might be non-loopback.
2918 */
David Ahern232378e2018-03-13 08:29:37 -07002919 if (dev &&
2920 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2921 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07002922 goto out;
2923 }
2924
2925 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
2926 /* IPv6 strictly inhibits using not link-local
2927 * addresses as nexthop address.
2928 * Otherwise, router will not able to send redirects.
2929 * It is very good, but in some (rare!) circumstances
2930 * (SIT, PtP, NBMA NOARP links) it is handy to allow
2931 * some exceptions. --ANK
2932 * We allow IPv4-mapped nexthops to support RFC4798-type
2933 * addressing
2934 */
2935 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
2936 NL_SET_ERR_MSG(extack, "Invalid gateway address");
2937 goto out;
2938 }
2939
2940 if (cfg->fc_flags & RTNH_F_ONLINK)
2941 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
2942 else
2943 err = ip6_route_check_nh(net, cfg, _dev, idev);
2944
2945 if (err)
2946 goto out;
2947 }
2948
2949 /* reload in case device was changed */
2950 dev = *_dev;
2951
2952 err = -EINVAL;
2953 if (!dev) {
2954 NL_SET_ERR_MSG(extack, "Egress device not specified");
2955 goto out;
2956 } else if (dev->flags & IFF_LOOPBACK) {
2957 NL_SET_ERR_MSG(extack,
2958 "Egress device can not be loopback device for this route");
2959 goto out;
2960 }
David Ahern232378e2018-03-13 08:29:37 -07002961
2962 /* if we did not check gw_addr above, do so now that the
2963 * egress device has been resolved.
2964 */
2965 if (need_addr_check &&
2966 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2967 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2968 goto out;
2969 }
2970
David Ahern9fbb7042018-03-13 08:29:36 -07002971 err = 0;
2972out:
2973 return err;
2974}
2975
David Ahern83c442512019-03-27 20:53:50 -07002976static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
2977{
2978 if ((flags & RTF_REJECT) ||
2979 (dev && (dev->flags & IFF_LOOPBACK) &&
2980 !(addr_type & IPV6_ADDR_LOOPBACK) &&
2981 !(flags & RTF_LOCAL)))
2982 return true;
2983
2984 return false;
2985}
2986
2987int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
2988 struct fib6_config *cfg, gfp_t gfp_flags,
2989 struct netlink_ext_ack *extack)
2990{
2991 struct net_device *dev = NULL;
2992 struct inet6_dev *idev = NULL;
2993 int addr_type;
2994 int err;
2995
David Ahernf1741732019-03-27 20:53:57 -07002996 fib6_nh->fib_nh_family = AF_INET6;
2997
David Ahern83c442512019-03-27 20:53:50 -07002998 err = -ENODEV;
2999 if (cfg->fc_ifindex) {
3000 dev = dev_get_by_index(net, cfg->fc_ifindex);
3001 if (!dev)
3002 goto out;
3003 idev = in6_dev_get(dev);
3004 if (!idev)
3005 goto out;
3006 }
3007
3008 if (cfg->fc_flags & RTNH_F_ONLINK) {
3009 if (!dev) {
3010 NL_SET_ERR_MSG(extack,
3011 "Nexthop device required for onlink");
3012 goto out;
3013 }
3014
3015 if (!(dev->flags & IFF_UP)) {
3016 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3017 err = -ENETDOWN;
3018 goto out;
3019 }
3020
David Ahernad1601a2019-03-27 20:53:56 -07003021 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07003022 }
3023
David Ahernad1601a2019-03-27 20:53:56 -07003024 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07003025
3026 /* We cannot add true routes via loopback here,
3027 * they would result in kernel looping; promote them to reject routes
3028 */
3029 addr_type = ipv6_addr_type(&cfg->fc_dst);
3030 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
3031 /* hold loopback dev/idev if we haven't done so. */
3032 if (dev != net->loopback_dev) {
3033 if (dev) {
3034 dev_put(dev);
3035 in6_dev_put(idev);
3036 }
3037 dev = net->loopback_dev;
3038 dev_hold(dev);
3039 idev = in6_dev_get(dev);
3040 if (!idev) {
3041 err = -ENODEV;
3042 goto out;
3043 }
3044 }
3045 goto set_dev;
3046 }
3047
3048 if (cfg->fc_flags & RTF_GATEWAY) {
3049 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
3050 if (err)
3051 goto out;
3052
David Ahernad1601a2019-03-27 20:53:56 -07003053 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07003054 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07003055 }
3056
3057 err = -ENODEV;
3058 if (!dev)
3059 goto out;
3060
3061 if (idev->cnf.disable_ipv6) {
3062 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3063 err = -EACCES;
3064 goto out;
3065 }
3066
3067 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3068 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3069 err = -ENETDOWN;
3070 goto out;
3071 }
3072
3073 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3074 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003075 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003076
David Ahern979e2762019-03-27 20:53:58 -07003077 err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
3078 cfg->fc_encap_type, cfg, gfp_flags, extack);
3079 if (err)
3080 goto out;
David Ahern83c442512019-03-27 20:53:50 -07003081set_dev:
David Ahernad1601a2019-03-27 20:53:56 -07003082 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003083 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003084 err = 0;
3085out:
3086 if (idev)
3087 in6_dev_put(idev);
3088
3089 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003090 lwtstate_put(fib6_nh->fib_nh_lws);
3091 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003092 if (dev)
3093 dev_put(dev);
3094 }
3095
3096 return err;
3097}
3098
David Aherndac7d0f2019-03-27 20:53:51 -07003099void fib6_nh_release(struct fib6_nh *fib6_nh)
3100{
David Ahern979e2762019-03-27 20:53:58 -07003101 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003102}
3103
David Ahern8d1c8022018-04-17 17:33:26 -07003104static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003105 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003106 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107{
Daniel Lezcano55786892008-03-04 13:47:47 -08003108 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003109 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003110 struct fib6_table *table;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003111 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003112 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003113
David Ahern557c44b2017-04-19 14:19:43 -07003114 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003115 if (cfg->fc_flags & RTF_PCPU) {
3116 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003117 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003118 }
David Ahern557c44b2017-04-19 14:19:43 -07003119
Wei Wang2ea23522017-10-27 17:30:12 -07003120 /* RTF_CACHE is an internal flag; can not be set by userspace */
3121 if (cfg->fc_flags & RTF_CACHE) {
3122 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3123 goto out;
3124 }
3125
David Aherne8478e82018-04-17 17:33:13 -07003126 if (cfg->fc_type > RTN_MAX) {
3127 NL_SET_ERR_MSG(extack, "Invalid route type");
3128 goto out;
3129 }
3130
David Ahernd5d531c2017-05-21 10:12:05 -06003131 if (cfg->fc_dst_len > 128) {
3132 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003133 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003134 }
3135 if (cfg->fc_src_len > 128) {
3136 NL_SET_ERR_MSG(extack, "Invalid source address length");
3137 goto out;
3138 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003139#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003140 if (cfg->fc_src_len) {
3141 NL_SET_ERR_MSG(extack,
3142 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003143 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003144 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145#endif
David Ahernfc1e64e2018-01-25 16:55:09 -08003146
Matti Vaittinend71314b2011-11-14 00:14:49 +00003147 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003148 if (cfg->fc_nlinfo.nlh &&
3149 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003150 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003151 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003152 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003153 table = fib6_new_table(net, cfg->fc_table);
3154 }
3155 } else {
3156 table = fib6_new_table(net, cfg->fc_table);
3157 }
David S. Miller38308472011-12-03 18:02:47 -05003158
3159 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003160 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003161
David Ahern93531c62018-04-17 17:33:25 -07003162 err = -ENOMEM;
3163 rt = fib6_info_alloc(gfp_flags);
3164 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003166
David Ahernd7e774f2018-11-06 12:51:15 -08003167 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3168 extack);
David Ahern767a2212018-10-04 20:07:51 -07003169 if (IS_ERR(rt->fib6_metrics)) {
3170 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003171 /* Do not leave garbage there. */
3172 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003173 goto out;
3174 }
3175
David Ahern93531c62018-04-17 17:33:25 -07003176 if (cfg->fc_flags & RTF_ADDRCONF)
3177 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178
Gao feng1716a962012-04-06 00:13:10 +00003179 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003180 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003181 clock_t_to_jiffies(cfg->fc_expires));
3182 else
David Ahern14895682018-04-17 17:33:17 -07003183 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003184
Thomas Graf86872cb2006-08-22 00:01:08 -07003185 if (cfg->fc_protocol == RTPROT_UNSPEC)
3186 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003187 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003188
David Ahern83c442512019-03-27 20:53:50 -07003189 rt->fib6_table = table;
3190 rt->fib6_metric = cfg->fc_metric;
3191 rt->fib6_type = cfg->fc_type;
David Ahern2b2450c2019-03-27 20:53:52 -07003192 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003193
David Ahern93c2fb22018-04-18 15:38:59 -07003194 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3195 rt->fib6_dst.plen = cfg->fc_dst_len;
3196 if (rt->fib6_dst.plen == 128)
David Ahern3b6761d2018-04-17 17:33:20 -07003197 rt->dst_host = true;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003198
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003200 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3201 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202#endif
David Ahern83c442512019-03-27 20:53:50 -07003203 err = fib6_nh_init(net, &rt->fib6_nh, cfg, gfp_flags, extack);
3204 if (err)
3205 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206
3207 /* We cannot add true routes via loopback here,
David Ahern83c442512019-03-27 20:53:50 -07003208 * they would result in kernel looping; promote them to reject routes
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 */
David Ahern83c442512019-03-27 20:53:50 -07003210 addr_type = ipv6_addr_type(&cfg->fc_dst);
David Ahernad1601a2019-03-27 20:53:56 -07003211 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh.fib_nh_dev, addr_type))
David Ahern83c442512019-03-27 20:53:50 -07003212 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
David Ahern955ec4c2018-01-24 19:45:29 -08003213
Daniel Walterc3968a82011-04-13 21:10:57 +00003214 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahern83c442512019-03-27 20:53:50 -07003215 struct net_device *dev = fib6_info_nh_dev(rt);
3216
Daniel Walterc3968a82011-04-13 21:10:57 +00003217 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003218 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003219 err = -EINVAL;
3220 goto out;
3221 }
David Ahern93c2fb22018-04-18 15:38:59 -07003222 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3223 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003224 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003225 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003226
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003227 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228out:
David Ahern93531c62018-04-17 17:33:25 -07003229 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003230 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003231}
3232
David Ahernacb54e32018-04-17 17:33:22 -07003233int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003234 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003235{
David Ahern8d1c8022018-04-17 17:33:26 -07003236 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003237 int err;
3238
David Ahernacb54e32018-04-17 17:33:22 -07003239 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003240 if (IS_ERR(rt))
3241 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003242
David Ahernd4ead6b2018-04-17 17:33:16 -07003243 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003244 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003245
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246 return err;
3247}
3248
David Ahern8d1c8022018-04-17 17:33:26 -07003249static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003250{
David Ahernafb1d4b52018-04-17 17:33:11 -07003251 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003252 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003253 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003254
David Ahern421842e2018-04-17 17:33:18 -07003255 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003256 err = -ENOENT;
3257 goto out;
3258 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003259
David Ahern93c2fb22018-04-18 15:38:59 -07003260 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003261 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003262 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003263 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003264
Gao feng6825a262012-09-19 19:25:34 +00003265out:
David Ahern93531c62018-04-17 17:33:25 -07003266 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003267 return err;
3268}
3269
David Ahern8d1c8022018-04-17 17:33:26 -07003270int ip6_del_rt(struct net *net, struct fib6_info *rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003271{
David Ahernafb1d4b52018-04-17 17:33:11 -07003272 struct nl_info info = { .nl_net = net };
3273
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003274 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003275}
3276
David Ahern8d1c8022018-04-17 17:33:26 -07003277static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003278{
3279 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003280 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003281 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003282 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003283 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003284
David Ahern421842e2018-04-17 17:33:18 -07003285 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003286 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003287 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003288 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003289
David Ahern93c2fb22018-04-18 15:38:59 -07003290 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003291 struct fib6_info *sibling, *next_sibling;
David Ahern0ae81332017-02-02 12:37:08 -08003292
David Ahern16a16cd2017-02-02 12:37:11 -08003293 /* prefer to send a single notification with all hops */
3294 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3295 if (skb) {
3296 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3297
David Ahernd4ead6b2018-04-17 17:33:16 -07003298 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003299 NULL, NULL, 0, RTM_DELROUTE,
3300 info->portid, seq, 0) < 0) {
3301 kfree_skb(skb);
3302 skb = NULL;
3303 } else
3304 info->skip_notify = 1;
3305 }
3306
David Ahern0ae81332017-02-02 12:37:08 -08003307 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003308 &rt->fib6_siblings,
3309 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003310 err = fib6_del(sibling, info);
3311 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003312 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003313 }
3314 }
3315
3316 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003317out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003318 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003319out_put:
David Ahern93531c62018-04-17 17:33:25 -07003320 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003321
3322 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003323 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003324 info->nlh, gfp_any());
3325 }
David Ahern0ae81332017-02-02 12:37:08 -08003326 return err;
3327}
3328
David Ahern23fb93a2018-04-17 17:33:23 -07003329static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
3330{
3331 int rc = -ESRCH;
3332
3333 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3334 goto out;
3335
3336 if (cfg->fc_flags & RTF_GATEWAY &&
3337 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3338 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003339
3340 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003341out:
3342 return rc;
3343}
3344
David Ahern333c4302017-05-21 10:12:04 -06003345static int ip6_route_del(struct fib6_config *cfg,
3346 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003347{
David Ahern8d1c8022018-04-17 17:33:26 -07003348 struct rt6_info *rt_cache;
Thomas Grafc71099a2006-08-04 23:20:06 -07003349 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003350 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003351 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352 int err = -ESRCH;
3353
Daniel Lezcano55786892008-03-04 13:47:47 -08003354 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003355 if (!table) {
3356 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003357 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003358 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359
Wei Wang66f5d6c2017-10-06 12:06:10 -07003360 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003361
3362 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003363 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003364 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003365 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003366
Linus Torvalds1da177e2005-04-16 15:20:36 -07003367 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003368 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003369 struct fib6_nh *nh;
3370
Wei Wang2b760fc2017-10-06 12:06:03 -07003371 if (cfg->fc_flags & RTF_CACHE) {
David Ahern7e4b5122019-04-16 14:36:00 -07003372 struct fib6_result res = {
3373 .f6i = rt,
3374 };
David Ahern23fb93a2018-04-17 17:33:23 -07003375 int rc;
3376
David Ahern7e4b5122019-04-16 14:36:00 -07003377 rt_cache = rt6_find_cached_rt(&res,
3378 &cfg->fc_dst,
Wei Wang2b760fc2017-10-06 12:06:03 -07003379 &cfg->fc_src);
David Ahern23fb93a2018-04-17 17:33:23 -07003380 if (rt_cache) {
3381 rc = ip6_del_cached_rt(rt_cache, cfg);
Eric Dumazet9e575012018-05-09 10:05:46 -07003382 if (rc != -ESRCH) {
3383 rcu_read_unlock();
David Ahern23fb93a2018-04-17 17:33:23 -07003384 return rc;
Eric Dumazet9e575012018-05-09 10:05:46 -07003385 }
David Ahern23fb93a2018-04-17 17:33:23 -07003386 }
3387 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003388 }
David Ahernad1601a2019-03-27 20:53:56 -07003389
3390 nh = &rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003391 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003392 (!nh->fib_nh_dev ||
3393 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003394 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003395 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003396 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003397 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003398 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003399 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003400 if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
Mantas Mc2ed1882016-12-16 10:30:59 +02003401 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003402 if (!fib6_info_hold_safe(rt))
3403 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003404 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003405
David Ahern0ae81332017-02-02 12:37:08 -08003406 /* if gateway was specified only delete the one hop */
3407 if (cfg->fc_flags & RTF_GATEWAY)
3408 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3409
3410 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003411 }
3412 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003413 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003414
3415 return err;
3416}
3417
David S. Miller6700c272012-07-17 03:29:28 -07003418static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003419{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003420 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003421 struct rt6_info *rt, *nrt = NULL;
David Ahern85bd05d2019-04-16 14:36:01 -07003422 struct fib6_result res = {};
David S. Millere8599ff2012-07-11 23:43:53 -07003423 struct ndisc_options ndopts;
3424 struct inet6_dev *in6_dev;
3425 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003426 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003427 int optlen, on_link;
3428 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07003429
Simon Horman29a3cad2013-05-28 20:34:26 +00003430 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003431 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07003432
3433 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07003434 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003435 return;
3436 }
3437
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003438 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07003439
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003440 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003441 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003442 return;
3443 }
3444
David S. Miller6e157b62012-07-12 00:05:02 -07003445 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003446 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003447 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003448 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07003449 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003450 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003451 return;
3452 }
3453
3454 in6_dev = __in6_dev_get(skb->dev);
3455 if (!in6_dev)
3456 return;
3457 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
3458 return;
3459
3460 /* RFC2461 8.1:
3461 * The IP source address of the Redirect MUST be the same as the current
3462 * first-hop router for the specified ICMP Destination Address.
3463 */
3464
Alexander Aringf997c552016-06-15 21:20:23 +02003465 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003466 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
3467 return;
3468 }
David S. Miller6e157b62012-07-12 00:05:02 -07003469
3470 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003471 if (ndopts.nd_opts_tgt_lladdr) {
3472 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
3473 skb->dev);
3474 if (!lladdr) {
3475 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
3476 return;
3477 }
3478 }
3479
David S. Miller6e157b62012-07-12 00:05:02 -07003480 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01003481 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07003482 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
3483 return;
3484 }
3485
3486 /* Redirect received -> path was valid.
3487 * Look, redirects are sent only in response to data packets,
3488 * so that this nexthop apparently is reachable. --ANK
3489 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02003490 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07003491
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003492 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07003493 if (!neigh)
3494 return;
3495
Linus Torvalds1da177e2005-04-16 15:20:36 -07003496 /*
3497 * We have finally decided to accept it.
3498 */
3499
Alexander Aringf997c552016-06-15 21:20:23 +02003500 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003501 NEIGH_UPDATE_F_WEAK_OVERRIDE|
3502 NEIGH_UPDATE_F_OVERRIDE|
3503 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02003504 NEIGH_UPDATE_F_ISROUTER)),
3505 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003506
David Ahern4d85cd02018-04-20 15:37:59 -07003507 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07003508 res.f6i = rcu_dereference(rt->from);
David S. Millerff24e492019-05-02 22:14:21 -04003509 if (!res.f6i)
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07003510 goto out;
David Ahern8a14e462018-04-23 11:32:07 -07003511
David Ahern85bd05d2019-04-16 14:36:01 -07003512 res.nh = &res.f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -07003513 res.fib6_flags = res.f6i->fib6_flags;
3514 res.fib6_type = res.f6i->fib6_type;
David Ahern85bd05d2019-04-16 14:36:01 -07003515 nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05003516 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003517 goto out;
3518
3519 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
3520 if (on_link)
3521 nrt->rt6i_flags &= ~RTF_GATEWAY;
3522
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003523 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07003525 /* rt6_insert_exception() will take care of duplicated exceptions */
David Ahern5012f0a2019-04-16 14:36:05 -07003526 if (rt6_insert_exception(nrt, &res)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07003527 dst_release_immediate(&nrt->dst);
3528 goto out;
3529 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003530
Changli Gaod8d1f302010-06-10 23:31:35 -07003531 netevent.old = &rt->dst;
3532 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003533 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00003534 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07003535 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
3536
Linus Torvalds1da177e2005-04-16 15:20:36 -07003537out:
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07003538 rcu_read_unlock();
David S. Millere8599ff2012-07-11 23:43:53 -07003539 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07003540}
3541
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003542#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07003543static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003544 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003545 const struct in6_addr *gwaddr,
3546 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003547{
David Ahern830218c2016-10-24 10:52:35 -07003548 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
3549 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003550 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07003551 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003552 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003553
David Ahern830218c2016-10-24 10:52:35 -07003554 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003555 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003556 return NULL;
3557
Wei Wang66f5d6c2017-10-06 12:06:10 -07003558 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07003559 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003560 if (!fn)
3561 goto out;
3562
Wei Wang66f5d6c2017-10-06 12:06:10 -07003563 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003564 if (rt->fib6_nh.fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003565 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07003566 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahernbdf00462019-04-05 16:30:26 -07003567 !rt->fib6_nh.fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003568 continue;
David Ahernad1601a2019-03-27 20:53:56 -07003569 if (!ipv6_addr_equal(&rt->fib6_nh.fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003570 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003571 if (!fib6_info_hold_safe(rt))
3572 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003573 break;
3574 }
3575out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003576 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003577 return rt;
3578}
3579
David Ahern8d1c8022018-04-17 17:33:26 -07003580static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003581 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003582 const struct in6_addr *gwaddr,
3583 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00003584 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003585{
Thomas Graf86872cb2006-08-22 00:01:08 -07003586 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08003587 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07003588 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07003589 .fc_dst_len = prefixlen,
3590 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
3591 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003592 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003593 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003594 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08003595 .fc_nlinfo.nlh = NULL,
3596 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003597 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003598
David Ahern830218c2016-10-24 10:52:35 -07003599 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003600 cfg.fc_dst = *prefix;
3601 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07003602
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08003603 /* We should treat it as a default route if prefix length is 0. */
3604 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07003605 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003606
David Ahernacb54e32018-04-17 17:33:22 -07003607 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003608
David Ahern830218c2016-10-24 10:52:35 -07003609 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003610}
3611#endif
3612
David Ahern8d1c8022018-04-17 17:33:26 -07003613struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003614 const struct in6_addr *addr,
3615 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003616{
David Ahern830218c2016-10-24 10:52:35 -07003617 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07003618 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07003619 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003620
David Ahernafb1d4b52018-04-17 17:33:11 -07003621 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003622 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003623 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003624
Wei Wang66f5d6c2017-10-06 12:06:10 -07003625 rcu_read_lock();
3626 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernad1601a2019-03-27 20:53:56 -07003627 struct fib6_nh *nh = &rt->fib6_nh;
3628
3629 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07003630 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07003631 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003632 break;
3633 }
Wei Wange873e4b2018-07-21 20:56:32 -07003634 if (rt && !fib6_info_hold_safe(rt))
3635 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003636 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003637 return rt;
3638}
3639
David Ahern8d1c8022018-04-17 17:33:26 -07003640struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003641 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08003642 struct net_device *dev,
3643 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003644{
Thomas Graf86872cb2006-08-22 00:01:08 -07003645 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07003646 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08003647 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07003648 .fc_ifindex = dev->ifindex,
3649 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
3650 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003651 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003652 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003653 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08003654 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07003655 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003656 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003657
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003658 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659
David Ahernacb54e32018-04-17 17:33:22 -07003660 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07003661 struct fib6_table *table;
3662
3663 table = fib6_get_table(dev_net(dev), cfg.fc_table);
3664 if (table)
3665 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
3666 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667
David Ahernafb1d4b52018-04-17 17:33:11 -07003668 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003669}
3670
David Ahernafb1d4b52018-04-17 17:33:11 -07003671static void __rt6_purge_dflt_routers(struct net *net,
3672 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003673{
David Ahern8d1c8022018-04-17 17:33:26 -07003674 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003675
3676restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003677 rcu_read_lock();
3678 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07003679 struct net_device *dev = fib6_info_nh_dev(rt);
3680 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
3681
David Ahern93c2fb22018-04-18 15:38:59 -07003682 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07003683 (!idev || idev->cnf.accept_ra != 2) &&
3684 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07003685 rcu_read_unlock();
3686 ip6_del_rt(net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687 goto restart;
3688 }
3689 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003690 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07003691
3692 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
3693}
3694
3695void rt6_purge_dflt_routers(struct net *net)
3696{
3697 struct fib6_table *table;
3698 struct hlist_head *head;
3699 unsigned int h;
3700
3701 rcu_read_lock();
3702
3703 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3704 head = &net->ipv6.fib_table_hash[h];
3705 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
3706 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07003707 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07003708 }
3709 }
3710
3711 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003712}
3713
Daniel Lezcano55786892008-03-04 13:47:47 -08003714static void rtmsg_to_fib6_config(struct net *net,
3715 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07003716 struct fib6_config *cfg)
3717{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003718 *cfg = (struct fib6_config){
3719 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
3720 : RT6_TABLE_MAIN,
3721 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07003722 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003723 .fc_expires = rtmsg->rtmsg_info,
3724 .fc_dst_len = rtmsg->rtmsg_dst_len,
3725 .fc_src_len = rtmsg->rtmsg_src_len,
3726 .fc_flags = rtmsg->rtmsg_flags,
3727 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07003728
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003729 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003730
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003731 .fc_dst = rtmsg->rtmsg_dst,
3732 .fc_src = rtmsg->rtmsg_src,
3733 .fc_gateway = rtmsg->rtmsg_gateway,
3734 };
Thomas Graf86872cb2006-08-22 00:01:08 -07003735}
3736
Daniel Lezcano55786892008-03-04 13:47:47 -08003737int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003738{
Thomas Graf86872cb2006-08-22 00:01:08 -07003739 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003740 struct in6_rtmsg rtmsg;
3741 int err;
3742
Ian Morris67ba4152014-08-24 21:53:10 +01003743 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003744 case SIOCADDRT: /* Add a route */
3745 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00003746 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747 return -EPERM;
3748 err = copy_from_user(&rtmsg, arg,
3749 sizeof(struct in6_rtmsg));
3750 if (err)
3751 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07003752
Daniel Lezcano55786892008-03-04 13:47:47 -08003753 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07003754
Linus Torvalds1da177e2005-04-16 15:20:36 -07003755 rtnl_lock();
3756 switch (cmd) {
3757 case SIOCADDRT:
David Ahernacb54e32018-04-17 17:33:22 -07003758 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003759 break;
3760 case SIOCDELRT:
David Ahern333c4302017-05-21 10:12:04 -06003761 err = ip6_route_del(&cfg, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762 break;
3763 default:
3764 err = -EINVAL;
3765 }
3766 rtnl_unlock();
3767
3768 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07003769 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770
3771 return -EINVAL;
3772}
3773
3774/*
3775 * Drop the packet on the floor
3776 */
3777
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07003778static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779{
Eric Dumazetadf30902009-06-02 05:19:30 +00003780 struct dst_entry *dst = skb_dst(skb);
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04003781 struct net *net = dev_net(dst->dev);
3782 struct inet6_dev *idev;
3783 int type;
3784
3785 if (netif_is_l3_master(skb->dev) &&
3786 dst->dev == net->loopback_dev)
3787 idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
3788 else
3789 idev = ip6_dst_idev(dst);
3790
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003791 switch (ipstats_mib_noroutes) {
3792 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07003793 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00003794 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04003795 IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003796 break;
3797 }
3798 /* FALLTHROUGH */
3799 case IPSTATS_MIB_OUTNOROUTES:
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04003800 IP6_INC_STATS(net, idev, ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003801 break;
3802 }
Stephen Suryaputra1d3fd8a2019-04-27 09:14:33 -04003803
3804 /* Start over by dropping the dst for l3mdev case */
3805 if (netif_is_l3_master(skb->dev))
3806 skb_dst_drop(skb);
3807
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00003808 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003809 kfree_skb(skb);
3810 return 0;
3811}
3812
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003813static int ip6_pkt_discard(struct sk_buff *skb)
3814{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003815 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003816}
3817
Eric W. Biedermanede20592015-10-07 16:48:47 -05003818static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819{
Eric Dumazetadf30902009-06-02 05:19:30 +00003820 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003821 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003822}
3823
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003824static int ip6_pkt_prohibit(struct sk_buff *skb)
3825{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003826 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003827}
3828
Eric W. Biedermanede20592015-10-07 16:48:47 -05003829static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003830{
Eric Dumazetadf30902009-06-02 05:19:30 +00003831 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003832 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003833}
3834
Linus Torvalds1da177e2005-04-16 15:20:36 -07003835/*
3836 * Allocate a dst for local (unicast / anycast) address.
3837 */
3838
David Ahern360a9882018-04-18 15:39:00 -07003839struct fib6_info *addrconf_f6i_alloc(struct net *net,
3840 struct inet6_dev *idev,
3841 const struct in6_addr *addr,
3842 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843{
David Ahernc7a1ce32019-03-21 05:21:35 -07003844 struct fib6_config cfg = {
3845 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
3846 .fc_ifindex = idev->dev->ifindex,
3847 .fc_flags = RTF_UP | RTF_ADDRCONF | RTF_NONEXTHOP,
3848 .fc_dst = *addr,
3849 .fc_dst_len = 128,
3850 .fc_protocol = RTPROT_KERNEL,
3851 .fc_nlinfo.nl_net = net,
3852 .fc_ignore_dev_down = true,
3853 };
David Ahern5f02ce242016-09-10 12:09:54 -07003854
David Aherne8478e82018-04-17 17:33:13 -07003855 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07003856 cfg.fc_type = RTN_ANYCAST;
3857 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07003858 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07003859 cfg.fc_type = RTN_LOCAL;
3860 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07003861 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003862
David Ahernc7a1ce32019-03-21 05:21:35 -07003863 return ip6_route_info_create(&cfg, gfp_flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003864}
3865
Daniel Walterc3968a82011-04-13 21:10:57 +00003866/* remove deleted ip from prefsrc entries */
3867struct arg_dev_net_ip {
3868 struct net_device *dev;
3869 struct net *net;
3870 struct in6_addr *addr;
3871};
3872
David Ahern8d1c8022018-04-17 17:33:26 -07003873static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00003874{
3875 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
3876 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
3877 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
3878
David Ahernad1601a2019-03-27 20:53:56 -07003879 if (((void *)rt->fib6_nh.fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07003880 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07003881 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07003882 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003883 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07003884 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07003885 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003886 }
3887 return 0;
3888}
3889
3890void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
3891{
3892 struct net *net = dev_net(ifp->idev->dev);
3893 struct arg_dev_net_ip adni = {
3894 .dev = ifp->idev->dev,
3895 .net = net,
3896 .addr = &ifp->addr,
3897 };
Li RongQing0c3584d2013-12-27 16:32:38 +08003898 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00003899}
3900
David Ahern2b2450c2019-03-27 20:53:52 -07003901#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003902
3903/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07003904static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003905{
3906 struct in6_addr *gateway = (struct in6_addr *)arg;
3907
David Ahern93c2fb22018-04-18 15:38:59 -07003908 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Ahernbdf00462019-04-05 16:30:26 -07003909 rt->fib6_nh.fib_nh_gw_family &&
David Ahernad1601a2019-03-27 20:53:56 -07003910 ipv6_addr_equal(gateway, &rt->fib6_nh.fib_nh_gw6)) {
Duan Jiongbe7a0102014-05-15 15:56:14 +08003911 return -1;
3912 }
Wei Wangb16cb452017-10-06 12:06:00 -07003913
3914 /* Further clean up cached routes in exception table.
3915 * This is needed because cached route may have a different
3916 * gateway than its 'parent' in the case of an ip redirect.
3917 */
3918 rt6_exceptions_clean_tohost(rt, gateway);
3919
Duan Jiongbe7a0102014-05-15 15:56:14 +08003920 return 0;
3921}
3922
3923void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
3924{
3925 fib6_clean_all(net, fib6_clean_tohost, gateway);
3926}
3927
Ido Schimmel2127d952018-01-07 12:45:03 +02003928struct arg_netdev_event {
3929 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003930 union {
David Ahernecc56632019-04-23 08:48:09 -07003931 unsigned char nh_flags;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003932 unsigned long event;
3933 };
Ido Schimmel2127d952018-01-07 12:45:03 +02003934};
3935
David Ahern8d1c8022018-04-17 17:33:26 -07003936static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003937{
David Ahern8d1c8022018-04-17 17:33:26 -07003938 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003939 struct fib6_node *fn;
3940
David Ahern93c2fb22018-04-18 15:38:59 -07003941 fn = rcu_dereference_protected(rt->fib6_node,
3942 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003943 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07003944 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003945 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07003946 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07003947 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003948 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07003949 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07003950 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003951 }
3952
3953 return NULL;
3954}
3955
David Ahern8d1c8022018-04-17 17:33:26 -07003956static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003957{
David Ahernad1601a2019-03-27 20:53:56 -07003958 if (rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ||
3959 (rt->fib6_nh.fib_nh_flags & RTNH_F_LINKDOWN &&
3960 ip6_ignore_linkdown(rt->fib6_nh.fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003961 return true;
3962
3963 return false;
3964}
3965
David Ahern8d1c8022018-04-17 17:33:26 -07003966static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003967{
David Ahern8d1c8022018-04-17 17:33:26 -07003968 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003969 int total = 0;
3970
3971 if (!rt6_is_dead(rt))
David Ahernad1601a2019-03-27 20:53:56 -07003972 total += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003973
David Ahern93c2fb22018-04-18 15:38:59 -07003974 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003975 if (!rt6_is_dead(iter))
David Ahernad1601a2019-03-27 20:53:56 -07003976 total += iter->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003977 }
3978
3979 return total;
3980}
3981
David Ahern8d1c8022018-04-17 17:33:26 -07003982static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003983{
3984 int upper_bound = -1;
3985
3986 if (!rt6_is_dead(rt)) {
David Ahernad1601a2019-03-27 20:53:56 -07003987 *weight += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003988 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
3989 total) - 1;
3990 }
David Ahernad1601a2019-03-27 20:53:56 -07003991 atomic_set(&rt->fib6_nh.fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003992}
3993
David Ahern8d1c8022018-04-17 17:33:26 -07003994static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003995{
David Ahern8d1c8022018-04-17 17:33:26 -07003996 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003997 int weight = 0;
3998
3999 rt6_upper_bound_set(rt, &weight, total);
4000
David Ahern93c2fb22018-04-18 15:38:59 -07004001 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004002 rt6_upper_bound_set(iter, &weight, total);
4003}
4004
David Ahern8d1c8022018-04-17 17:33:26 -07004005void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004006{
David Ahern8d1c8022018-04-17 17:33:26 -07004007 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004008 int total;
4009
4010 /* In case the entire multipath route was marked for flushing,
4011 * then there is no need to rebalance upon the removal of every
4012 * sibling route.
4013 */
David Ahern93c2fb22018-04-18 15:38:59 -07004014 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004015 return;
4016
4017 /* During lookup routes are evaluated in order, so we need to
4018 * make sure upper bounds are assigned from the first sibling
4019 * onwards.
4020 */
4021 first = rt6_multipath_first_sibling(rt);
4022 if (WARN_ON_ONCE(!first))
4023 return;
4024
4025 total = rt6_multipath_total_weight(first);
4026 rt6_multipath_upper_bound_set(first, total);
4027}
4028
David Ahern8d1c8022018-04-17 17:33:26 -07004029static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02004030{
4031 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07004032 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02004033
David Ahernad1601a2019-03-27 20:53:56 -07004034 if (rt != net->ipv6.fib6_null_entry &&
4035 rt->fib6_nh.fib_nh_dev == arg->dev) {
4036 rt->fib6_nh.fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07004037 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004038 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004039 }
Ido Schimmel2127d952018-01-07 12:45:03 +02004040
4041 return 0;
4042}
4043
David Ahernecc56632019-04-23 08:48:09 -07004044void rt6_sync_up(struct net_device *dev, unsigned char nh_flags)
Ido Schimmel2127d952018-01-07 12:45:03 +02004045{
4046 struct arg_netdev_event arg = {
4047 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004048 {
4049 .nh_flags = nh_flags,
4050 },
Ido Schimmel2127d952018-01-07 12:45:03 +02004051 };
4052
4053 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
4054 arg.nh_flags |= RTNH_F_LINKDOWN;
4055
4056 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
4057}
4058
David Ahern8d1c8022018-04-17 17:33:26 -07004059static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004060 const struct net_device *dev)
4061{
David Ahern8d1c8022018-04-17 17:33:26 -07004062 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004063
David Ahernad1601a2019-03-27 20:53:56 -07004064 if (rt->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004065 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07004066 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004067 if (iter->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004068 return true;
4069
4070 return false;
4071}
4072
David Ahern8d1c8022018-04-17 17:33:26 -07004073static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004074{
David Ahern8d1c8022018-04-17 17:33:26 -07004075 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004076
4077 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004078 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004079 iter->should_flush = 1;
4080}
4081
David Ahern8d1c8022018-04-17 17:33:26 -07004082static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004083 const struct net_device *down_dev)
4084{
David Ahern8d1c8022018-04-17 17:33:26 -07004085 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004086 unsigned int dead = 0;
4087
David Ahernad1601a2019-03-27 20:53:56 -07004088 if (rt->fib6_nh.fib_nh_dev == down_dev ||
4089 rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004090 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004091 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004092 if (iter->fib6_nh.fib_nh_dev == down_dev ||
4093 iter->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004094 dead++;
4095
4096 return dead;
4097}
4098
David Ahern8d1c8022018-04-17 17:33:26 -07004099static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004100 const struct net_device *dev,
David Ahernecc56632019-04-23 08:48:09 -07004101 unsigned char nh_flags)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004102{
David Ahern8d1c8022018-04-17 17:33:26 -07004103 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004104
David Ahernad1601a2019-03-27 20:53:56 -07004105 if (rt->fib6_nh.fib_nh_dev == dev)
4106 rt->fib6_nh.fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004107 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004108 if (iter->fib6_nh.fib_nh_dev == dev)
4109 iter->fib6_nh.fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004110}
4111
David Aherna1a22c12017-01-18 07:40:36 -08004112/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004113static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004114{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004115 const struct arg_netdev_event *arg = p_arg;
4116 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004117 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004118
David Ahern421842e2018-04-17 17:33:18 -07004119 if (rt == net->ipv6.fib6_null_entry)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004120 return 0;
4121
4122 switch (arg->event) {
4123 case NETDEV_UNREGISTER:
David Ahernad1601a2019-03-27 20:53:56 -07004124 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004125 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004126 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004127 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004128 if (!rt->fib6_nsiblings)
David Ahernad1601a2019-03-27 20:53:56 -07004129 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004130 if (rt6_multipath_uses_dev(rt, dev)) {
4131 unsigned int count;
4132
4133 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004134 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004135 rt6_multipath_flush(rt);
4136 return -1;
4137 }
4138 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4139 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004140 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004141 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004142 }
4143 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004144 case NETDEV_CHANGE:
David Ahernad1601a2019-03-27 20:53:56 -07004145 if (rt->fib6_nh.fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004146 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004147 break;
David Ahernad1601a2019-03-27 20:53:56 -07004148 rt->fib6_nh.fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004149 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004150 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004151 }
David S. Millerc159d302011-12-26 15:24:36 -05004152
Linus Torvalds1da177e2005-04-16 15:20:36 -07004153 return 0;
4154}
4155
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004156void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004157{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004158 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004159 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004160 {
4161 .event = event,
4162 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004163 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004164 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004165
David Ahern7c6bb7d2018-10-11 20:17:21 -07004166 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4167 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4168 else
4169 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004170}
4171
4172void rt6_disable_ip(struct net_device *dev, unsigned long event)
4173{
4174 rt6_sync_down_dev(dev, event);
4175 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4176 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004177}
4178
Eric Dumazet95c96172012-04-15 05:58:06 +00004179struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004180 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004181 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004182};
4183
David Ahern8d1c8022018-04-17 17:33:26 -07004184static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004185{
4186 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4187 struct inet6_dev *idev;
4188
4189 /* In IPv6 pmtu discovery is not optional,
4190 so that RTAX_MTU lock cannot disable it.
4191 We still use this lock to block changes
4192 caused by addrconf/ndisc.
4193 */
4194
4195 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004196 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004197 return 0;
4198
4199 /* For administrative MTU increase, there is no way to discover
4200 IPv6 PMTU increase, so PMTU increase should be updated here.
4201 Since RFC 1981 doesn't include administrative MTU increase
4202 update PMTU increase is a MUST. (i.e. jumbo frame)
4203 */
David Ahernad1601a2019-03-27 20:53:56 -07004204 if (rt->fib6_nh.fib_nh_dev == arg->dev &&
David Ahernd4ead6b2018-04-17 17:33:16 -07004205 !fib6_metric_locked(rt, RTAX_MTU)) {
4206 u32 mtu = rt->fib6_pmtu;
4207
4208 if (mtu >= arg->mtu ||
4209 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4210 fib6_metric_set(rt, RTAX_MTU, arg->mtu);
4211
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004212 spin_lock_bh(&rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01004213 rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004214 spin_unlock_bh(&rt6_exception_lock);
Simon Arlott566cfd82007-07-26 00:09:55 -07004215 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004216 return 0;
4217}
4218
Eric Dumazet95c96172012-04-15 05:58:06 +00004219void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004220{
Thomas Grafc71099a2006-08-04 23:20:06 -07004221 struct rt6_mtu_change_arg arg = {
4222 .dev = dev,
4223 .mtu = mtu,
4224 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004225
Li RongQing0c3584d2013-12-27 16:32:38 +08004226 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004227}
4228
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004229static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07004230 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004231 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004232 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004233 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004234 [RTA_PRIORITY] = { .type = NLA_U32 },
4235 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004236 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004237 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004238 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4239 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004240 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004241 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004242 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004243 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004244 [RTA_IP_PROTO] = { .type = NLA_U8 },
4245 [RTA_SPORT] = { .type = NLA_U16 },
4246 [RTA_DPORT] = { .type = NLA_U16 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004247};
4248
4249static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004250 struct fib6_config *cfg,
4251 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004252{
Thomas Graf86872cb2006-08-22 00:01:08 -07004253 struct rtmsg *rtm;
4254 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004255 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004256 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004257
Johannes Berg8cb08172019-04-26 14:07:28 +02004258 err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
4259 rtm_ipv6_policy, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004260 if (err < 0)
4261 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004262
Thomas Graf86872cb2006-08-22 00:01:08 -07004263 err = -EINVAL;
4264 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004265
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004266 *cfg = (struct fib6_config){
4267 .fc_table = rtm->rtm_table,
4268 .fc_dst_len = rtm->rtm_dst_len,
4269 .fc_src_len = rtm->rtm_src_len,
4270 .fc_flags = RTF_UP,
4271 .fc_protocol = rtm->rtm_protocol,
4272 .fc_type = rtm->rtm_type,
4273
4274 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4275 .fc_nlinfo.nlh = nlh,
4276 .fc_nlinfo.nl_net = sock_net(skb->sk),
4277 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004278
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004279 if (rtm->rtm_type == RTN_UNREACHABLE ||
4280 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004281 rtm->rtm_type == RTN_PROHIBIT ||
4282 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004283 cfg->fc_flags |= RTF_REJECT;
4284
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004285 if (rtm->rtm_type == RTN_LOCAL)
4286 cfg->fc_flags |= RTF_LOCAL;
4287
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004288 if (rtm->rtm_flags & RTM_F_CLONED)
4289 cfg->fc_flags |= RTF_CACHE;
4290
David Ahernfc1e64e2018-01-25 16:55:09 -08004291 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4292
Thomas Graf86872cb2006-08-22 00:01:08 -07004293 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004294 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004295 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004296 }
David Aherne3818542019-02-26 09:00:03 -08004297 if (tb[RTA_VIA]) {
4298 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4299 goto errout;
4300 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004301
4302 if (tb[RTA_DST]) {
4303 int plen = (rtm->rtm_dst_len + 7) >> 3;
4304
4305 if (nla_len(tb[RTA_DST]) < plen)
4306 goto errout;
4307
4308 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004309 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004310
4311 if (tb[RTA_SRC]) {
4312 int plen = (rtm->rtm_src_len + 7) >> 3;
4313
4314 if (nla_len(tb[RTA_SRC]) < plen)
4315 goto errout;
4316
4317 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004318 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004319
Daniel Walterc3968a82011-04-13 21:10:57 +00004320 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004321 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004322
Thomas Graf86872cb2006-08-22 00:01:08 -07004323 if (tb[RTA_OIF])
4324 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4325
4326 if (tb[RTA_PRIORITY])
4327 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4328
4329 if (tb[RTA_METRICS]) {
4330 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4331 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004332 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004333
4334 if (tb[RTA_TABLE])
4335 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4336
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004337 if (tb[RTA_MULTIPATH]) {
4338 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4339 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004340
4341 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004342 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004343 if (err < 0)
4344 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004345 }
4346
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004347 if (tb[RTA_PREF]) {
4348 pref = nla_get_u8(tb[RTA_PREF]);
4349 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4350 pref != ICMPV6_ROUTER_PREF_HIGH)
4351 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4352 cfg->fc_flags |= RTF_PREF(pref);
4353 }
4354
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004355 if (tb[RTA_ENCAP])
4356 cfg->fc_encap = tb[RTA_ENCAP];
4357
David Ahern9ed59592017-01-17 14:57:36 -08004358 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004359 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4360
David Ahernc255bd62017-05-27 16:19:27 -06004361 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004362 if (err < 0)
4363 goto errout;
4364 }
4365
Xin Long32bc2012015-12-16 17:50:11 +08004366 if (tb[RTA_EXPIRES]) {
4367 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4368
4369 if (addrconf_finite_timeout(timeout)) {
4370 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
4371 cfg->fc_flags |= RTF_EXPIRES;
4372 }
4373 }
4374
Thomas Graf86872cb2006-08-22 00:01:08 -07004375 err = 0;
4376errout:
4377 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004378}
4379
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004380struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07004381 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004382 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004383 struct list_head next;
4384};
4385
David Ahernd4ead6b2018-04-17 17:33:16 -07004386static int ip6_route_info_append(struct net *net,
4387 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07004388 struct fib6_info *rt,
4389 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004390{
4391 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004392 int err = -EEXIST;
4393
4394 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004395 /* check if fib6_info already exists */
4396 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004397 return err;
4398 }
4399
4400 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
4401 if (!nh)
4402 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07004403 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004404 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
4405 list_add_tail(&nh->next, rt6_nh_list);
4406
4407 return 0;
4408}
4409
David Ahern8d1c8022018-04-17 17:33:26 -07004410static void ip6_route_mpath_notify(struct fib6_info *rt,
4411 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08004412 struct nl_info *info,
4413 __u16 nlflags)
4414{
4415 /* if this is an APPEND route, then rt points to the first route
4416 * inserted and rt_last points to last route inserted. Userspace
4417 * wants a consistent dump of the route which starts at the first
4418 * nexthop. Since sibling routes are always added at the end of
4419 * the list, find the first sibling of the last route appended
4420 */
David Ahern93c2fb22018-04-18 15:38:59 -07004421 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
4422 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07004423 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07004424 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08004425 }
4426
4427 if (rt)
4428 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4429}
4430
David Ahern333c4302017-05-21 10:12:04 -06004431static int ip6_route_multipath_add(struct fib6_config *cfg,
4432 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004433{
David Ahern8d1c8022018-04-17 17:33:26 -07004434 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08004435 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004436 struct fib6_config r_cfg;
4437 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07004438 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004439 struct rt6_nh *err_nh;
4440 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08004441 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004442 int remaining;
4443 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004444 int err = 1;
4445 int nhn = 0;
4446 int replace = (cfg->fc_nlinfo.nlh &&
4447 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
4448 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004449
David Ahern3b1137f2017-02-02 12:37:10 -08004450 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
4451 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
4452 nlflags |= NLM_F_APPEND;
4453
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02004454 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004455 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004456
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004457 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07004458 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004459 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004460 while (rtnh_ok(rtnh, remaining)) {
4461 memcpy(&r_cfg, cfg, sizeof(*cfg));
4462 if (rtnh->rtnh_ifindex)
4463 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4464
4465 attrlen = rtnh_attrlen(rtnh);
4466 if (attrlen > 0) {
4467 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4468
4469 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4470 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004471 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004472 r_cfg.fc_flags |= RTF_GATEWAY;
4473 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004474 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
4475 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
4476 if (nla)
4477 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004478 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004479
David Ahern68e2ffd2018-03-20 10:06:59 -07004480 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07004481 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004482 if (IS_ERR(rt)) {
4483 err = PTR_ERR(rt);
4484 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004485 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004486 }
David Ahernb5d2d752018-07-15 09:35:19 -07004487 if (!rt6_qualify_for_ecmp(rt)) {
4488 err = -EINVAL;
4489 NL_SET_ERR_MSG(extack,
4490 "Device only routes can not be added for IPv6 using the multipath API.");
4491 fib6_info_release(rt);
4492 goto cleanup;
4493 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004494
David Ahernad1601a2019-03-27 20:53:56 -07004495 rt->fib6_nh.fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02004496
David Ahernd4ead6b2018-04-17 17:33:16 -07004497 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
4498 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004499 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07004500 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004501 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004502 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004503
4504 rtnh = rtnh_next(rtnh, &remaining);
4505 }
4506
David Ahern3b1137f2017-02-02 12:37:10 -08004507 /* for add and replace send one notification with all nexthops.
4508 * Skip the notification in fib6_add_rt2node and send one with
4509 * the full route when done
4510 */
4511 info->skip_notify = 1;
4512
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004513 err_nh = NULL;
4514 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004515 err = __ip6_ins_rt(nh->fib6_info, info, extack);
4516 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08004517
David Ahernf7225172018-06-04 13:41:42 -07004518 if (!err) {
4519 /* save reference to last route successfully inserted */
4520 rt_last = nh->fib6_info;
4521
4522 /* save reference to first route for notification */
4523 if (!rt_notif)
4524 rt_notif = nh->fib6_info;
4525 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004526
David Ahern8d1c8022018-04-17 17:33:26 -07004527 /* nh->fib6_info is used or freed at this point, reset to NULL*/
4528 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004529 if (err) {
4530 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08004531 NL_SET_ERR_MSG_MOD(extack,
4532 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004533 err_nh = nh;
4534 goto add_errout;
4535 }
4536
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004537 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02004538 * these flags after the first nexthop: if there is a collision,
4539 * we have already failed to add the first nexthop:
4540 * fib6_add_rt2node() has rejected it; when replacing, old
4541 * nexthops have been replaced by first new, the rest should
4542 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004543 */
Michal Kubeček27596472015-05-18 20:54:00 +02004544 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
4545 NLM_F_REPLACE);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004546 nhn++;
4547 }
4548
David Ahern3b1137f2017-02-02 12:37:10 -08004549 /* success ... tell user about new route */
4550 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004551 goto cleanup;
4552
4553add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08004554 /* send notification for routes that were added so that
4555 * the delete notifications sent by ip6_route_del are
4556 * coherent
4557 */
4558 if (rt_notif)
4559 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
4560
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004561 /* Delete routes that were already added */
4562 list_for_each_entry(nh, &rt6_nh_list, next) {
4563 if (err_nh == nh)
4564 break;
David Ahern333c4302017-05-21 10:12:04 -06004565 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004566 }
4567
4568cleanup:
4569 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004570 if (nh->fib6_info)
4571 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004572 list_del(&nh->next);
4573 kfree(nh);
4574 }
4575
4576 return err;
4577}
4578
David Ahern333c4302017-05-21 10:12:04 -06004579static int ip6_route_multipath_del(struct fib6_config *cfg,
4580 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004581{
4582 struct fib6_config r_cfg;
4583 struct rtnexthop *rtnh;
4584 int remaining;
4585 int attrlen;
4586 int err = 1, last_err = 0;
4587
4588 remaining = cfg->fc_mp_len;
4589 rtnh = (struct rtnexthop *)cfg->fc_mp;
4590
4591 /* Parse a Multipath Entry */
4592 while (rtnh_ok(rtnh, remaining)) {
4593 memcpy(&r_cfg, cfg, sizeof(*cfg));
4594 if (rtnh->rtnh_ifindex)
4595 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4596
4597 attrlen = rtnh_attrlen(rtnh);
4598 if (attrlen > 0) {
4599 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4600
4601 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4602 if (nla) {
4603 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
4604 r_cfg.fc_flags |= RTF_GATEWAY;
4605 }
4606 }
David Ahern333c4302017-05-21 10:12:04 -06004607 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004608 if (err)
4609 last_err = err;
4610
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004611 rtnh = rtnh_next(rtnh, &remaining);
4612 }
4613
4614 return last_err;
4615}
4616
David Ahernc21ef3e2017-04-16 09:48:24 -07004617static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4618 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004619{
Thomas Graf86872cb2006-08-22 00:01:08 -07004620 struct fib6_config cfg;
4621 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004622
David Ahern333c4302017-05-21 10:12:04 -06004623 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004624 if (err < 0)
4625 return err;
4626
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004627 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004628 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004629 else {
4630 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06004631 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004632 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004633}
4634
David Ahernc21ef3e2017-04-16 09:48:24 -07004635static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4636 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004637{
Thomas Graf86872cb2006-08-22 00:01:08 -07004638 struct fib6_config cfg;
4639 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004640
David Ahern333c4302017-05-21 10:12:04 -06004641 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004642 if (err < 0)
4643 return err;
4644
David Ahern67f69512019-03-21 05:21:34 -07004645 if (cfg.fc_metric == 0)
4646 cfg.fc_metric = IP6_RT_PRIO_USER;
4647
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004648 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004649 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004650 else
David Ahernacb54e32018-04-17 17:33:22 -07004651 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004652}
4653
David Ahern8d1c8022018-04-17 17:33:26 -07004654static size_t rt6_nlmsg_size(struct fib6_info *rt)
Thomas Graf339bf982006-11-10 14:10:15 -08004655{
David Ahernbeb1afac52017-02-02 12:37:09 -08004656 int nexthop_len = 0;
4657
David Ahern93c2fb22018-04-18 15:38:59 -07004658 if (rt->fib6_nsiblings) {
David Ahernbeb1afac52017-02-02 12:37:09 -08004659 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
4660 + NLA_ALIGN(sizeof(struct rtnexthop))
4661 + nla_total_size(16) /* RTA_GATEWAY */
David Ahernad1601a2019-03-27 20:53:56 -07004662 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08004663
David Ahern93c2fb22018-04-18 15:38:59 -07004664 nexthop_len *= rt->fib6_nsiblings;
David Ahernbeb1afac52017-02-02 12:37:09 -08004665 }
4666
Thomas Graf339bf982006-11-10 14:10:15 -08004667 return NLMSG_ALIGN(sizeof(struct rtmsg))
4668 + nla_total_size(16) /* RTA_SRC */
4669 + nla_total_size(16) /* RTA_DST */
4670 + nla_total_size(16) /* RTA_GATEWAY */
4671 + nla_total_size(16) /* RTA_PREFSRC */
4672 + nla_total_size(4) /* RTA_TABLE */
4673 + nla_total_size(4) /* RTA_IIF */
4674 + nla_total_size(4) /* RTA_OIF */
4675 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08004676 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01004677 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004678 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004679 + nla_total_size(1) /* RTA_PREF */
David Ahernad1601a2019-03-27 20:53:56 -07004680 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws)
David Ahernbeb1afac52017-02-02 12:37:09 -08004681 + nexthop_len;
4682}
4683
David Ahernd4ead6b2018-04-17 17:33:16 -07004684static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07004685 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07004686 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004687 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08004688 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004689{
Xin Long22d0bd82018-09-11 14:33:58 +08004690 struct rt6_info *rt6 = (struct rt6_info *)dst;
4691 struct rt6key *rt6_dst, *rt6_src;
4692 u32 *pmetrics, table, rt6_flags;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004693 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08004694 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07004695 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004696
Eric W. Biederman15e47302012-09-07 20:12:54 +00004697 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05004698 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08004699 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004700
Xin Long22d0bd82018-09-11 14:33:58 +08004701 if (rt6) {
4702 rt6_dst = &rt6->rt6i_dst;
4703 rt6_src = &rt6->rt6i_src;
4704 rt6_flags = rt6->rt6i_flags;
4705 } else {
4706 rt6_dst = &rt->fib6_dst;
4707 rt6_src = &rt->fib6_src;
4708 rt6_flags = rt->fib6_flags;
4709 }
4710
Thomas Graf2d7202b2006-08-22 00:01:27 -07004711 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004712 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08004713 rtm->rtm_dst_len = rt6_dst->plen;
4714 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004715 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07004716 if (rt->fib6_table)
4717 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07004718 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07004719 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08004720 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04004721 if (nla_put_u32(skb, RTA_TABLE, table))
4722 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07004723
4724 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004725 rtm->rtm_flags = 0;
4726 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07004727 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004728
Xin Long22d0bd82018-09-11 14:33:58 +08004729 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004730 rtm->rtm_flags |= RTM_F_CLONED;
4731
David Ahernd4ead6b2018-04-17 17:33:16 -07004732 if (dest) {
4733 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04004734 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004735 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004736 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08004737 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004738 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004739#ifdef CONFIG_IPV6_SUBTREES
4740 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02004741 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04004742 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004743 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04004744 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08004745 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004746 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004747#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004748 if (iif) {
4749#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08004750 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08004751 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02004752
David Ahernfd61c6b2017-01-17 15:51:07 -08004753 if (err == 0)
4754 return 0;
4755 if (err < 0)
4756 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004757 } else
4758#endif
David S. Millerc78679e2012-04-01 20:27:33 -04004759 if (nla_put_u32(skb, RTA_IIF, iif))
4760 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07004761 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004762 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07004763 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02004764 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004765 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004766 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07004767
David Ahern93c2fb22018-04-18 15:38:59 -07004768 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00004769 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07004770 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02004771 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004772 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00004773 }
4774
David Ahernd4ead6b2018-04-17 17:33:16 -07004775 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
4776 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07004777 goto nla_put_failure;
4778
David Ahern93c2fb22018-04-18 15:38:59 -07004779 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04004780 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00004781
David Ahernbeb1afac52017-02-02 12:37:09 -08004782 /* For multipath routes, walk the siblings list and add
4783 * each as a nexthop within RTA_MULTIPATH.
4784 */
Xin Long22d0bd82018-09-11 14:33:58 +08004785 if (rt6) {
4786 if (rt6_flags & RTF_GATEWAY &&
4787 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
4788 goto nla_put_failure;
4789
4790 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
4791 goto nla_put_failure;
4792 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07004793 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08004794 struct nlattr *mp;
4795
Michal Kubecekae0be8d2019-04-26 11:13:06 +02004796 mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
David Ahernbeb1afac52017-02-02 12:37:09 -08004797 if (!mp)
4798 goto nla_put_failure;
4799
David Ahernc0a72072019-04-02 14:11:58 -07004800 if (fib_add_nexthop(skb, &rt->fib6_nh.nh_common,
4801 rt->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004802 goto nla_put_failure;
4803
4804 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07004805 &rt->fib6_siblings, fib6_siblings) {
David Ahernc0a72072019-04-02 14:11:58 -07004806 if (fib_add_nexthop(skb, &sibling->fib6_nh.nh_common,
4807 sibling->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004808 goto nla_put_failure;
4809 }
4810
4811 nla_nest_end(skb, mp);
4812 } else {
David Ahernecc56632019-04-23 08:48:09 -07004813 unsigned char nh_flags = 0;
4814
David Ahernc0a72072019-04-02 14:11:58 -07004815 if (fib_nexthop_info(skb, &rt->fib6_nh.nh_common,
David Ahernecc56632019-04-23 08:48:09 -07004816 &nh_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004817 goto nla_put_failure;
David Ahernecc56632019-04-23 08:48:09 -07004818
4819 rtm->rtm_flags |= nh_flags;
David Ahernbeb1afac52017-02-02 12:37:09 -08004820 }
4821
Xin Long22d0bd82018-09-11 14:33:58 +08004822 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07004823 expires = dst ? dst->expires : rt->expires;
4824 expires -= jiffies;
4825 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07004826
David Ahernd4ead6b2018-04-17 17:33:16 -07004827 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08004828 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004829
Xin Long22d0bd82018-09-11 14:33:58 +08004830 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004831 goto nla_put_failure;
4832
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004833
Johannes Berg053c0952015-01-16 22:09:00 +01004834 nlmsg_end(skb, nlh);
4835 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004836
4837nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08004838 nlmsg_cancel(skb, nlh);
4839 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004840}
4841
David Ahern13e38902018-10-15 18:56:44 -07004842static bool fib6_info_uses_dev(const struct fib6_info *f6i,
4843 const struct net_device *dev)
4844{
David Ahernad1601a2019-03-27 20:53:56 -07004845 if (f6i->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004846 return true;
4847
4848 if (f6i->fib6_nsiblings) {
4849 struct fib6_info *sibling, *next_sibling;
4850
4851 list_for_each_entry_safe(sibling, next_sibling,
4852 &f6i->fib6_siblings, fib6_siblings) {
David Ahernad1601a2019-03-27 20:53:56 -07004853 if (sibling->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004854 return true;
4855 }
4856 }
4857
4858 return false;
4859}
4860
David Ahern8d1c8022018-04-17 17:33:26 -07004861int rt6_dump_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004862{
4863 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07004864 struct fib_dump_filter *filter = &arg->filter;
4865 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08004866 struct net *net = arg->net;
4867
David Ahern421842e2018-04-17 17:33:18 -07004868 if (rt == net->ipv6.fib6_null_entry)
David Ahern1f17e2f2017-01-26 13:54:08 -08004869 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004870
David Ahern13e38902018-10-15 18:56:44 -07004871 if ((filter->flags & RTM_F_PREFIX) &&
4872 !(rt->fib6_flags & RTF_PREFIX_RT)) {
4873 /* success since this is not a prefix route */
4874 return 1;
4875 }
4876 if (filter->filter_set) {
4877 if ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
4878 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
4879 (filter->protocol && rt->fib6_protocol != filter->protocol)) {
David Ahernf8cfe2c2017-01-17 15:51:08 -08004880 return 1;
4881 }
David Ahern13e38902018-10-15 18:56:44 -07004882 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08004883 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004884
David Ahernd4ead6b2018-04-17 17:33:16 -07004885 return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
4886 RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
David Ahern13e38902018-10-15 18:56:44 -07004887 arg->cb->nlh->nlmsg_seq, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004888}
4889
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004890static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
4891 const struct nlmsghdr *nlh,
4892 struct nlattr **tb,
4893 struct netlink_ext_ack *extack)
4894{
4895 struct rtmsg *rtm;
4896 int i, err;
4897
4898 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
4899 NL_SET_ERR_MSG_MOD(extack,
4900 "Invalid header for get route request");
4901 return -EINVAL;
4902 }
4903
4904 if (!netlink_strict_get_check(skb))
Johannes Berg8cb08172019-04-26 14:07:28 +02004905 return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
4906 rtm_ipv6_policy, extack);
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004907
4908 rtm = nlmsg_data(nlh);
4909 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
4910 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
4911 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
4912 rtm->rtm_type) {
4913 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
4914 return -EINVAL;
4915 }
4916 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
4917 NL_SET_ERR_MSG_MOD(extack,
4918 "Invalid flags for get route request");
4919 return -EINVAL;
4920 }
4921
Johannes Berg8cb08172019-04-26 14:07:28 +02004922 err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
4923 rtm_ipv6_policy, extack);
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004924 if (err)
4925 return err;
4926
4927 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
4928 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
4929 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
4930 return -EINVAL;
4931 }
4932
4933 for (i = 0; i <= RTA_MAX; i++) {
4934 if (!tb[i])
4935 continue;
4936
4937 switch (i) {
4938 case RTA_SRC:
4939 case RTA_DST:
4940 case RTA_IIF:
4941 case RTA_OIF:
4942 case RTA_MARK:
4943 case RTA_UID:
4944 case RTA_SPORT:
4945 case RTA_DPORT:
4946 case RTA_IP_PROTO:
4947 break;
4948 default:
4949 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
4950 return -EINVAL;
4951 }
4952 }
4953
4954 return 0;
4955}
4956
David Ahernc21ef3e2017-04-16 09:48:24 -07004957static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
4958 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004959{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09004960 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07004961 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004962 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07004963 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004964 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004965 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07004966 struct sk_buff *skb;
4967 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07004968 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004969 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07004970
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004971 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07004972 if (err < 0)
4973 goto errout;
4974
4975 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02004976 rtm = nlmsg_data(nlh);
4977 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004978 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07004979
4980 if (tb[RTA_SRC]) {
4981 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
4982 goto errout;
4983
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004984 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07004985 }
4986
4987 if (tb[RTA_DST]) {
4988 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
4989 goto errout;
4990
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004991 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07004992 }
4993
4994 if (tb[RTA_IIF])
4995 iif = nla_get_u32(tb[RTA_IIF]);
4996
4997 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004998 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07004999
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07005000 if (tb[RTA_MARK])
5001 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
5002
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09005003 if (tb[RTA_UID])
5004 fl6.flowi6_uid = make_kuid(current_user_ns(),
5005 nla_get_u32(tb[RTA_UID]));
5006 else
5007 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
5008
Roopa Prabhueacb9382018-05-22 14:03:28 -07005009 if (tb[RTA_SPORT])
5010 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
5011
5012 if (tb[RTA_DPORT])
5013 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
5014
5015 if (tb[RTA_IP_PROTO]) {
5016 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08005017 &fl6.flowi6_proto, AF_INET6,
5018 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07005019 if (err)
5020 goto errout;
5021 }
5022
Thomas Grafab364a62006-08-22 00:01:47 -07005023 if (iif) {
5024 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005025 int flags = 0;
5026
Florian Westphal121622d2017-08-15 16:34:42 +02005027 rcu_read_lock();
5028
5029 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07005030 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02005031 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07005032 err = -ENODEV;
5033 goto errout;
5034 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005035
5036 fl6.flowi6_iif = iif;
5037
5038 if (!ipv6_addr_any(&fl6.saddr))
5039 flags |= RT6_LOOKUP_F_HAS_SADDR;
5040
David Ahernb75cc8f2018-03-02 08:32:17 -08005041 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02005042
5043 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005044 } else {
5045 fl6.flowi6_oif = oif;
5046
Ido Schimmel58acfd72017-12-20 12:28:25 +02005047 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005048 }
5049
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005050
5051 rt = container_of(dst, struct rt6_info, dst);
5052 if (rt->dst.error) {
5053 err = rt->dst.error;
5054 ip6_rt_put(rt);
5055 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07005056 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005057
WANG Cong9d6acb32017-03-01 20:48:39 -08005058 if (rt == net->ipv6.ip6_null_entry) {
5059 err = rt->dst.error;
5060 ip6_rt_put(rt);
5061 goto errout;
5062 }
5063
Linus Torvalds1da177e2005-04-16 15:20:36 -07005064 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05005065 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00005066 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07005067 err = -ENOBUFS;
5068 goto errout;
5069 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005070
Changli Gaod8d1f302010-06-10 23:31:35 -07005071 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07005072
5073 rcu_read_lock();
5074 from = rcu_dereference(rt->from);
Martin KaFai Lau886b7a52019-04-30 10:45:12 -07005075 if (from) {
5076 if (fibmatch)
5077 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
5078 iif, RTM_NEWROUTE,
5079 NETLINK_CB(in_skb).portid,
5080 nlh->nlmsg_seq, 0);
5081 else
5082 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5083 &fl6.saddr, iif, RTM_NEWROUTE,
5084 NETLINK_CB(in_skb).portid,
5085 nlh->nlmsg_seq, 0);
5086 } else {
5087 err = -ENETUNREACH;
5088 }
David Aherna68886a2018-04-20 15:38:02 -07005089 rcu_read_unlock();
5090
Linus Torvalds1da177e2005-04-16 15:20:36 -07005091 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005092 kfree_skb(skb);
5093 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005094 }
5095
Eric W. Biederman15e47302012-09-07 20:12:54 +00005096 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005097errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005098 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005099}
5100
David Ahern8d1c8022018-04-17 17:33:26 -07005101void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07005102 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005103{
5104 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08005105 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005106 u32 seq;
5107 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005108
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005109 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05005110 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07005111
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005112 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05005113 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07005114 goto errout;
5115
David Ahernd4ead6b2018-04-17 17:33:16 -07005116 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
5117 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08005118 if (err < 0) {
5119 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
5120 WARN_ON(err == -EMSGSIZE);
5121 kfree_skb(skb);
5122 goto errout;
5123 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00005124 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08005125 info->nlh, gfp_any());
5126 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07005127errout:
5128 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08005129 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005130}
5131
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005132static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00005133 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005134{
Jiri Pirko351638e2013-05-28 01:30:21 +00005135 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09005136 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005137
WANG Cong242d3a42017-05-08 10:12:13 -07005138 if (!(dev->flags & IFF_LOOPBACK))
5139 return NOTIFY_OK;
5140
5141 if (event == NETDEV_REGISTER) {
David Ahernad1601a2019-03-27 20:53:56 -07005142 net->ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07005143 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005144 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
5145#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07005146 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005147 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07005148 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005149 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
5150#endif
WANG Cong76da0702017-06-20 11:42:27 -07005151 } else if (event == NETDEV_UNREGISTER &&
5152 dev->reg_state != NETREG_UNREGISTERED) {
5153 /* NETDEV_UNREGISTER could be fired for multiple times by
5154 * netdev_wait_allrefs(). Make sure we only call this once.
5155 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07005156 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005157#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07005158 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
5159 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005160#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005161 }
5162
5163 return NOTIFY_OK;
5164}
5165
Linus Torvalds1da177e2005-04-16 15:20:36 -07005166/*
5167 * /proc
5168 */
5169
5170#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07005171static int rt6_stats_seq_show(struct seq_file *seq, void *v)
5172{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005173 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005174 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005175 net->ipv6.rt6_stats->fib_nodes,
5176 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07005177 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005178 net->ipv6.rt6_stats->fib_rt_entries,
5179 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00005180 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005181 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182
5183 return 0;
5184}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005185#endif /* CONFIG_PROC_FS */
5186
5187#ifdef CONFIG_SYSCTL
5188
Linus Torvalds1da177e2005-04-16 15:20:36 -07005189static
Joe Perchesfe2c6332013-06-11 23:04:25 -07005190int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005191 void __user *buffer, size_t *lenp, loff_t *ppos)
5192{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005193 struct net *net;
5194 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005195 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005196 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005197 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005198
5199 net = (struct net *)ctl->extra1;
5200 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005201 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5202 if (ret)
5203 return ret;
5204
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02005205 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005206 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005207}
5208
David Ahern7c6bb7d2018-10-11 20:17:21 -07005209static int zero;
5210static int one = 1;
5211
David Aherned792e22018-10-08 14:06:34 -07005212static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005213 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005214 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08005215 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005216 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07005217 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005218 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07005219 },
5220 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005221 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005222 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005223 .maxlen = sizeof(int),
5224 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005225 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 },
5227 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005228 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08005229 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005230 .maxlen = sizeof(int),
5231 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005232 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005233 },
5234 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005235 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005236 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005237 .maxlen = sizeof(int),
5238 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005239 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005240 },
5241 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005242 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08005243 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005244 .maxlen = sizeof(int),
5245 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005246 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005247 },
5248 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005249 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005250 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005251 .maxlen = sizeof(int),
5252 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005253 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005254 },
5255 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005256 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08005257 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005258 .maxlen = sizeof(int),
5259 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005260 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005261 },
5262 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005263 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08005264 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005265 .maxlen = sizeof(int),
5266 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005267 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005268 },
5269 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005270 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08005271 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005272 .maxlen = sizeof(int),
5273 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005274 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005275 },
5276 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005277 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08005278 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005279 .maxlen = sizeof(int),
5280 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005281 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005282 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07005283 {
5284 .procname = "skip_notify_on_dev_down",
5285 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
5286 .maxlen = sizeof(int),
5287 .mode = 0644,
5288 .proc_handler = proc_dointvec,
5289 .extra1 = &zero,
5290 .extra2 = &one,
5291 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08005292 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005293};
5294
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005295struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005296{
5297 struct ctl_table *table;
5298
5299 table = kmemdup(ipv6_route_table_template,
5300 sizeof(ipv6_route_table_template),
5301 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005302
5303 if (table) {
5304 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005305 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005306 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005307 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
5308 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
5309 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
5310 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
5311 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
5312 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
5313 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08005314 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005315 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00005316
5317 /* Don't export sysctls to unprivileged users */
5318 if (net->user_ns != &init_user_ns)
5319 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005320 }
5321
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005322 return table;
5323}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005324#endif
5325
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005326static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005327{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07005328 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005329
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005330 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
5331 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005332
Eric Dumazetfc66f952010-10-08 06:37:34 +00005333 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
5334 goto out_ip6_dst_ops;
5335
David Ahern421842e2018-04-17 17:33:18 -07005336 net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
5337 sizeof(*net->ipv6.fib6_null_entry),
5338 GFP_KERNEL);
5339 if (!net->ipv6.fib6_null_entry)
5340 goto out_ip6_dst_entries;
5341
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005342 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
5343 sizeof(*net->ipv6.ip6_null_entry),
5344 GFP_KERNEL);
5345 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07005346 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005347 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005348 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
5349 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005350
5351#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02005352 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005353 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
5354 sizeof(*net->ipv6.ip6_prohibit_entry),
5355 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005356 if (!net->ipv6.ip6_prohibit_entry)
5357 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005358 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005359 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
5360 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005361
5362 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
5363 sizeof(*net->ipv6.ip6_blk_hole_entry),
5364 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005365 if (!net->ipv6.ip6_blk_hole_entry)
5366 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005367 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005368 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
5369 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005370#endif
5371
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005372 net->ipv6.sysctl.flush_delay = 0;
5373 net->ipv6.sysctl.ip6_rt_max_size = 4096;
5374 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
5375 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
5376 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
5377 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
5378 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
5379 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005380 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005381
Benjamin Thery6891a342008-03-04 13:49:47 -08005382 net->ipv6.ip6_rt_gc_expire = 30*HZ;
5383
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005384 ret = 0;
5385out:
5386 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005387
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005388#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5389out_ip6_prohibit_entry:
5390 kfree(net->ipv6.ip6_prohibit_entry);
5391out_ip6_null_entry:
5392 kfree(net->ipv6.ip6_null_entry);
5393#endif
David Ahern421842e2018-04-17 17:33:18 -07005394out_fib6_null_entry:
5395 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005396out_ip6_dst_entries:
5397 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005398out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005399 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005400}
5401
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005402static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005403{
David Ahern421842e2018-04-17 17:33:18 -07005404 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005405 kfree(net->ipv6.ip6_null_entry);
5406#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5407 kfree(net->ipv6.ip6_prohibit_entry);
5408 kfree(net->ipv6.ip6_blk_hole_entry);
5409#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005410 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005411}
5412
Thomas Grafd1896342012-06-18 12:08:33 +00005413static int __net_init ip6_route_net_init_late(struct net *net)
5414{
5415#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02005416 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
5417 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02005418 proc_create_net_single("rt6_stats", 0444, net->proc_net,
5419 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00005420#endif
5421 return 0;
5422}
5423
5424static void __net_exit ip6_route_net_exit_late(struct net *net)
5425{
5426#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00005427 remove_proc_entry("ipv6_route", net->proc_net);
5428 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00005429#endif
5430}
5431
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005432static struct pernet_operations ip6_route_net_ops = {
5433 .init = ip6_route_net_init,
5434 .exit = ip6_route_net_exit,
5435};
5436
David S. Millerc3426b42012-06-09 16:27:05 -07005437static int __net_init ipv6_inetpeer_init(struct net *net)
5438{
5439 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
5440
5441 if (!bp)
5442 return -ENOMEM;
5443 inet_peer_base_init(bp);
5444 net->ipv6.peers = bp;
5445 return 0;
5446}
5447
5448static void __net_exit ipv6_inetpeer_exit(struct net *net)
5449{
5450 struct inet_peer_base *bp = net->ipv6.peers;
5451
5452 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07005453 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07005454 kfree(bp);
5455}
5456
David S. Miller2b823f72012-06-09 19:00:16 -07005457static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07005458 .init = ipv6_inetpeer_init,
5459 .exit = ipv6_inetpeer_exit,
5460};
5461
Thomas Grafd1896342012-06-18 12:08:33 +00005462static struct pernet_operations ip6_route_net_late_ops = {
5463 .init = ip6_route_net_init_late,
5464 .exit = ip6_route_net_exit_late,
5465};
5466
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005467static struct notifier_block ip6_route_dev_notifier = {
5468 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07005469 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005470};
5471
WANG Cong2f460932017-05-03 22:07:31 -07005472void __init ip6_route_init_special_entries(void)
5473{
5474 /* Registering of the loopback is done before this portion of code,
5475 * the loopback reference in rt6_info will not be taken, do it
5476 * manually for init_net */
David Ahernad1601a2019-03-27 20:53:56 -07005477 init_net.ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07005478 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
5479 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5480 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
5481 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
5482 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5483 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
5484 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5485 #endif
5486}
5487
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005488int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005489{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005490 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005491 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005492
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005493 ret = -ENOMEM;
5494 ip6_dst_ops_template.kmem_cachep =
5495 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
5496 SLAB_HWCACHE_ALIGN, NULL);
5497 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08005498 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07005499
Eric Dumazetfc66f952010-10-08 06:37:34 +00005500 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005501 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005502 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005503
David S. Millerc3426b42012-06-09 16:27:05 -07005504 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
5505 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005506 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00005507
David S. Miller7e52b332012-06-15 15:51:55 -07005508 ret = register_pernet_subsys(&ip6_route_net_ops);
5509 if (ret)
5510 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07005511
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07005512 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
5513
David S. Millere8803b62012-06-16 01:12:19 -07005514 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005515 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005516 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005517
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005518 ret = xfrm6_init();
5519 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005520 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08005521
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005522 ret = fib6_rules_init();
5523 if (ret)
5524 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08005525
Thomas Grafd1896342012-06-18 12:08:33 +00005526 ret = register_pernet_subsys(&ip6_route_net_late_ops);
5527 if (ret)
5528 goto fib6_rules_init;
5529
Florian Westphal16feebc2017-12-02 21:44:08 +01005530 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
5531 inet6_rtm_newroute, NULL, 0);
5532 if (ret < 0)
5533 goto out_register_late_subsys;
5534
5535 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
5536 inet6_rtm_delroute, NULL, 0);
5537 if (ret < 0)
5538 goto out_register_late_subsys;
5539
5540 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
5541 inet6_rtm_getroute, NULL,
5542 RTNL_FLAG_DOIT_UNLOCKED);
5543 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00005544 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005545
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005546 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005547 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00005548 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005549
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005550 for_each_possible_cpu(cpu) {
5551 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
5552
5553 INIT_LIST_HEAD(&ul->head);
5554 spin_lock_init(&ul->lock);
5555 }
5556
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005557out:
5558 return ret;
5559
Thomas Grafd1896342012-06-18 12:08:33 +00005560out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01005561 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00005562 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005563fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005564 fib6_rules_cleanup();
5565xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005566 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00005567out_fib6_init:
5568 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005569out_register_subsys:
5570 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07005571out_register_inetpeer:
5572 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005573out_dst_entries:
5574 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005575out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005576 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005577 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005578}
5579
5580void ip6_route_cleanup(void)
5581{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005582 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00005583 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07005584 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005585 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005586 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07005587 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005588 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005589 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005590 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005591}