blob: 5a1e1176c33c817ca24bf737e2eedeb1a7af68c9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Wei Wang35732d02017-10-06 12:05:57 -070047#include <linux/jhash.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020048#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <net/snmp.h>
50#include <net/ipv6.h>
51#include <net/ip6_fib.h>
52#include <net/ip6_route.h>
53#include <net/ndisc.h>
54#include <net/addrconf.h>
55#include <net/tcp.h>
56#include <linux/rtnetlink.h>
57#include <net/dst.h>
Jiri Benc904af042015-08-20 13:56:31 +020058#include <net/dst_metadata.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070060#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070061#include <net/netlink.h>
Nicolas Dichtel51ebd312012-10-22 03:42:09 +000062#include <net/nexthop.h>
Roopa Prabhu19e42e42015-07-21 10:43:48 +020063#include <net/lwtunnel.h>
Jiri Benc904af042015-08-20 13:56:31 +020064#include <net/ip_tunnels.h>
David Ahernca254492015-10-12 11:47:10 -070065#include <net/l3mdev.h>
Roopa Prabhueacb9382018-05-22 14:03:28 -070066#include <net/ip.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080067#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69#ifdef CONFIG_SYSCTL
70#include <linux/sysctl.h>
71#endif
72
David Ahern30d444d2018-05-23 17:08:48 -070073static int ip6_rt_type_to_error(u8 fib6_type);
74
75#define CREATE_TRACE_POINTS
76#include <trace/events/fib6.h>
77EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
78#undef CREATE_TRACE_POINTS
79
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020080enum rt6_nud_state {
Jiri Benc7e980562013-12-11 13:48:20 +010081 RT6_NUD_FAIL_HARD = -3,
82 RT6_NUD_FAIL_PROBE = -2,
83 RT6_NUD_FAIL_DO_RR = -1,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020084 RT6_NUD_SUCCEED = 1
85};
86
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080088static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000089static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090static struct dst_entry *ip6_negative_advice(struct dst_entry *);
91static void ip6_dst_destroy(struct dst_entry *);
92static void ip6_dst_ifdown(struct dst_entry *,
93 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080094static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
96static int ip6_pkt_discard(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050097static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Kamala R7150aed2013-12-02 19:55:21 +053098static int ip6_pkt_prohibit(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050099static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100static void ip6_link_failure(struct sk_buff *skb);
David S. Miller6700c272012-07-17 03:29:28 -0700101static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
102 struct sk_buff *skb, u32 mtu);
103static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
104 struct sk_buff *skb);
David Ahern702cea52019-04-09 14:41:13 -0700105static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
106 int strict);
David Ahern8d1c8022018-04-17 17:33:26 -0700107static size_t rt6_nlmsg_size(struct fib6_info *rt);
David Ahernd4ead6b2018-04-17 17:33:16 -0700108static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -0700109 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -0700110 struct in6_addr *dest, struct in6_addr *src,
David Ahern16a16cd2017-02-02 12:37:11 -0800111 int iif, int type, u32 portid, u32 seq,
112 unsigned int flags);
David Ahern7e4b5122019-04-16 14:36:00 -0700113static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang35732d02017-10-06 12:05:57 -0700114 struct in6_addr *daddr,
115 struct in6_addr *saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800117#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -0700118static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000119 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700120 const struct in6_addr *gwaddr,
121 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +0000122 unsigned int pref);
David Ahern8d1c8022018-04-17 17:33:26 -0700123static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000124 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700125 const struct in6_addr *gwaddr,
126 struct net_device *dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800127#endif
128
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700129struct uncached_list {
130 spinlock_t lock;
131 struct list_head head;
132};
133
134static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
135
Xin Long510c3212018-02-14 19:06:02 +0800136void rt6_uncached_list_add(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700137{
138 struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
139
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700140 rt->rt6i_uncached_list = ul;
141
142 spin_lock_bh(&ul->lock);
143 list_add_tail(&rt->rt6i_uncached, &ul->head);
144 spin_unlock_bh(&ul->lock);
145}
146
Xin Long510c3212018-02-14 19:06:02 +0800147void rt6_uncached_list_del(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700148{
149 if (!list_empty(&rt->rt6i_uncached)) {
150 struct uncached_list *ul = rt->rt6i_uncached_list;
Wei Wang81eb8442017-10-06 12:06:11 -0700151 struct net *net = dev_net(rt->dst.dev);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700152
153 spin_lock_bh(&ul->lock);
154 list_del(&rt->rt6i_uncached);
Wei Wang81eb8442017-10-06 12:06:11 -0700155 atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700156 spin_unlock_bh(&ul->lock);
157 }
158}
159
160static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
161{
162 struct net_device *loopback_dev = net->loopback_dev;
163 int cpu;
164
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500165 if (dev == loopback_dev)
166 return;
167
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700168 for_each_possible_cpu(cpu) {
169 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
170 struct rt6_info *rt;
171
172 spin_lock_bh(&ul->lock);
173 list_for_each_entry(rt, &ul->head, rt6i_uncached) {
174 struct inet6_dev *rt_idev = rt->rt6i_idev;
175 struct net_device *rt_dev = rt->dst.dev;
176
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500177 if (rt_idev->dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700178 rt->rt6i_idev = in6_dev_get(loopback_dev);
179 in6_dev_put(rt_idev);
180 }
181
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500182 if (rt_dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700183 rt->dst.dev = loopback_dev;
184 dev_hold(rt->dst.dev);
185 dev_put(rt_dev);
186 }
187 }
188 spin_unlock_bh(&ul->lock);
189 }
190}
191
David Ahernf8a1b432018-04-17 17:33:21 -0700192static inline const void *choose_neigh_daddr(const struct in6_addr *p,
David S. Millerf894cbf2012-07-02 21:52:24 -0700193 struct sk_buff *skb,
194 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500195{
David S. Millera7563f32012-01-26 16:29:16 -0500196 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500197 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700198 else if (skb)
199 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500200 return daddr;
201}
202
David Ahernf8a1b432018-04-17 17:33:21 -0700203struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
204 struct net_device *dev,
205 struct sk_buff *skb,
206 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700207{
David S. Miller39232972012-01-26 15:22:32 -0500208 struct neighbour *n;
209
David Ahernf8a1b432018-04-17 17:33:21 -0700210 daddr = choose_neigh_daddr(gw, skb, daddr);
211 n = __ipv6_neigh_lookup(dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500212 if (n)
213 return n;
Stefano Brivio7adf3242019-01-02 13:29:27 +0100214
215 n = neigh_create(&nd_tbl, daddr, dev);
216 return IS_ERR(n) ? NULL : n;
David Ahernf8a1b432018-04-17 17:33:21 -0700217}
218
219static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
220 struct sk_buff *skb,
221 const void *daddr)
222{
223 const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
224
225 return ip6_neigh_lookup(&rt->rt6i_gateway, dst->dev, skb, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500226}
227
Julian Anastasov63fca652017-02-06 23:14:15 +0200228static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
229{
230 struct net_device *dev = dst->dev;
231 struct rt6_info *rt = (struct rt6_info *)dst;
232
David Ahernf8a1b432018-04-17 17:33:21 -0700233 daddr = choose_neigh_daddr(&rt->rt6i_gateway, NULL, daddr);
Julian Anastasov63fca652017-02-06 23:14:15 +0200234 if (!daddr)
235 return;
236 if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
237 return;
238 if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
239 return;
240 __ipv6_confirm_neigh(dev, daddr);
241}
242
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800243static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 .gc = ip6_dst_gc,
246 .gc_thresh = 1024,
247 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800248 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000249 .mtu = ip6_mtu,
David Ahernd4ead6b2018-04-17 17:33:16 -0700250 .cow_metrics = dst_cow_metrics_generic,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 .destroy = ip6_dst_destroy,
252 .ifdown = ip6_dst_ifdown,
253 .negative_advice = ip6_negative_advice,
254 .link_failure = ip6_link_failure,
255 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700256 .redirect = rt6_do_redirect,
Eric W. Biederman9f8955c2015-10-07 16:48:39 -0500257 .local_out = __ip6_local_out,
David Ahernf8a1b432018-04-17 17:33:21 -0700258 .neigh_lookup = ip6_dst_neigh_lookup,
Julian Anastasov63fca652017-02-06 23:14:15 +0200259 .confirm_neigh = ip6_confirm_neigh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260};
261
Steffen Klassertebb762f2011-11-23 02:12:51 +0000262static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800263{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000264 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
265
266 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800267}
268
David S. Miller6700c272012-07-17 03:29:28 -0700269static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
270 struct sk_buff *skb, u32 mtu)
David S. Miller14e50e52007-05-24 18:17:54 -0700271{
272}
273
David S. Miller6700c272012-07-17 03:29:28 -0700274static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
275 struct sk_buff *skb)
David S. Millerb587ee32012-07-12 00:39:24 -0700276{
277}
278
David S. Miller14e50e52007-05-24 18:17:54 -0700279static struct dst_ops ip6_dst_blackhole_ops = {
280 .family = AF_INET6,
David S. Miller14e50e52007-05-24 18:17:54 -0700281 .destroy = ip6_dst_destroy,
282 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000283 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800284 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700285 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700286 .redirect = ip6_rt_blackhole_redirect,
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -0700287 .cow_metrics = dst_cow_metrics_generic,
David Ahernf8a1b432018-04-17 17:33:21 -0700288 .neigh_lookup = ip6_dst_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700289};
290
David S. Miller62fa8a82011-01-26 20:51:05 -0800291static const u32 ip6_template_metrics[RTAX_MAX] = {
Li RongQing14edd872012-10-24 14:01:18 +0800292 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800293};
294
David Ahern8d1c8022018-04-17 17:33:26 -0700295static const struct fib6_info fib6_null_entry_template = {
David Ahern93c2fb22018-04-18 15:38:59 -0700296 .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP),
297 .fib6_protocol = RTPROT_KERNEL,
298 .fib6_metric = ~(u32)0,
299 .fib6_ref = ATOMIC_INIT(1),
David Ahern421842e2018-04-17 17:33:18 -0700300 .fib6_type = RTN_UNREACHABLE,
301 .fib6_metrics = (struct dst_metrics *)&dst_default_metrics,
302};
303
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000304static const struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700305 .dst = {
306 .__refcnt = ATOMIC_INIT(1),
307 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000308 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700309 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700310 .input = ip6_pkt_discard,
311 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 },
313 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314};
315
Thomas Graf101367c2006-08-04 03:39:02 -0700316#ifdef CONFIG_IPV6_MULTIPLE_TABLES
317
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000318static const struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700319 .dst = {
320 .__refcnt = ATOMIC_INIT(1),
321 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000322 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700323 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700324 .input = ip6_pkt_prohibit,
325 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700326 },
327 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700328};
329
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000330static const struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700331 .dst = {
332 .__refcnt = ATOMIC_INIT(1),
333 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000334 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700335 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700336 .input = dst_discard,
Eric W. Biedermanede20592015-10-07 16:48:47 -0500337 .output = dst_discard_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700338 },
339 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700340};
341
342#endif
343
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700344static void rt6_info_init(struct rt6_info *rt)
345{
346 struct dst_entry *dst = &rt->dst;
347
348 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700349 INIT_LIST_HEAD(&rt->rt6i_uncached);
350}
351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352/* allocate dst with ip6_dst_ops */
David Ahern93531c62018-04-17 17:33:25 -0700353struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
354 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
David S. Miller97bab732012-06-09 22:36:36 -0700356 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
Wei Wangb2a9c0e2017-06-17 10:42:41 -0700357 1, DST_OBSOLETE_FORCE_CHK, flags);
David S. Millercf911662011-04-28 14:31:47 -0700358
Wei Wang81eb8442017-10-06 12:06:11 -0700359 if (rt) {
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700360 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -0700361 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
362 }
Steffen Klassert81048912012-07-05 23:37:09 +0000363
David S. Millercf911662011-04-28 14:31:47 -0700364 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365}
David Ahern9ab179d2016-04-07 11:10:06 -0700366EXPORT_SYMBOL(ip6_dst_alloc);
Martin KaFai Laud52d3992015-05-22 20:56:06 -0700367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368static void ip6_dst_destroy(struct dst_entry *dst)
369{
370 struct rt6_info *rt = (struct rt6_info *)dst;
David Aherna68886a2018-04-20 15:38:02 -0700371 struct fib6_info *from;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700372 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
David Ahern1620a332018-10-04 20:07:54 -0700374 ip_dst_metrics_put(dst);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700375 rt6_uncached_list_del(rt);
376
377 idev = rt->rt6i_idev;
David S. Miller38308472011-12-03 18:02:47 -0500378 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 rt->rt6i_idev = NULL;
380 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900381 }
Gao feng1716a962012-04-06 00:13:10 +0000382
David Aherna68886a2018-04-20 15:38:02 -0700383 rcu_read_lock();
384 from = rcu_dereference(rt->from);
385 rcu_assign_pointer(rt->from, NULL);
David Ahern93531c62018-04-17 17:33:25 -0700386 fib6_info_release(from);
David Aherna68886a2018-04-20 15:38:02 -0700387 rcu_read_unlock();
David S. Millerb3419362010-11-30 12:27:11 -0800388}
389
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
391 int how)
392{
393 struct rt6_info *rt = (struct rt6_info *)dst;
394 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800395 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900396 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Wei Wange5645f52017-08-14 10:44:59 -0700398 if (idev && idev->dev != loopback_dev) {
399 struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev);
400 if (loopback_idev) {
401 rt->rt6i_idev = loopback_idev;
402 in6_dev_put(idev);
David S. Miller97cac082012-07-02 22:43:47 -0700403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
405}
406
Martin KaFai Lau5973fb12015-11-11 11:51:07 -0800407static bool __rt6_check_expired(const struct rt6_info *rt)
408{
409 if (rt->rt6i_flags & RTF_EXPIRES)
410 return time_after(jiffies, rt->dst.expires);
411 else
412 return false;
413}
414
Eric Dumazeta50feda2012-05-18 18:57:34 +0000415static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416{
David Aherna68886a2018-04-20 15:38:02 -0700417 struct fib6_info *from;
418
419 from = rcu_dereference(rt->from);
420
Gao feng1716a962012-04-06 00:13:10 +0000421 if (rt->rt6i_flags & RTF_EXPIRES) {
422 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000423 return true;
David Aherna68886a2018-04-20 15:38:02 -0700424 } else if (from) {
Xin Long1e2ea8a2017-08-26 20:10:10 +0800425 return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
David Aherna68886a2018-04-20 15:38:02 -0700426 fib6_check_expired(from);
Gao feng1716a962012-04-06 00:13:10 +0000427 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000428 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430
David Ahernb1d40992019-04-16 14:35:59 -0700431void fib6_select_path(const struct net *net, struct fib6_result *res,
432 struct flowi6 *fl6, int oif, bool have_oif_match,
433 const struct sk_buff *skb, int strict)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000434{
David Ahern8d1c8022018-04-17 17:33:26 -0700435 struct fib6_info *sibling, *next_sibling;
David Ahernb1d40992019-04-16 14:35:59 -0700436 struct fib6_info *match = res->f6i;
437
438 if (!match->fib6_nsiblings || have_oif_match)
439 goto out;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000440
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200441 /* We might have already computed the hash for ICMPv6 errors. In such
442 * case it will always be non-zero. Otherwise now is the time to do it.
443 */
444 if (!fl6->mp_hash)
David Ahernb4bac172018-03-02 08:32:18 -0800445 fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200446
David Ahernad1601a2019-03-27 20:53:56 -0700447 if (fl6->mp_hash <= atomic_read(&match->fib6_nh.fib_nh_upper_bound))
David Ahernb1d40992019-04-16 14:35:59 -0700448 goto out;
Ido Schimmelbbfcd772017-11-21 09:50:12 +0200449
David Ahern93c2fb22018-04-18 15:38:59 -0700450 list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
451 fib6_siblings) {
David Ahern702cea52019-04-09 14:41:13 -0700452 const struct fib6_nh *nh = &sibling->fib6_nh;
David Ahern5e670d82018-04-17 17:33:14 -0700453 int nh_upper_bound;
454
David Ahern702cea52019-04-09 14:41:13 -0700455 nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
David Ahern5e670d82018-04-17 17:33:14 -0700456 if (fl6->mp_hash > nh_upper_bound)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200457 continue;
David Ahern702cea52019-04-09 14:41:13 -0700458 if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200459 break;
460 match = sibling;
461 break;
462 }
463
David Ahernb1d40992019-04-16 14:35:59 -0700464out:
465 res->f6i = match;
466 res->nh = &match->fib6_nh;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000467}
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469/*
Wei Wang66f5d6c2017-10-06 12:06:10 -0700470 * Route lookup. rcu_read_lock() should be held.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 */
472
David Ahern0c59d002019-04-09 14:41:18 -0700473static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
474 const struct in6_addr *saddr, int oif, int flags)
475{
476 const struct net_device *dev;
477
478 if (nh->fib_nh_flags & RTNH_F_DEAD)
479 return false;
480
481 dev = nh->fib_nh_dev;
482 if (oif) {
483 if (dev->ifindex == oif)
484 return true;
485 } else {
486 if (ipv6_chk_addr(net, saddr, dev,
487 flags & RT6_LOOKUP_F_IFACE))
488 return true;
489 }
490
491 return false;
492}
493
David Ahern75ef7382019-04-16 14:36:07 -0700494static void rt6_device_match(struct net *net, struct fib6_result *res,
495 const struct in6_addr *saddr, int oif, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
David Ahern75ef7382019-04-16 14:36:07 -0700497 struct fib6_info *f6i = res->f6i;
498 struct fib6_info *spf6i;
499 struct fib6_nh *nh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
David Ahern75ef7382019-04-16 14:36:07 -0700501 if (!oif && ipv6_addr_any(saddr)) {
502 nh = &f6i->fib6_nh;
503 if (!(nh->fib_nh_flags & RTNH_F_DEAD)) {
504 res->nh = nh;
505 return;
506 }
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900507 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
David Ahern75ef7382019-04-16 14:36:07 -0700509 for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
510 nh = &spf6i->fib6_nh;
511 if (__rt6_device_match(net, nh, saddr, oif, flags)) {
512 res->f6i = spf6i;
513 res->nh = nh;
514 }
515 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
David Ahern75ef7382019-04-16 14:36:07 -0700517 if (oif && flags & RT6_LOOKUP_F_IFACE) {
518 res->f6i = net->ipv6.fib6_null_entry;
519 res->nh = &res->f6i->fib6_nh;
520 return;
521 }
522
523 res->nh = &f6i->fib6_nh;
524 if (res->nh->fib_nh_flags & RTNH_F_DEAD) {
525 res->f6i = net->ipv6.fib6_null_entry;
526 res->nh = &res->f6i->fib6_nh;
527 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528}
529
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800530#ifdef CONFIG_IPV6_ROUTER_PREF
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200531struct __rt6_probe_work {
532 struct work_struct work;
533 struct in6_addr target;
534 struct net_device *dev;
535};
536
537static void rt6_probe_deferred(struct work_struct *w)
538{
539 struct in6_addr mcaddr;
540 struct __rt6_probe_work *work =
541 container_of(w, struct __rt6_probe_work, work);
542
543 addrconf_addr_solict_mult(&work->target, &mcaddr);
Erik Nordmarkadc176c2016-12-02 14:00:08 -0800544 ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200545 dev_put(work->dev);
Michael Büsch662f5532015-02-08 10:14:07 +0100546 kfree(work);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200547}
548
David Aherncc3a86c2019-04-09 14:41:12 -0700549static void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800550{
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200551 struct __rt6_probe_work *work = NULL;
David Ahern5e670d82018-04-17 17:33:14 -0700552 const struct in6_addr *nh_gw;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000553 struct neighbour *neigh;
David Ahern5e670d82018-04-17 17:33:14 -0700554 struct net_device *dev;
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200555 struct inet6_dev *idev;
David Ahern5e670d82018-04-17 17:33:14 -0700556
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800557 /*
558 * Okay, this does not seem to be appropriate
559 * for now, however, we need to check if it
560 * is really so; aka Router Reachability Probing.
561 *
562 * Router Reachability Probe MUST be rate-limited
563 * to no more than one per minute.
564 */
David Aherncc3a86c2019-04-09 14:41:12 -0700565 if (fib6_nh->fib_nh_gw_family)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000566 return;
David Ahern5e670d82018-04-17 17:33:14 -0700567
David Aherncc3a86c2019-04-09 14:41:12 -0700568 nh_gw = &fib6_nh->fib_nh_gw6;
569 dev = fib6_nh->fib_nh_dev;
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000570 rcu_read_lock_bh();
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200571 idev = __in6_dev_get(dev);
David Ahern5e670d82018-04-17 17:33:14 -0700572 neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000573 if (neigh) {
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700574 if (neigh->nud_state & NUD_VALID)
575 goto out;
576
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000577 write_lock(&neigh->lock);
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700578 if (!(neigh->nud_state & NUD_VALID) &&
579 time_after(jiffies,
David Aherndcd1f572018-04-18 15:39:05 -0700580 neigh->updated + idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700581 work = kmalloc(sizeof(*work), GFP_ATOMIC);
582 if (work)
583 __neigh_set_probe_once(neigh);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200584 }
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000585 write_unlock(&neigh->lock);
David Aherncc3a86c2019-04-09 14:41:12 -0700586 } else if (time_after(jiffies, fib6_nh->last_probe +
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200587 idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700588 work = kmalloc(sizeof(*work), GFP_ATOMIC);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000589 }
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700590
591 if (work) {
David Aherncc3a86c2019-04-09 14:41:12 -0700592 fib6_nh->last_probe = jiffies;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700593 INIT_WORK(&work->work, rt6_probe_deferred);
David Ahern5e670d82018-04-17 17:33:14 -0700594 work->target = *nh_gw;
595 dev_hold(dev);
596 work->dev = dev;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700597 schedule_work(&work->work);
598 }
599
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700600out:
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000601 rcu_read_unlock_bh();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800602}
603#else
David Aherncc3a86c2019-04-09 14:41:12 -0700604static inline void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800605{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800606}
607#endif
608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800610 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 */
David Ahern1ba9a892019-04-09 14:41:10 -0700612static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200614 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
David Ahern5e670d82018-04-17 17:33:14 -0700615 struct neighbour *neigh;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000616
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000617 rcu_read_lock_bh();
David Ahern1ba9a892019-04-09 14:41:10 -0700618 neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
619 &fib6_nh->fib_nh_gw6);
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000620 if (neigh) {
621 read_lock(&neigh->lock);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800622 if (neigh->nud_state & NUD_VALID)
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200623 ret = RT6_NUD_SUCCEED;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800624#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000625 else if (!(neigh->nud_state & NUD_FAILED))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200626 ret = RT6_NUD_SUCCEED;
Jiri Benc7e980562013-12-11 13:48:20 +0100627 else
628 ret = RT6_NUD_FAIL_PROBE;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800629#endif
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000630 read_unlock(&neigh->lock);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200631 } else {
632 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
Jiri Benc7e980562013-12-11 13:48:20 +0100633 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
Paul Marksa5a81f02012-12-03 10:26:54 +0000634 }
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000635 rcu_read_unlock_bh();
636
Paul Marksa5a81f02012-12-03 10:26:54 +0000637 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800638}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
David Ahern702cea52019-04-09 14:41:13 -0700640static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
641 int strict)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800642{
David Ahern6e1809a2019-04-09 14:41:11 -0700643 int m = 0;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900644
David Ahern6e1809a2019-04-09 14:41:11 -0700645 if (!oif || nh->fib_nh_dev->ifindex == oif)
646 m = 2;
647
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700648 if (!m && (strict & RT6_LOOKUP_F_IFACE))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200649 return RT6_NUD_FAIL_HARD;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800650#ifdef CONFIG_IPV6_ROUTER_PREF
David Ahern702cea52019-04-09 14:41:13 -0700651 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800652#endif
David Ahern1ba9a892019-04-09 14:41:10 -0700653 if ((strict & RT6_LOOKUP_F_REACHABLE) &&
David Ahern702cea52019-04-09 14:41:13 -0700654 !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
David Ahern1ba9a892019-04-09 14:41:10 -0700655 int n = rt6_check_neigh(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200656 if (n < 0)
657 return n;
658 }
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800659 return m;
660}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
David Ahern28679ed2019-04-09 14:41:14 -0700662static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
663 int oif, int strict, int *mpri, bool *do_rr)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800664{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200665 bool match_do_rr = false;
David Ahern28679ed2019-04-09 14:41:14 -0700666 bool rc = false;
667 int m;
Andy Gospodarek35103d12015-08-13 10:39:01 -0400668
David Ahern28679ed2019-04-09 14:41:14 -0700669 if (nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel8067bb82018-01-07 12:45:09 +0200670 goto out;
671
David Ahern28679ed2019-04-09 14:41:14 -0700672 if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
673 nh->fib_nh_flags & RTNH_F_LINKDOWN &&
David Ahernd5d32e42016-10-24 12:27:23 -0700674 !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
Andy Gospodarek35103d12015-08-13 10:39:01 -0400675 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700676
David Ahern28679ed2019-04-09 14:41:14 -0700677 m = rt6_score_route(nh, fib6_flags, oif, strict);
Jiri Benc7e980562013-12-11 13:48:20 +0100678 if (m == RT6_NUD_FAIL_DO_RR) {
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200679 match_do_rr = true;
680 m = 0; /* lowest valid score */
Jiri Benc7e980562013-12-11 13:48:20 +0100681 } else if (m == RT6_NUD_FAIL_HARD) {
David S. Millerf11e6652007-03-24 20:36:25 -0700682 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700683 }
684
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200685 if (strict & RT6_LOOKUP_F_REACHABLE)
David Ahern28679ed2019-04-09 14:41:14 -0700686 rt6_probe(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200687
Jiri Benc7e980562013-12-11 13:48:20 +0100688 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200689 if (m > *mpri) {
690 *do_rr = match_do_rr;
691 *mpri = m;
David Ahern28679ed2019-04-09 14:41:14 -0700692 rc = true;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200693 }
David S. Millerf11e6652007-03-24 20:36:25 -0700694out:
David Ahern28679ed2019-04-09 14:41:14 -0700695 return rc;
David S. Millerf11e6652007-03-24 20:36:25 -0700696}
697
David Ahernb7bc4b62019-04-16 14:36:08 -0700698static void __find_rr_leaf(struct fib6_info *f6i_start,
David Ahern30c15f02019-04-09 14:41:15 -0700699 struct fib6_info *nomatch, u32 metric,
David Ahernb7bc4b62019-04-16 14:36:08 -0700700 struct fib6_result *res, struct fib6_info **cont,
David Ahern30c15f02019-04-09 14:41:15 -0700701 int oif, int strict, bool *do_rr, int *mpri)
David S. Millerf11e6652007-03-24 20:36:25 -0700702{
David Ahernb7bc4b62019-04-16 14:36:08 -0700703 struct fib6_info *f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700704
David Ahernb7bc4b62019-04-16 14:36:08 -0700705 for (f6i = f6i_start;
706 f6i && f6i != nomatch;
707 f6i = rcu_dereference(f6i->fib6_next)) {
David Ahern30c15f02019-04-09 14:41:15 -0700708 struct fib6_nh *nh;
709
David Ahernb7bc4b62019-04-16 14:36:08 -0700710 if (cont && f6i->fib6_metric != metric) {
711 *cont = f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700712 return;
713 }
714
David Ahernb7bc4b62019-04-16 14:36:08 -0700715 if (fib6_check_expired(f6i))
David Ahern30c15f02019-04-09 14:41:15 -0700716 continue;
717
David Ahernb7bc4b62019-04-16 14:36:08 -0700718 nh = &f6i->fib6_nh;
719 if (find_match(nh, f6i->fib6_flags, oif, strict, mpri, do_rr)) {
720 res->f6i = f6i;
721 res->nh = nh;
722 }
David Ahern30c15f02019-04-09 14:41:15 -0700723 }
724}
725
David Ahernb7bc4b62019-04-16 14:36:08 -0700726static void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
727 struct fib6_info *rr_head, int oif, int strict,
728 bool *do_rr, struct fib6_result *res)
David Ahern30c15f02019-04-09 14:41:15 -0700729{
David Ahernb7bc4b62019-04-16 14:36:08 -0700730 u32 metric = rr_head->fib6_metric;
731 struct fib6_info *cont = NULL;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800732 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
David Ahernb7bc4b62019-04-16 14:36:08 -0700734 __find_rr_leaf(rr_head, NULL, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700735 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700736
David Ahernb7bc4b62019-04-16 14:36:08 -0700737 __find_rr_leaf(leaf, rr_head, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700738 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700739
David Ahernb7bc4b62019-04-16 14:36:08 -0700740 if (res->f6i || !cont)
741 return;
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700742
David Ahernb7bc4b62019-04-16 14:36:08 -0700743 __find_rr_leaf(cont, NULL, metric, res, NULL,
David Ahern30c15f02019-04-09 14:41:15 -0700744 oif, strict, do_rr, &mpri);
David S. Millerf11e6652007-03-24 20:36:25 -0700745}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800746
David Ahernb7bc4b62019-04-16 14:36:08 -0700747static void rt6_select(struct net *net, struct fib6_node *fn, int oif,
748 struct fib6_result *res, int strict)
David S. Millerf11e6652007-03-24 20:36:25 -0700749{
David Ahern8d1c8022018-04-17 17:33:26 -0700750 struct fib6_info *leaf = rcu_dereference(fn->leaf);
David Ahernb7bc4b62019-04-16 14:36:08 -0700751 struct fib6_info *rt0;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200752 bool do_rr = false;
Wei Wang17ecf592017-10-06 12:06:09 -0700753 int key_plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
David Ahernb7bc4b62019-04-16 14:36:08 -0700755 /* make sure this function or its helpers sets f6i */
756 res->f6i = NULL;
757
David Ahern421842e2018-04-17 17:33:18 -0700758 if (!leaf || leaf == net->ipv6.fib6_null_entry)
David Ahernb7bc4b62019-04-16 14:36:08 -0700759 goto out;
Wei Wang8d1040e2017-10-06 12:06:08 -0700760
Wei Wang66f5d6c2017-10-06 12:06:10 -0700761 rt0 = rcu_dereference(fn->rr_ptr);
David S. Millerf11e6652007-03-24 20:36:25 -0700762 if (!rt0)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700763 rt0 = leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Wei Wang17ecf592017-10-06 12:06:09 -0700765 /* Double check to make sure fn is not an intermediate node
766 * and fn->leaf does not points to its child's leaf
767 * (This might happen if all routes under fn are deleted from
768 * the tree and fib6_repair_tree() is called on the node.)
769 */
David Ahern93c2fb22018-04-18 15:38:59 -0700770 key_plen = rt0->fib6_dst.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700771#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -0700772 if (rt0->fib6_src.plen)
773 key_plen = rt0->fib6_src.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700774#endif
775 if (fn->fn_bit != key_plen)
David Ahernb7bc4b62019-04-16 14:36:08 -0700776 goto out;
Wei Wang17ecf592017-10-06 12:06:09 -0700777
David Ahernb7bc4b62019-04-16 14:36:08 -0700778 find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200779 if (do_rr) {
David Ahern8fb11a92018-05-04 13:54:24 -0700780 struct fib6_info *next = rcu_dereference(rt0->fib6_next);
David S. Millerf11e6652007-03-24 20:36:25 -0700781
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800782 /* no entries matched; do round-robin */
David Ahern93c2fb22018-04-18 15:38:59 -0700783 if (!next || next->fib6_metric != rt0->fib6_metric)
Wei Wang8d1040e2017-10-06 12:06:08 -0700784 next = leaf;
David S. Millerf11e6652007-03-24 20:36:25 -0700785
Wei Wang66f5d6c2017-10-06 12:06:10 -0700786 if (next != rt0) {
David Ahern93c2fb22018-04-18 15:38:59 -0700787 spin_lock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700788 /* make sure next is not being deleted from the tree */
David Ahern93c2fb22018-04-18 15:38:59 -0700789 if (next->fib6_node)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700790 rcu_assign_pointer(fn->rr_ptr, next);
David Ahern93c2fb22018-04-18 15:38:59 -0700791 spin_unlock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700792 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 }
794
David Ahernb7bc4b62019-04-16 14:36:08 -0700795out:
796 if (!res->f6i) {
797 res->f6i = net->ipv6.fib6_null_entry;
798 res->nh = &res->f6i->fib6_nh;
799 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800}
801
David Ahern85bd05d2019-04-16 14:36:01 -0700802static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700803{
David Ahern85bd05d2019-04-16 14:36:01 -0700804 return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
805 res->nh->fib_nh_gw_family;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700806}
807
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800808#ifdef CONFIG_IPV6_ROUTE_INFO
809int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000810 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800811{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900812 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800813 struct route_info *rinfo = (struct route_info *) opt;
814 struct in6_addr prefix_buf, *prefix;
815 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900816 unsigned long lifetime;
David Ahern8d1c8022018-04-17 17:33:26 -0700817 struct fib6_info *rt;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800818
819 if (len < sizeof(struct route_info)) {
820 return -EINVAL;
821 }
822
823 /* Sanity check for prefix_len and length */
824 if (rinfo->length > 3) {
825 return -EINVAL;
826 } else if (rinfo->prefix_len > 128) {
827 return -EINVAL;
828 } else if (rinfo->prefix_len > 64) {
829 if (rinfo->length < 2) {
830 return -EINVAL;
831 }
832 } else if (rinfo->prefix_len > 0) {
833 if (rinfo->length < 1) {
834 return -EINVAL;
835 }
836 }
837
838 pref = rinfo->route_pref;
839 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000840 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800841
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900842 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800843
844 if (rinfo->length == 3)
845 prefix = (struct in6_addr *)rinfo->prefix;
846 else {
847 /* this function is safe */
848 ipv6_addr_prefix(&prefix_buf,
849 (struct in6_addr *)rinfo->prefix,
850 rinfo->prefix_len);
851 prefix = &prefix_buf;
852 }
853
Duan Jiongf104a562013-11-08 09:56:53 +0800854 if (rinfo->prefix_len == 0)
David Ahernafb1d4b52018-04-17 17:33:11 -0700855 rt = rt6_get_dflt_router(net, gwaddr, dev);
Duan Jiongf104a562013-11-08 09:56:53 +0800856 else
857 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
David Ahern830218c2016-10-24 10:52:35 -0700858 gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800859
860 if (rt && !lifetime) {
David Ahernafb1d4b52018-04-17 17:33:11 -0700861 ip6_del_rt(net, rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800862 rt = NULL;
863 }
864
865 if (!rt && lifetime)
David Ahern830218c2016-10-24 10:52:35 -0700866 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
867 dev, pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800868 else if (rt)
David Ahern93c2fb22018-04-18 15:38:59 -0700869 rt->fib6_flags = RTF_ROUTEINFO |
870 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800871
872 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000873 if (!addrconf_finite_timeout(lifetime))
David Ahern14895682018-04-17 17:33:17 -0700874 fib6_clean_expires(rt);
Gao feng1716a962012-04-06 00:13:10 +0000875 else
David Ahern14895682018-04-17 17:33:17 -0700876 fib6_set_expires(rt, jiffies + HZ * lifetime);
Gao feng1716a962012-04-06 00:13:10 +0000877
David Ahern93531c62018-04-17 17:33:25 -0700878 fib6_info_release(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800879 }
880 return 0;
881}
882#endif
883
David Ahernae90d862018-04-17 17:33:12 -0700884/*
885 * Misc support functions
886 */
887
888/* called with rcu_lock held */
David Ahern0d161582019-04-16 14:36:04 -0700889static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -0700890{
David Ahern0d161582019-04-16 14:36:04 -0700891 struct net_device *dev = res->nh->fib_nh_dev;
892 const struct fib6_info *f6i = res->f6i;
David Ahernae90d862018-04-17 17:33:12 -0700893
David Ahern0d161582019-04-16 14:36:04 -0700894 if (f6i->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
David Ahernae90d862018-04-17 17:33:12 -0700895 /* for copies of local routes, dst->dev needs to be the
896 * device if it is a master device, the master device if
897 * device is enslaved, and the loopback as the default
898 */
899 if (netif_is_l3_slave(dev) &&
David Ahern0d161582019-04-16 14:36:04 -0700900 !rt6_need_strict(&f6i->fib6_dst.addr))
David Ahernae90d862018-04-17 17:33:12 -0700901 dev = l3mdev_master_dev_rcu(dev);
902 else if (!netif_is_l3_master(dev))
903 dev = dev_net(dev)->loopback_dev;
904 /* last case is netif_is_l3_master(dev) is true in which
905 * case we want dev returned to be dev
906 */
907 }
908
909 return dev;
910}
911
David Ahern6edb3c92018-04-17 17:33:15 -0700912static const int fib6_prop[RTN_MAX + 1] = {
913 [RTN_UNSPEC] = 0,
914 [RTN_UNICAST] = 0,
915 [RTN_LOCAL] = 0,
916 [RTN_BROADCAST] = 0,
917 [RTN_ANYCAST] = 0,
918 [RTN_MULTICAST] = 0,
919 [RTN_BLACKHOLE] = -EINVAL,
920 [RTN_UNREACHABLE] = -EHOSTUNREACH,
921 [RTN_PROHIBIT] = -EACCES,
922 [RTN_THROW] = -EAGAIN,
923 [RTN_NAT] = -EINVAL,
924 [RTN_XRESOLVE] = -EINVAL,
925};
926
927static int ip6_rt_type_to_error(u8 fib6_type)
928{
929 return fib6_prop[fib6_type];
930}
931
David Ahern8d1c8022018-04-17 17:33:26 -0700932static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
David Ahern3b6761d2018-04-17 17:33:20 -0700933{
934 unsigned short flags = 0;
935
936 if (rt->dst_nocount)
937 flags |= DST_NOCOUNT;
938 if (rt->dst_nopolicy)
939 flags |= DST_NOPOLICY;
940 if (rt->dst_host)
941 flags |= DST_HOST;
942
943 return flags;
944}
945
David Ahern8d1c8022018-04-17 17:33:26 -0700946static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct fib6_info *ort)
David Ahern6edb3c92018-04-17 17:33:15 -0700947{
948 rt->dst.error = ip6_rt_type_to_error(ort->fib6_type);
949
950 switch (ort->fib6_type) {
951 case RTN_BLACKHOLE:
952 rt->dst.output = dst_discard_out;
953 rt->dst.input = dst_discard;
954 break;
955 case RTN_PROHIBIT:
956 rt->dst.output = ip6_pkt_prohibit_out;
957 rt->dst.input = ip6_pkt_prohibit;
958 break;
959 case RTN_THROW:
960 case RTN_UNREACHABLE:
961 default:
962 rt->dst.output = ip6_pkt_discard_out;
963 rt->dst.input = ip6_pkt_discard;
964 break;
965 }
966}
967
David Ahern0d161582019-04-16 14:36:04 -0700968static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
David Ahern6edb3c92018-04-17 17:33:15 -0700969{
David Ahern0d161582019-04-16 14:36:04 -0700970 struct fib6_info *ort = res->f6i;
971
David Ahern93c2fb22018-04-18 15:38:59 -0700972 if (ort->fib6_flags & RTF_REJECT) {
David Ahern6edb3c92018-04-17 17:33:15 -0700973 ip6_rt_init_dst_reject(rt, ort);
974 return;
975 }
976
977 rt->dst.error = 0;
978 rt->dst.output = ip6_output;
979
Hangbin Liud23c4b62018-08-23 11:31:37 +0800980 if (ort->fib6_type == RTN_LOCAL || ort->fib6_type == RTN_ANYCAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700981 rt->dst.input = ip6_input;
David Ahern93c2fb22018-04-18 15:38:59 -0700982 } else if (ipv6_addr_type(&ort->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700983 rt->dst.input = ip6_mc_input;
984 } else {
985 rt->dst.input = ip6_forward;
986 }
987
David Ahern0d161582019-04-16 14:36:04 -0700988 if (res->nh->fib_nh_lws) {
989 rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
David Ahern6edb3c92018-04-17 17:33:15 -0700990 lwtunnel_set_redirect(&rt->dst);
991 }
992
993 rt->dst.lastuse = jiffies;
994}
995
Wei Wange873e4b2018-07-21 20:56:32 -0700996/* Caller must already hold reference to @from */
David Ahern8d1c8022018-04-17 17:33:26 -0700997static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
David Ahernae90d862018-04-17 17:33:12 -0700998{
David Ahernae90d862018-04-17 17:33:12 -0700999 rt->rt6i_flags &= ~RTF_EXPIRES;
David Aherna68886a2018-04-20 15:38:02 -07001000 rcu_assign_pointer(rt->from, from);
David Aherne1255ed2018-10-04 20:07:53 -07001001 ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
David Ahernae90d862018-04-17 17:33:12 -07001002}
1003
David Ahern0d161582019-04-16 14:36:04 -07001004/* Caller must already hold reference to f6i in result */
1005static void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -07001006{
David Ahern0d161582019-04-16 14:36:04 -07001007 const struct fib6_nh *nh = res->nh;
1008 const struct net_device *dev = nh->fib_nh_dev;
1009 struct fib6_info *f6i = res->f6i;
David Aherndcd1f572018-04-18 15:39:05 -07001010
David Ahern0d161582019-04-16 14:36:04 -07001011 ip6_rt_init_dst(rt, res);
David Ahern6edb3c92018-04-17 17:33:15 -07001012
David Ahern0d161582019-04-16 14:36:04 -07001013 rt->rt6i_dst = f6i->fib6_dst;
David Aherndcd1f572018-04-18 15:39:05 -07001014 rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
David Ahern0d161582019-04-16 14:36:04 -07001015 rt->rt6i_flags = f6i->fib6_flags;
1016 if (nh->fib_nh_gw_family) {
1017 rt->rt6i_gateway = nh->fib_nh_gw6;
David Ahern2b2450c2019-03-27 20:53:52 -07001018 rt->rt6i_flags |= RTF_GATEWAY;
1019 }
David Ahern0d161582019-04-16 14:36:04 -07001020 rt6_set_from(rt, f6i);
David Ahernae90d862018-04-17 17:33:12 -07001021#ifdef CONFIG_IPV6_SUBTREES
David Ahern0d161582019-04-16 14:36:04 -07001022 rt->rt6i_src = f6i->fib6_src;
David Ahernae90d862018-04-17 17:33:12 -07001023#endif
David Ahernae90d862018-04-17 17:33:12 -07001024}
1025
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001026static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1027 struct in6_addr *saddr)
1028{
Wei Wang66f5d6c2017-10-06 12:06:10 -07001029 struct fib6_node *pn, *sn;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001030 while (1) {
1031 if (fn->fn_flags & RTN_TL_ROOT)
1032 return NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001033 pn = rcu_dereference(fn->parent);
1034 sn = FIB6_SUBTREE(pn);
1035 if (sn && sn != fn)
David Ahern64547432018-05-09 20:34:19 -07001036 fn = fib6_node_lookup(sn, NULL, saddr);
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001037 else
1038 fn = pn;
1039 if (fn->fn_flags & RTN_RTINFO)
1040 return fn;
1041 }
1042}
Thomas Grafc71099a2006-08-04 23:20:06 -07001043
David Ahern10585b42019-03-20 09:24:50 -07001044static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
Wei Wangd3843fe2017-10-06 12:06:06 -07001045{
1046 struct rt6_info *rt = *prt;
1047
1048 if (dst_hold_safe(&rt->dst))
1049 return true;
David Ahern10585b42019-03-20 09:24:50 -07001050 if (net) {
Wei Wangd3843fe2017-10-06 12:06:06 -07001051 rt = net->ipv6.ip6_null_entry;
1052 dst_hold(&rt->dst);
1053 } else {
1054 rt = NULL;
1055 }
1056 *prt = rt;
1057 return false;
1058}
1059
David Aherndec9b0e2018-04-17 17:33:19 -07001060/* called with rcu_lock held */
David Ahern9b6b35a2019-04-16 14:36:02 -07001061static struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
David Aherndec9b0e2018-04-17 17:33:19 -07001062{
David Ahern9b6b35a2019-04-16 14:36:02 -07001063 struct net_device *dev = res->nh->fib_nh_dev;
1064 struct fib6_info *f6i = res->f6i;
1065 unsigned short flags;
David Aherndec9b0e2018-04-17 17:33:19 -07001066 struct rt6_info *nrt;
1067
David Ahern9b6b35a2019-04-16 14:36:02 -07001068 if (!fib6_info_hold_safe(f6i))
Xin Long1c87e792019-03-20 14:45:48 +08001069 goto fallback;
Wei Wange873e4b2018-07-21 20:56:32 -07001070
David Ahern9b6b35a2019-04-16 14:36:02 -07001071 flags = fib6_info_dst_flags(f6i);
David Ahern93531c62018-04-17 17:33:25 -07001072 nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
Xin Long1c87e792019-03-20 14:45:48 +08001073 if (!nrt) {
David Ahern9b6b35a2019-04-16 14:36:02 -07001074 fib6_info_release(f6i);
Xin Long1c87e792019-03-20 14:45:48 +08001075 goto fallback;
1076 }
David Aherndec9b0e2018-04-17 17:33:19 -07001077
David Ahern0d161582019-04-16 14:36:04 -07001078 ip6_rt_copy_init(nrt, res);
Xin Long1c87e792019-03-20 14:45:48 +08001079 return nrt;
1080
1081fallback:
1082 nrt = dev_net(dev)->ipv6.ip6_null_entry;
1083 dst_hold(&nrt->dst);
David Aherndec9b0e2018-04-17 17:33:19 -07001084 return nrt;
1085}
1086
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001087static struct rt6_info *ip6_pol_route_lookup(struct net *net,
1088 struct fib6_table *table,
David Ahernb75cc8f2018-03-02 08:32:17 -08001089 struct flowi6 *fl6,
1090 const struct sk_buff *skb,
1091 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092{
David Ahernb1d40992019-04-16 14:35:59 -07001093 struct fib6_result res = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 struct fib6_node *fn;
David Ahern23fb93a2018-04-17 17:33:23 -07001095 struct rt6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
David Ahernb6cdbc82018-03-29 17:44:57 -07001097 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1098 flags &= ~RT6_LOOKUP_F_IFACE;
1099
Wei Wang66f5d6c2017-10-06 12:06:10 -07001100 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07001101 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -07001102restart:
David Ahernb1d40992019-04-16 14:35:59 -07001103 res.f6i = rcu_dereference(fn->leaf);
1104 if (!res.f6i)
1105 res.f6i = net->ipv6.fib6_null_entry;
David Ahernaf52a522019-04-09 14:41:16 -07001106 else
David Ahern75ef7382019-04-16 14:36:07 -07001107 rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
1108 flags);
David Ahernaf52a522019-04-09 14:41:16 -07001109
David Ahernb1d40992019-04-16 14:35:59 -07001110 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001111 fn = fib6_backtrack(fn, &fl6->saddr);
1112 if (fn)
1113 goto restart;
David Ahernaf52a522019-04-09 14:41:16 -07001114
1115 rt = net->ipv6.ip6_null_entry;
1116 dst_hold(&rt->dst);
1117 goto out;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001118 }
Wei Wang2b760fc2017-10-06 12:06:03 -07001119
David Ahernb1d40992019-04-16 14:35:59 -07001120 fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1121 fl6->flowi6_oif != 0, skb, flags);
1122
David S. Miller4c9483b2011-03-12 16:22:43 -05001123 /* Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001124 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001125 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001126 if (ip6_hold_safe(net, &rt))
David Aherndec9b0e2018-04-17 17:33:19 -07001127 dst_use_noref(&rt->dst, jiffies);
David Ahern23fb93a2018-04-17 17:33:23 -07001128 } else {
David Ahern9b6b35a2019-04-16 14:36:02 -07001129 rt = ip6_create_rt_rcu(&res);
David Aherndec9b0e2018-04-17 17:33:19 -07001130 }
Wei Wangd3843fe2017-10-06 12:06:06 -07001131
David Ahernaf52a522019-04-09 14:41:16 -07001132out:
David Ahern8ff2e5b2019-04-16 14:36:09 -07001133 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahernaf52a522019-04-09 14:41:16 -07001134
Wei Wang66f5d6c2017-10-06 12:06:10 -07001135 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08001136
Thomas Grafc71099a2006-08-04 23:20:06 -07001137 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001138}
1139
Ian Morris67ba4152014-08-24 21:53:10 +01001140struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08001141 const struct sk_buff *skb, int flags)
Florian Westphalea6e5742011-09-05 16:05:44 +02001142{
David Ahernb75cc8f2018-03-02 08:32:17 -08001143 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
Florian Westphalea6e5742011-09-05 16:05:44 +02001144}
1145EXPORT_SYMBOL_GPL(ip6_route_lookup);
1146
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001147struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
David Ahernb75cc8f2018-03-02 08:32:17 -08001148 const struct in6_addr *saddr, int oif,
1149 const struct sk_buff *skb, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -07001150{
David S. Miller4c9483b2011-03-12 16:22:43 -05001151 struct flowi6 fl6 = {
1152 .flowi6_oif = oif,
1153 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -07001154 };
1155 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001156 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07001157
Thomas Grafadaa70b2006-10-13 15:01:03 -07001158 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -05001159 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -07001160 flags |= RT6_LOOKUP_F_HAS_SADDR;
1161 }
1162
David Ahernb75cc8f2018-03-02 08:32:17 -08001163 dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -07001164 if (dst->error == 0)
1165 return (struct rt6_info *) dst;
1166
1167 dst_release(dst);
1168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 return NULL;
1170}
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001171EXPORT_SYMBOL(rt6_lookup);
1172
Thomas Grafc71099a2006-08-04 23:20:06 -07001173/* ip6_ins_rt is called with FREE table->tb6_lock.
Wei Wang1cfb71e2017-06-17 10:42:33 -07001174 * It takes new route entry, the addition fails by any reason the
1175 * route is released.
1176 * Caller must hold dst before calling it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 */
1178
David Ahern8d1c8022018-04-17 17:33:26 -07001179static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
David Ahern333c4302017-05-21 10:12:04 -06001180 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181{
1182 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001183 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184
David Ahern93c2fb22018-04-18 15:38:59 -07001185 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001186 spin_lock_bh(&table->tb6_lock);
David Ahernd4ead6b2018-04-17 17:33:16 -07001187 err = fib6_add(&table->tb6_root, rt, info, extack);
Wei Wang66f5d6c2017-10-06 12:06:10 -07001188 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
1190 return err;
1191}
1192
David Ahern8d1c8022018-04-17 17:33:26 -07001193int ip6_ins_rt(struct net *net, struct fib6_info *rt)
Thomas Graf40e22e82006-08-22 00:00:45 -07001194{
David Ahernafb1d4b52018-04-17 17:33:11 -07001195 struct nl_info info = { .nl_net = net, };
Florian Westphale715b6d2015-01-05 23:57:44 +01001196
David Ahernd4ead6b2018-04-17 17:33:16 -07001197 return __ip6_ins_rt(rt, &info, NULL);
Thomas Graf40e22e82006-08-22 00:00:45 -07001198}
1199
David Ahern85bd05d2019-04-16 14:36:01 -07001200static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001201 const struct in6_addr *daddr,
1202 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203{
David Ahern85bd05d2019-04-16 14:36:01 -07001204 struct fib6_info *f6i = res->f6i;
David Ahern4832c302017-08-17 12:17:20 -07001205 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 struct rt6_info *rt;
1207
1208 /*
1209 * Clone the route.
1210 */
1211
David Ahern85bd05d2019-04-16 14:36:01 -07001212 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001213 return NULL;
1214
David Ahern0d161582019-04-16 14:36:04 -07001215 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001216 rt = ip6_dst_alloc(dev_net(dev), dev, 0);
Wei Wange873e4b2018-07-21 20:56:32 -07001217 if (!rt) {
David Ahern85bd05d2019-04-16 14:36:01 -07001218 fib6_info_release(f6i);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001219 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001220 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001221
David Ahern0d161582019-04-16 14:36:04 -07001222 ip6_rt_copy_init(rt, res);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001223 rt->rt6i_flags |= RTF_CACHE;
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001224 rt->dst.flags |= DST_HOST;
1225 rt->rt6i_dst.addr = *daddr;
1226 rt->rt6i_dst.plen = 128;
1227
David Ahern85bd05d2019-04-16 14:36:01 -07001228 if (!rt6_is_gw_or_nonexthop(res)) {
1229 if (f6i->fib6_dst.plen != 128 &&
1230 ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001231 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232#ifdef CONFIG_IPV6_SUBTREES
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001233 if (rt->rt6i_src.plen && saddr) {
1234 rt->rt6i_src.addr = *saddr;
1235 rt->rt6i_src.plen = 128;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001236 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001237#endif
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001238 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001240 return rt;
1241}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
David Aherndb3fede2019-04-16 14:36:03 -07001243static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001244{
David Aherndb3fede2019-04-16 14:36:03 -07001245 struct fib6_info *f6i = res->f6i;
1246 unsigned short flags = fib6_info_dst_flags(f6i);
David Ahern4832c302017-08-17 12:17:20 -07001247 struct net_device *dev;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001248 struct rt6_info *pcpu_rt;
1249
David Aherndb3fede2019-04-16 14:36:03 -07001250 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001251 return NULL;
1252
David Ahern4832c302017-08-17 12:17:20 -07001253 rcu_read_lock();
David Ahern0d161582019-04-16 14:36:04 -07001254 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001255 pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags);
David Ahern4832c302017-08-17 12:17:20 -07001256 rcu_read_unlock();
Wei Wange873e4b2018-07-21 20:56:32 -07001257 if (!pcpu_rt) {
David Aherndb3fede2019-04-16 14:36:03 -07001258 fib6_info_release(f6i);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001259 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001260 }
David Ahern0d161582019-04-16 14:36:04 -07001261 ip6_rt_copy_init(pcpu_rt, res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001262 pcpu_rt->rt6i_flags |= RTF_PCPU;
1263 return pcpu_rt;
1264}
1265
Wei Wang66f5d6c2017-10-06 12:06:10 -07001266/* It should be called with rcu_read_lock() acquired */
David Aherndb3fede2019-04-16 14:36:03 -07001267static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001268{
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001269 struct rt6_info *pcpu_rt, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001270
David Aherndb3fede2019-04-16 14:36:03 -07001271 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001272 pcpu_rt = *p;
1273
David Ahernd4ead6b2018-04-17 17:33:16 -07001274 if (pcpu_rt)
David Ahern10585b42019-03-20 09:24:50 -07001275 ip6_hold_safe(NULL, &pcpu_rt);
Wei Wangd3843fe2017-10-06 12:06:06 -07001276
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001277 return pcpu_rt;
1278}
1279
David Ahernafb1d4b52018-04-17 17:33:11 -07001280static struct rt6_info *rt6_make_pcpu_route(struct net *net,
David Aherndb3fede2019-04-16 14:36:03 -07001281 const struct fib6_result *res)
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001282{
1283 struct rt6_info *pcpu_rt, *prev, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001284
David Aherndb3fede2019-04-16 14:36:03 -07001285 pcpu_rt = ip6_rt_pcpu_alloc(res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001286 if (!pcpu_rt) {
Martin KaFai Lau9c7370a2015-08-14 11:05:54 -07001287 dst_hold(&net->ipv6.ip6_null_entry->dst);
1288 return net->ipv6.ip6_null_entry;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001289 }
1290
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001291 dst_hold(&pcpu_rt->dst);
David Aherndb3fede2019-04-16 14:36:03 -07001292 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Wei Wanga94b9362017-10-06 12:06:04 -07001293 prev = cmpxchg(p, NULL, pcpu_rt);
Eric Dumazet951f7882017-10-08 21:07:18 -07001294 BUG_ON(prev);
Wei Wanga94b9362017-10-06 12:06:04 -07001295
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001296 return pcpu_rt;
1297}
1298
Wei Wang35732d02017-10-06 12:05:57 -07001299/* exception hash table implementation
1300 */
1301static DEFINE_SPINLOCK(rt6_exception_lock);
1302
1303/* Remove rt6_ex from hash table and free the memory
1304 * Caller must hold rt6_exception_lock
1305 */
1306static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1307 struct rt6_exception *rt6_ex)
1308{
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001309 struct fib6_info *from;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001310 struct net *net;
Wei Wang81eb8442017-10-06 12:06:11 -07001311
Wei Wang35732d02017-10-06 12:05:57 -07001312 if (!bucket || !rt6_ex)
1313 return;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001314
1315 net = dev_net(rt6_ex->rt6i->dst.dev);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001316 net->ipv6.rt6_stats->fib_rt_cache--;
1317
1318 /* purge completely the exception to allow releasing the held resources:
1319 * some [sk] cache may keep the dst around for unlimited time
1320 */
1321 from = rcu_dereference_protected(rt6_ex->rt6i->from,
1322 lockdep_is_held(&rt6_exception_lock));
1323 rcu_assign_pointer(rt6_ex->rt6i->from, NULL);
1324 fib6_info_release(from);
1325 dst_dev_put(&rt6_ex->rt6i->dst);
1326
Wei Wang35732d02017-10-06 12:05:57 -07001327 hlist_del_rcu(&rt6_ex->hlist);
David Ahern77634cc2018-04-17 17:33:27 -07001328 dst_release(&rt6_ex->rt6i->dst);
Wei Wang35732d02017-10-06 12:05:57 -07001329 kfree_rcu(rt6_ex, rcu);
1330 WARN_ON_ONCE(!bucket->depth);
1331 bucket->depth--;
1332}
1333
1334/* Remove oldest rt6_ex in bucket and free the memory
1335 * Caller must hold rt6_exception_lock
1336 */
1337static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
1338{
1339 struct rt6_exception *rt6_ex, *oldest = NULL;
1340
1341 if (!bucket)
1342 return;
1343
1344 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1345 if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
1346 oldest = rt6_ex;
1347 }
1348 rt6_remove_exception(bucket, oldest);
1349}
1350
1351static u32 rt6_exception_hash(const struct in6_addr *dst,
1352 const struct in6_addr *src)
1353{
1354 static u32 seed __read_mostly;
1355 u32 val;
1356
1357 net_get_random_once(&seed, sizeof(seed));
1358 val = jhash(dst, sizeof(*dst), seed);
1359
1360#ifdef CONFIG_IPV6_SUBTREES
1361 if (src)
1362 val = jhash(src, sizeof(*src), val);
1363#endif
1364 return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
1365}
1366
1367/* Helper function to find the cached rt in the hash table
1368 * and update bucket pointer to point to the bucket for this
1369 * (daddr, saddr) pair
1370 * Caller must hold rt6_exception_lock
1371 */
1372static struct rt6_exception *
1373__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
1374 const struct in6_addr *daddr,
1375 const struct in6_addr *saddr)
1376{
1377 struct rt6_exception *rt6_ex;
1378 u32 hval;
1379
1380 if (!(*bucket) || !daddr)
1381 return NULL;
1382
1383 hval = rt6_exception_hash(daddr, saddr);
1384 *bucket += hval;
1385
1386 hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
1387 struct rt6_info *rt6 = rt6_ex->rt6i;
1388 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1389
1390#ifdef CONFIG_IPV6_SUBTREES
1391 if (matched && saddr)
1392 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1393#endif
1394 if (matched)
1395 return rt6_ex;
1396 }
1397 return NULL;
1398}
1399
1400/* Helper function to find the cached rt in the hash table
1401 * and update bucket pointer to point to the bucket for this
1402 * (daddr, saddr) pair
1403 * Caller must hold rcu_read_lock()
1404 */
1405static struct rt6_exception *
1406__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
1407 const struct in6_addr *daddr,
1408 const struct in6_addr *saddr)
1409{
1410 struct rt6_exception *rt6_ex;
1411 u32 hval;
1412
1413 WARN_ON_ONCE(!rcu_read_lock_held());
1414
1415 if (!(*bucket) || !daddr)
1416 return NULL;
1417
1418 hval = rt6_exception_hash(daddr, saddr);
1419 *bucket += hval;
1420
1421 hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
1422 struct rt6_info *rt6 = rt6_ex->rt6i;
1423 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1424
1425#ifdef CONFIG_IPV6_SUBTREES
1426 if (matched && saddr)
1427 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1428#endif
1429 if (matched)
1430 return rt6_ex;
1431 }
1432 return NULL;
1433}
1434
David Ahernb748f262019-04-16 14:36:06 -07001435static unsigned int fib6_mtu(const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001436{
David Ahernb748f262019-04-16 14:36:06 -07001437 const struct fib6_nh *nh = res->nh;
David Ahernd4ead6b2018-04-17 17:33:16 -07001438 unsigned int mtu;
1439
David Ahernb748f262019-04-16 14:36:06 -07001440 if (res->f6i->fib6_pmtu) {
1441 mtu = res->f6i->fib6_pmtu;
David Aherndcd1f572018-04-18 15:39:05 -07001442 } else {
David Ahernb748f262019-04-16 14:36:06 -07001443 struct net_device *dev = nh->fib_nh_dev;
David Aherndcd1f572018-04-18 15:39:05 -07001444 struct inet6_dev *idev;
1445
1446 rcu_read_lock();
1447 idev = __in6_dev_get(dev);
1448 mtu = idev->cnf.mtu6;
1449 rcu_read_unlock();
1450 }
1451
David Ahernd4ead6b2018-04-17 17:33:16 -07001452 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1453
David Ahernb748f262019-04-16 14:36:06 -07001454 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahernd4ead6b2018-04-17 17:33:16 -07001455}
1456
Wei Wang35732d02017-10-06 12:05:57 -07001457static int rt6_insert_exception(struct rt6_info *nrt,
David Ahern5012f0a2019-04-16 14:36:05 -07001458 const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001459{
David Ahern5e670d82018-04-17 17:33:14 -07001460 struct net *net = dev_net(nrt->dst.dev);
Wei Wang35732d02017-10-06 12:05:57 -07001461 struct rt6_exception_bucket *bucket;
1462 struct in6_addr *src_key = NULL;
1463 struct rt6_exception *rt6_ex;
David Ahern5012f0a2019-04-16 14:36:05 -07001464 struct fib6_info *f6i = res->f6i;
Wei Wang35732d02017-10-06 12:05:57 -07001465 int err = 0;
1466
Wei Wang35732d02017-10-06 12:05:57 -07001467 spin_lock_bh(&rt6_exception_lock);
1468
David Ahern5012f0a2019-04-16 14:36:05 -07001469 if (f6i->exception_bucket_flushed) {
Wei Wang35732d02017-10-06 12:05:57 -07001470 err = -EINVAL;
1471 goto out;
1472 }
1473
David Ahern5012f0a2019-04-16 14:36:05 -07001474 bucket = rcu_dereference_protected(f6i->rt6i_exception_bucket,
Wei Wang35732d02017-10-06 12:05:57 -07001475 lockdep_is_held(&rt6_exception_lock));
1476 if (!bucket) {
1477 bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
1478 GFP_ATOMIC);
1479 if (!bucket) {
1480 err = -ENOMEM;
1481 goto out;
1482 }
David Ahern5012f0a2019-04-16 14:36:05 -07001483 rcu_assign_pointer(f6i->rt6i_exception_bucket, bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001484 }
1485
1486#ifdef CONFIG_IPV6_SUBTREES
David Ahern5012f0a2019-04-16 14:36:05 -07001487 /* fib6_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001488 * and exception table is indexed by a hash of
David Ahern5012f0a2019-04-16 14:36:05 -07001489 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001490 * Otherwise, the exception table is indexed by
David Ahern5012f0a2019-04-16 14:36:05 -07001491 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001492 */
David Ahern5012f0a2019-04-16 14:36:05 -07001493 if (f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001494 src_key = &nrt->rt6i_src.addr;
1495#endif
David Ahern5012f0a2019-04-16 14:36:05 -07001496 /* rt6_mtu_change() might lower mtu on f6i.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001497 * Only insert this exception route if its mtu
David Ahern5012f0a2019-04-16 14:36:05 -07001498 * is less than f6i's mtu value.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001499 */
David Ahernb748f262019-04-16 14:36:06 -07001500 if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001501 err = -EINVAL;
1502 goto out;
1503 }
Wei Wang60006a42017-10-06 12:05:58 -07001504
Wei Wang35732d02017-10-06 12:05:57 -07001505 rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
1506 src_key);
1507 if (rt6_ex)
1508 rt6_remove_exception(bucket, rt6_ex);
1509
1510 rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
1511 if (!rt6_ex) {
1512 err = -ENOMEM;
1513 goto out;
1514 }
1515 rt6_ex->rt6i = nrt;
1516 rt6_ex->stamp = jiffies;
Wei Wang35732d02017-10-06 12:05:57 -07001517 hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
1518 bucket->depth++;
Wei Wang81eb8442017-10-06 12:06:11 -07001519 net->ipv6.rt6_stats->fib_rt_cache++;
Wei Wang35732d02017-10-06 12:05:57 -07001520
1521 if (bucket->depth > FIB6_MAX_DEPTH)
1522 rt6_exception_remove_oldest(bucket);
1523
1524out:
1525 spin_unlock_bh(&rt6_exception_lock);
1526
1527 /* Update fn->fn_sernum to invalidate all cached dst */
Paolo Abenib886d5f2017-10-19 16:07:10 +02001528 if (!err) {
David Ahern5012f0a2019-04-16 14:36:05 -07001529 spin_lock_bh(&f6i->fib6_table->tb6_lock);
1530 fib6_update_sernum(net, f6i);
1531 spin_unlock_bh(&f6i->fib6_table->tb6_lock);
Paolo Abenib886d5f2017-10-19 16:07:10 +02001532 fib6_force_start_gc(net);
1533 }
Wei Wang35732d02017-10-06 12:05:57 -07001534
1535 return err;
1536}
1537
David Ahern8d1c8022018-04-17 17:33:26 -07001538void rt6_flush_exceptions(struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001539{
1540 struct rt6_exception_bucket *bucket;
1541 struct rt6_exception *rt6_ex;
1542 struct hlist_node *tmp;
1543 int i;
1544
1545 spin_lock_bh(&rt6_exception_lock);
1546 /* Prevent rt6_insert_exception() to recreate the bucket list */
1547 rt->exception_bucket_flushed = 1;
1548
1549 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1550 lockdep_is_held(&rt6_exception_lock));
1551 if (!bucket)
1552 goto out;
1553
1554 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1555 hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist)
1556 rt6_remove_exception(bucket, rt6_ex);
1557 WARN_ON_ONCE(bucket->depth);
1558 bucket++;
1559 }
1560
1561out:
1562 spin_unlock_bh(&rt6_exception_lock);
1563}
1564
1565/* Find cached rt in the hash table inside passed in rt
1566 * Caller has to hold rcu_read_lock()
1567 */
David Ahern7e4b5122019-04-16 14:36:00 -07001568static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang35732d02017-10-06 12:05:57 -07001569 struct in6_addr *daddr,
1570 struct in6_addr *saddr)
1571{
1572 struct rt6_exception_bucket *bucket;
1573 struct in6_addr *src_key = NULL;
1574 struct rt6_exception *rt6_ex;
David Ahern7e4b5122019-04-16 14:36:00 -07001575 struct rt6_info *ret = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001576
David Ahern7e4b5122019-04-16 14:36:00 -07001577 bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001578
1579#ifdef CONFIG_IPV6_SUBTREES
David Ahern7e4b5122019-04-16 14:36:00 -07001580 /* fib6i_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001581 * and exception table is indexed by a hash of
David Ahern7e4b5122019-04-16 14:36:00 -07001582 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001583 * Otherwise, the exception table is indexed by
David Ahern7e4b5122019-04-16 14:36:00 -07001584 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001585 */
David Ahern7e4b5122019-04-16 14:36:00 -07001586 if (res->f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001587 src_key = saddr;
1588#endif
1589 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1590
1591 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
David Ahern7e4b5122019-04-16 14:36:00 -07001592 ret = rt6_ex->rt6i;
Wei Wang35732d02017-10-06 12:05:57 -07001593
David Ahern7e4b5122019-04-16 14:36:00 -07001594 return ret;
Wei Wang35732d02017-10-06 12:05:57 -07001595}
1596
1597/* Remove the passed in cached rt from the hash table that contains it */
David Ahern23fb93a2018-04-17 17:33:23 -07001598static int rt6_remove_exception_rt(struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001599{
Wei Wang35732d02017-10-06 12:05:57 -07001600 struct rt6_exception_bucket *bucket;
1601 struct in6_addr *src_key = NULL;
1602 struct rt6_exception *rt6_ex;
David Ahern8a14e462018-04-23 11:32:07 -07001603 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001604 int err;
1605
Eric Dumazet091311d2018-04-24 09:22:49 -07001606 from = rcu_dereference(rt->from);
Wei Wang35732d02017-10-06 12:05:57 -07001607 if (!from ||
Colin Ian King442d7132017-10-10 19:10:30 +01001608 !(rt->rt6i_flags & RTF_CACHE))
Wei Wang35732d02017-10-06 12:05:57 -07001609 return -EINVAL;
1610
1611 if (!rcu_access_pointer(from->rt6i_exception_bucket))
1612 return -ENOENT;
1613
1614 spin_lock_bh(&rt6_exception_lock);
1615 bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
1616 lockdep_is_held(&rt6_exception_lock));
1617#ifdef CONFIG_IPV6_SUBTREES
1618 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1619 * and exception table is indexed by a hash of
1620 * both rt6i_dst and rt6i_src.
1621 * Otherwise, the exception table is indexed by
1622 * a hash of only rt6i_dst.
1623 */
David Ahern93c2fb22018-04-18 15:38:59 -07001624 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001625 src_key = &rt->rt6i_src.addr;
1626#endif
1627 rt6_ex = __rt6_find_exception_spinlock(&bucket,
1628 &rt->rt6i_dst.addr,
1629 src_key);
1630 if (rt6_ex) {
1631 rt6_remove_exception(bucket, rt6_ex);
1632 err = 0;
1633 } else {
1634 err = -ENOENT;
1635 }
1636
1637 spin_unlock_bh(&rt6_exception_lock);
1638 return err;
1639}
1640
1641/* Find rt6_ex which contains the passed in rt cache and
1642 * refresh its stamp
1643 */
1644static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1645{
Wei Wang35732d02017-10-06 12:05:57 -07001646 struct rt6_exception_bucket *bucket;
1647 struct in6_addr *src_key = NULL;
1648 struct rt6_exception *rt6_ex;
Paolo Abeni193f3682019-02-21 11:19:41 +01001649 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001650
1651 rcu_read_lock();
Paolo Abeni193f3682019-02-21 11:19:41 +01001652 from = rcu_dereference(rt->from);
1653 if (!from || !(rt->rt6i_flags & RTF_CACHE))
1654 goto unlock;
1655
Wei Wang35732d02017-10-06 12:05:57 -07001656 bucket = rcu_dereference(from->rt6i_exception_bucket);
1657
1658#ifdef CONFIG_IPV6_SUBTREES
1659 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1660 * and exception table is indexed by a hash of
1661 * both rt6i_dst and rt6i_src.
1662 * Otherwise, the exception table is indexed by
1663 * a hash of only rt6i_dst.
1664 */
David Ahern93c2fb22018-04-18 15:38:59 -07001665 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001666 src_key = &rt->rt6i_src.addr;
1667#endif
1668 rt6_ex = __rt6_find_exception_rcu(&bucket,
1669 &rt->rt6i_dst.addr,
1670 src_key);
1671 if (rt6_ex)
1672 rt6_ex->stamp = jiffies;
1673
Paolo Abeni193f3682019-02-21 11:19:41 +01001674unlock:
Wei Wang35732d02017-10-06 12:05:57 -07001675 rcu_read_unlock();
1676}
1677
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001678static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1679 struct rt6_info *rt, int mtu)
1680{
1681 /* If the new MTU is lower than the route PMTU, this new MTU will be the
1682 * lowest MTU in the path: always allow updating the route PMTU to
1683 * reflect PMTU decreases.
1684 *
1685 * If the new MTU is higher, and the route PMTU is equal to the local
1686 * MTU, this means the old MTU is the lowest in the path, so allow
1687 * updating it: if other nodes now have lower MTUs, PMTU discovery will
1688 * handle this.
1689 */
1690
1691 if (dst_mtu(&rt->dst) >= mtu)
1692 return true;
1693
1694 if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
1695 return true;
1696
1697 return false;
1698}
1699
1700static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
David Ahern8d1c8022018-04-17 17:33:26 -07001701 struct fib6_info *rt, int mtu)
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001702{
1703 struct rt6_exception_bucket *bucket;
1704 struct rt6_exception *rt6_ex;
1705 int i;
1706
1707 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1708 lockdep_is_held(&rt6_exception_lock));
1709
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001710 if (!bucket)
1711 return;
1712
1713 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1714 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1715 struct rt6_info *entry = rt6_ex->rt6i;
1716
1717 /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
David Ahernd4ead6b2018-04-17 17:33:16 -07001718 * route), the metrics of its rt->from have already
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001719 * been updated.
1720 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001721 if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001722 rt6_mtu_change_route_allowed(idev, entry, mtu))
David Ahernd4ead6b2018-04-17 17:33:16 -07001723 dst_metric_set(&entry->dst, RTAX_MTU, mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001724 }
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001725 bucket++;
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001726 }
1727}
1728
Wei Wangb16cb452017-10-06 12:06:00 -07001729#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE)
1730
David Ahern8d1c8022018-04-17 17:33:26 -07001731static void rt6_exceptions_clean_tohost(struct fib6_info *rt,
Wei Wangb16cb452017-10-06 12:06:00 -07001732 struct in6_addr *gateway)
1733{
1734 struct rt6_exception_bucket *bucket;
1735 struct rt6_exception *rt6_ex;
1736 struct hlist_node *tmp;
1737 int i;
1738
1739 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1740 return;
1741
1742 spin_lock_bh(&rt6_exception_lock);
1743 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1744 lockdep_is_held(&rt6_exception_lock));
1745
1746 if (bucket) {
1747 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1748 hlist_for_each_entry_safe(rt6_ex, tmp,
1749 &bucket->chain, hlist) {
1750 struct rt6_info *entry = rt6_ex->rt6i;
1751
1752 if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
1753 RTF_CACHE_GATEWAY &&
1754 ipv6_addr_equal(gateway,
1755 &entry->rt6i_gateway)) {
1756 rt6_remove_exception(bucket, rt6_ex);
1757 }
1758 }
1759 bucket++;
1760 }
1761 }
1762
1763 spin_unlock_bh(&rt6_exception_lock);
1764}
1765
Wei Wangc757faa2017-10-06 12:06:01 -07001766static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
1767 struct rt6_exception *rt6_ex,
1768 struct fib6_gc_args *gc_args,
1769 unsigned long now)
1770{
1771 struct rt6_info *rt = rt6_ex->rt6i;
1772
Paolo Abeni1859bac2017-10-19 16:07:11 +02001773 /* we are pruning and obsoleting aged-out and non gateway exceptions
1774 * even if others have still references to them, so that on next
1775 * dst_check() such references can be dropped.
1776 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
1777 * expired, independently from their aging, as per RFC 8201 section 4
1778 */
Wei Wang31afeb42018-01-26 11:40:17 -08001779 if (!(rt->rt6i_flags & RTF_EXPIRES)) {
1780 if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
1781 RT6_TRACE("aging clone %p\n", rt);
1782 rt6_remove_exception(bucket, rt6_ex);
1783 return;
1784 }
1785 } else if (time_after(jiffies, rt->dst.expires)) {
1786 RT6_TRACE("purging expired route %p\n", rt);
Wei Wangc757faa2017-10-06 12:06:01 -07001787 rt6_remove_exception(bucket, rt6_ex);
1788 return;
Wei Wang31afeb42018-01-26 11:40:17 -08001789 }
1790
1791 if (rt->rt6i_flags & RTF_GATEWAY) {
Wei Wangc757faa2017-10-06 12:06:01 -07001792 struct neighbour *neigh;
1793 __u8 neigh_flags = 0;
1794
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001795 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
1796 if (neigh)
Wei Wangc757faa2017-10-06 12:06:01 -07001797 neigh_flags = neigh->flags;
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001798
Wei Wangc757faa2017-10-06 12:06:01 -07001799 if (!(neigh_flags & NTF_ROUTER)) {
1800 RT6_TRACE("purging route %p via non-router but gateway\n",
1801 rt);
1802 rt6_remove_exception(bucket, rt6_ex);
1803 return;
1804 }
1805 }
Wei Wang31afeb42018-01-26 11:40:17 -08001806
Wei Wangc757faa2017-10-06 12:06:01 -07001807 gc_args->more++;
1808}
1809
David Ahern8d1c8022018-04-17 17:33:26 -07001810void rt6_age_exceptions(struct fib6_info *rt,
Wei Wangc757faa2017-10-06 12:06:01 -07001811 struct fib6_gc_args *gc_args,
1812 unsigned long now)
1813{
1814 struct rt6_exception_bucket *bucket;
1815 struct rt6_exception *rt6_ex;
1816 struct hlist_node *tmp;
1817 int i;
1818
1819 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1820 return;
1821
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001822 rcu_read_lock_bh();
1823 spin_lock(&rt6_exception_lock);
Wei Wangc757faa2017-10-06 12:06:01 -07001824 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1825 lockdep_is_held(&rt6_exception_lock));
1826
1827 if (bucket) {
1828 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1829 hlist_for_each_entry_safe(rt6_ex, tmp,
1830 &bucket->chain, hlist) {
1831 rt6_age_examine_exception(bucket, rt6_ex,
1832 gc_args, now);
1833 }
1834 bucket++;
1835 }
1836 }
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001837 spin_unlock(&rt6_exception_lock);
1838 rcu_read_unlock_bh();
Wei Wangc757faa2017-10-06 12:06:01 -07001839}
1840
David Ahern1d053da2018-05-09 20:34:21 -07001841/* must be called with rcu lock held */
David Aherneffda4d2019-04-16 14:36:10 -07001842int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
1843 struct flowi6 *fl6, struct fib6_result *res, int strict)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844{
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001845 struct fib6_node *fn, *saved_fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846
David Ahern64547432018-05-09 20:34:19 -07001847 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001848 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849
David Ahernca254492015-10-12 11:47:10 -07001850 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1851 oif = 0;
1852
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001853redo_rt6_select:
David Aherneffda4d2019-04-16 14:36:10 -07001854 rt6_select(net, fn, oif, res, strict);
1855 if (res->f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001856 fn = fib6_backtrack(fn, &fl6->saddr);
1857 if (fn)
1858 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001859 else if (strict & RT6_LOOKUP_F_REACHABLE) {
1860 /* also consider unreachable route */
1861 strict &= ~RT6_LOOKUP_F_REACHABLE;
1862 fn = saved_fn;
1863 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001864 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001865 }
1866
David Aherneffda4d2019-04-16 14:36:10 -07001867 trace_fib6_table_lookup(net, res, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08001868
David Aherneffda4d2019-04-16 14:36:10 -07001869 return 0;
David Ahern1d053da2018-05-09 20:34:21 -07001870}
1871
1872struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1873 int oif, struct flowi6 *fl6,
1874 const struct sk_buff *skb, int flags)
1875{
David Ahernb1d40992019-04-16 14:35:59 -07001876 struct fib6_result res = {};
David Ahern1d053da2018-05-09 20:34:21 -07001877 struct rt6_info *rt;
1878 int strict = 0;
1879
1880 strict |= flags & RT6_LOOKUP_F_IFACE;
1881 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1882 if (net->ipv6.devconf_all->forwarding == 0)
1883 strict |= RT6_LOOKUP_F_REACHABLE;
1884
1885 rcu_read_lock();
1886
David Aherneffda4d2019-04-16 14:36:10 -07001887 fib6_table_lookup(net, table, oif, fl6, &res, strict);
David Ahernb1d40992019-04-16 14:35:59 -07001888 if (res.f6i == net->ipv6.fib6_null_entry) {
David Ahern421842e2018-04-17 17:33:18 -07001889 rt = net->ipv6.ip6_null_entry;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001890 rcu_read_unlock();
Wei Wangd3843fe2017-10-06 12:06:06 -07001891 dst_hold(&rt->dst);
Wei Wangd3843fe2017-10-06 12:06:06 -07001892 return rt;
David Ahern23fb93a2018-04-17 17:33:23 -07001893 }
1894
David Ahernb1d40992019-04-16 14:35:59 -07001895 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07001896
David Ahern23fb93a2018-04-17 17:33:23 -07001897 /*Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001898 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001899 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001900 if (ip6_hold_safe(net, &rt))
Wei Wangd3843fe2017-10-06 12:06:06 -07001901 dst_use_noref(&rt->dst, jiffies);
David Ahernd4ead6b2018-04-17 17:33:16 -07001902
Wei Wang66f5d6c2017-10-06 12:06:10 -07001903 rcu_read_unlock();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001904 return rt;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001905 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07001906 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001907 /* Create a RTF_CACHE clone which will not be
1908 * owned by the fib6 tree. It is for the special case where
1909 * the daddr in the skb during the neighbor look-up is different
1910 * from the fl6->daddr used to look-up route here.
1911 */
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001912 struct rt6_info *uncached_rt;
1913
David Ahern85bd05d2019-04-16 14:36:01 -07001914 uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001915
David Ahern4d85cd02018-04-20 15:37:59 -07001916 rcu_read_unlock();
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001917
Wei Wang1cfb71e2017-06-17 10:42:33 -07001918 if (uncached_rt) {
1919 /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
1920 * No need for another dst_hold()
1921 */
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07001922 rt6_uncached_list_add(uncached_rt);
Wei Wang81eb8442017-10-06 12:06:11 -07001923 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang1cfb71e2017-06-17 10:42:33 -07001924 } else {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001925 uncached_rt = net->ipv6.ip6_null_entry;
Wei Wang1cfb71e2017-06-17 10:42:33 -07001926 dst_hold(&uncached_rt->dst);
1927 }
David Ahernb8115802015-11-19 12:24:22 -08001928
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001929 return uncached_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001930 } else {
1931 /* Get a percpu copy */
1932
1933 struct rt6_info *pcpu_rt;
1934
Eric Dumazet951f7882017-10-08 21:07:18 -07001935 local_bh_disable();
David Aherndb3fede2019-04-16 14:36:03 -07001936 pcpu_rt = rt6_get_pcpu_route(&res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001937
David Ahern93531c62018-04-17 17:33:25 -07001938 if (!pcpu_rt)
David Aherndb3fede2019-04-16 14:36:03 -07001939 pcpu_rt = rt6_make_pcpu_route(net, &res);
David Ahern93531c62018-04-17 17:33:25 -07001940
Eric Dumazet951f7882017-10-08 21:07:18 -07001941 local_bh_enable();
1942 rcu_read_unlock();
David Ahernd4bea422018-05-09 20:34:24 -07001943
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001944 return pcpu_rt;
1945 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001946}
David Ahern9ff74382016-06-13 13:44:19 -07001947EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07001948
David Ahernb75cc8f2018-03-02 08:32:17 -08001949static struct rt6_info *ip6_pol_route_input(struct net *net,
1950 struct fib6_table *table,
1951 struct flowi6 *fl6,
1952 const struct sk_buff *skb,
1953 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001954{
David Ahernb75cc8f2018-03-02 08:32:17 -08001955 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001956}
1957
Mahesh Bandeward409b842016-09-16 12:59:08 -07001958struct dst_entry *ip6_route_input_lookup(struct net *net,
1959 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08001960 struct flowi6 *fl6,
1961 const struct sk_buff *skb,
1962 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001963{
1964 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
1965 flags |= RT6_LOOKUP_F_IFACE;
1966
David Ahernb75cc8f2018-03-02 08:32:17 -08001967 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001968}
Mahesh Bandeward409b842016-09-16 12:59:08 -07001969EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001970
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001971static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001972 struct flow_keys *keys,
1973 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001974{
1975 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1976 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001977 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001978 const struct ipv6hdr *inner_iph;
1979 const struct icmp6hdr *icmph;
1980 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07001981 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001982
1983 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
1984 goto out;
1985
Eric Dumazetcea67a22018-04-29 09:54:59 -07001986 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
1987 sizeof(_icmph), &_icmph);
1988 if (!icmph)
1989 goto out;
1990
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001991 if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
1992 icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
1993 icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
1994 icmph->icmp6_type != ICMPV6_PARAMPROB)
1995 goto out;
1996
1997 inner_iph = skb_header_pointer(skb,
1998 skb_transport_offset(skb) + sizeof(*icmph),
1999 sizeof(_inner_iph), &_inner_iph);
2000 if (!inner_iph)
2001 goto out;
2002
2003 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002004 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002005out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002006 if (_flkeys) {
2007 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
2008 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
2009 keys->tags.flow_label = _flkeys->tags.flow_label;
2010 keys->basic.ip_proto = _flkeys->basic.ip_proto;
2011 } else {
2012 keys->addrs.v6addrs.src = key_iph->saddr;
2013 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002014 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002015 keys->basic.ip_proto = key_iph->nexthdr;
2016 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002017}
2018
2019/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08002020u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
2021 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002022{
2023 struct flow_keys hash_keys;
David Ahern9a2a5372018-03-02 08:32:15 -08002024 u32 mhash;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002025
David S. Millerbbfa0472018-03-12 11:09:33 -04002026 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002027 case 0:
2028 memset(&hash_keys, 0, sizeof(hash_keys));
2029 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2030 if (skb) {
2031 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2032 } else {
2033 hash_keys.addrs.v6addrs.src = fl6->saddr;
2034 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002035 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002036 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2037 }
2038 break;
2039 case 1:
2040 if (skb) {
2041 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2042 struct flow_keys keys;
2043
2044 /* short-circuit if we already have L4 hash present */
2045 if (skb->l4_hash)
2046 return skb_get_hash_raw(skb) >> 1;
2047
2048 memset(&hash_keys, 0, sizeof(hash_keys));
2049
2050 if (!flkeys) {
2051 skb_flow_dissect_flow_keys(skb, &keys, flag);
2052 flkeys = &keys;
2053 }
2054 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2055 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2056 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2057 hash_keys.ports.src = flkeys->ports.src;
2058 hash_keys.ports.dst = flkeys->ports.dst;
2059 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2060 } else {
2061 memset(&hash_keys, 0, sizeof(hash_keys));
2062 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2063 hash_keys.addrs.v6addrs.src = fl6->saddr;
2064 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2065 hash_keys.ports.src = fl6->fl6_sport;
2066 hash_keys.ports.dst = fl6->fl6_dport;
2067 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2068 }
2069 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002070 }
David Ahern9a2a5372018-03-02 08:32:15 -08002071 mhash = flow_hash_from_keys(&hash_keys);
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002072
David Ahern9a2a5372018-03-02 08:32:15 -08002073 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002074}
2075
Thomas Grafc71099a2006-08-04 23:20:06 -07002076void ip6_route_input(struct sk_buff *skb)
2077{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002078 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002079 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002080 int flags = RT6_LOOKUP_F_HAS_SADDR;
Jiri Benc904af042015-08-20 13:56:31 +02002081 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002082 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002083 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002084 .daddr = iph->daddr,
2085 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002086 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002087 .flowi6_mark = skb->mark,
2088 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002089 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002090 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002091
Jiri Benc904af042015-08-20 13:56:31 +02002092 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002093 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002094 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002095
2096 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2097 flkeys = &_flkeys;
2098
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002099 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002100 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002101 skb_dst_drop(skb);
David Ahernb75cc8f2018-03-02 08:32:17 -08002102 skb_dst_set(skb,
2103 ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002104}
2105
David Ahernb75cc8f2018-03-02 08:32:17 -08002106static struct rt6_info *ip6_pol_route_output(struct net *net,
2107 struct fib6_table *table,
2108 struct flowi6 *fl6,
2109 const struct sk_buff *skb,
2110 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002111{
David Ahernb75cc8f2018-03-02 08:32:17 -08002112 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002113}
2114
Paolo Abeni6f21c962016-01-29 12:30:19 +01002115struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
2116 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002117{
David Ahernd46a9d62015-10-21 08:42:22 -07002118 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002119
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002120 if (ipv6_addr_type(&fl6->daddr) &
2121 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002122 struct dst_entry *dst;
2123
2124 dst = l3mdev_link_scope_lookup(net, fl6);
2125 if (dst)
2126 return dst;
2127 }
David Ahernca254492015-10-12 11:47:10 -07002128
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002129 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002130
David Ahernd46a9d62015-10-21 08:42:22 -07002131 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002132 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002133 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002134 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002135
David Ahernd46a9d62015-10-21 08:42:22 -07002136 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002137 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002138 else if (sk)
2139 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002140
David Ahernb75cc8f2018-03-02 08:32:17 -08002141 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002143EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144
David S. Miller2774c132011-03-01 14:59:04 -08002145struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002146{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002147 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002148 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002149 struct dst_entry *new = NULL;
2150
Wei Wang1dbe32522017-06-17 10:42:26 -07002151 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002152 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002153 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002154 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002155 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002156
Changli Gaod8d1f302010-06-10 23:31:35 -07002157 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002158 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002159 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002160 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002161
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002162 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002163
Wei Wang1dbe32522017-06-17 10:42:26 -07002164 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002165 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002166 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002167
2168 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2169#ifdef CONFIG_IPV6_SUBTREES
2170 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2171#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002172 }
2173
David S. Miller69ead7a2011-03-01 14:45:33 -08002174 dst_release(dst_orig);
2175 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002176}
David S. Miller14e50e52007-05-24 18:17:54 -07002177
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178/*
2179 * Destination cache support functions
2180 */
2181
David Ahern8d1c8022018-04-17 17:33:26 -07002182static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002183{
Steffen Klassert36143642017-08-25 09:05:42 +02002184 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002185
David Ahern8ae86972018-04-20 15:38:03 -07002186 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002187 return false;
2188
2189 if (fib6_check_expired(f6i))
2190 return false;
2191
2192 return true;
2193}
2194
David Aherna68886a2018-04-20 15:38:02 -07002195static struct dst_entry *rt6_check(struct rt6_info *rt,
2196 struct fib6_info *from,
2197 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002198{
Wei Wangc5cff852017-08-21 09:47:10 -07002199 u32 rt_cookie = 0;
2200
David Aherna68886a2018-04-20 15:38:02 -07002201 if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
David Ahern93531c62018-04-17 17:33:25 -07002202 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002203 return NULL;
2204
2205 if (rt6_check_expired(rt))
2206 return NULL;
2207
2208 return &rt->dst;
2209}
2210
David Aherna68886a2018-04-20 15:38:02 -07002211static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2212 struct fib6_info *from,
2213 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002214{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002215 if (!__rt6_check_expired(rt) &&
2216 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002217 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002218 return &rt->dst;
2219 else
2220 return NULL;
2221}
2222
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
2224{
David Aherna87b7dc2018-04-20 15:38:00 -07002225 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002226 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 struct rt6_info *rt;
2228
David Aherna87b7dc2018-04-20 15:38:00 -07002229 rt = container_of(dst, struct rt6_info, dst);
2230
2231 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002233 /* All IPV6 dsts are created with ->obsolete set to the value
2234 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2235 * into this function always.
2236 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002237
David Aherna68886a2018-04-20 15:38:02 -07002238 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002239
David Aherna68886a2018-04-20 15:38:02 -07002240 if (from && (rt->rt6i_flags & RTF_PCPU ||
2241 unlikely(!list_empty(&rt->rt6i_uncached))))
2242 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002243 else
David Aherna68886a2018-04-20 15:38:02 -07002244 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002245
2246 rcu_read_unlock();
2247
2248 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249}
2250
2251static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2252{
2253 struct rt6_info *rt = (struct rt6_info *) dst;
2254
2255 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002256 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002257 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002258 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002259 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002260 dst = NULL;
2261 }
David Ahernc3c14da2018-04-23 11:32:06 -07002262 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002263 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002265 dst = NULL;
2266 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002268 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269}
2270
2271static void ip6_link_failure(struct sk_buff *skb)
2272{
2273 struct rt6_info *rt;
2274
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002275 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276
Eric Dumazetadf30902009-06-02 05:19:30 +00002277 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002279 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002280 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002281 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002282 } else {
David Aherna68886a2018-04-20 15:38:02 -07002283 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002284 struct fib6_node *fn;
2285
David Aherna68886a2018-04-20 15:38:02 -07002286 from = rcu_dereference(rt->from);
2287 if (from) {
2288 fn = rcu_dereference(from->fib6_node);
2289 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2290 fn->fn_sernum = -1;
2291 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002292 }
David Ahern8a14e462018-04-23 11:32:07 -07002293 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 }
2295}
2296
David Ahern6a3e0302018-04-20 15:37:57 -07002297static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2298{
David Aherna68886a2018-04-20 15:38:02 -07002299 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2300 struct fib6_info *from;
2301
2302 rcu_read_lock();
2303 from = rcu_dereference(rt0->from);
2304 if (from)
2305 rt0->dst.expires = from->expires;
2306 rcu_read_unlock();
2307 }
David Ahern6a3e0302018-04-20 15:37:57 -07002308
2309 dst_set_expires(&rt0->dst, timeout);
2310 rt0->rt6i_flags |= RTF_EXPIRES;
2311}
2312
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002313static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2314{
2315 struct net *net = dev_net(rt->dst.dev);
2316
David Ahernd4ead6b2018-04-17 17:33:16 -07002317 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002318 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002319 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2320}
2321
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002322static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2323{
2324 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002325 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002326}
2327
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002328static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2329 const struct ipv6hdr *iph, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002331 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002332 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333
Xin Long19bda362016-10-28 18:18:01 +08002334 if (dst_metric_locked(dst, RTAX_MTU))
2335 return;
2336
Julian Anastasov0dec8792017-02-06 23:14:16 +02002337 if (iph) {
2338 daddr = &iph->daddr;
2339 saddr = &iph->saddr;
2340 } else if (sk) {
2341 daddr = &sk->sk_v6_daddr;
2342 saddr = &inet6_sk(sk)->saddr;
2343 } else {
2344 daddr = NULL;
2345 saddr = NULL;
2346 }
2347 dst_confirm_neigh(dst, daddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002348 mtu = max_t(u32, mtu, IPV6_MIN_MTU);
2349 if (mtu >= dst_mtu(dst))
2350 return;
David S. Miller81aded22012-06-15 14:54:11 -07002351
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002352 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002353 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002354 /* update rt6_ex->stamp for cache */
2355 if (rt6->rt6i_flags & RTF_CACHE)
2356 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002357 } else if (daddr) {
David Ahern85bd05d2019-04-16 14:36:01 -07002358 struct fib6_result res = {};
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002359 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002360
David Ahern4d85cd02018-04-20 15:37:59 -07002361 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07002362 res.f6i = rcu_dereference(rt6->from);
2363 if (!res.f6i) {
Jonathan Lemon9c69a132019-04-14 14:21:29 -07002364 rcu_read_unlock();
2365 return;
2366 }
David Ahern85bd05d2019-04-16 14:36:01 -07002367 res.nh = &res.f6i->fib6_nh;
2368 nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002369 if (nrt6) {
2370 rt6_do_update_pmtu(nrt6, mtu);
David Ahern5012f0a2019-04-16 14:36:05 -07002371 if (rt6_insert_exception(nrt6, &res))
Wei Wang2b760fc2017-10-06 12:06:03 -07002372 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002373 }
David Aherna68886a2018-04-20 15:38:02 -07002374 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375 }
2376}
2377
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002378static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2379 struct sk_buff *skb, u32 mtu)
2380{
2381 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
2382}
2383
David S. Miller42ae66c2012-06-15 20:01:57 -07002384void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002385 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002386{
2387 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2388 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002389 struct flowi6 fl6 = {
2390 .flowi6_oif = oif,
2391 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2392 .daddr = iph->daddr,
2393 .saddr = iph->saddr,
2394 .flowlabel = ip6_flowinfo(iph),
2395 .flowi6_uid = uid,
2396 };
David S. Miller81aded22012-06-15 14:54:11 -07002397
2398 dst = ip6_route_output(net, NULL, &fl6);
2399 if (!dst->error)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002400 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07002401 dst_release(dst);
2402}
2403EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2404
2405void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2406{
David Ahern7ddacfa2018-11-18 10:45:30 -08002407 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002408 struct dst_entry *dst;
2409
David Ahern7ddacfa2018-11-18 10:45:30 -08002410 if (!oif && skb->dev)
2411 oif = l3mdev_master_ifindex(skb->dev);
2412
2413 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002414
2415 dst = __sk_dst_get(sk);
2416 if (!dst || !dst->obsolete ||
2417 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2418 return;
2419
2420 bh_lock_sock(sk);
2421 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2422 ip6_datagram_dst_update(sk, false);
2423 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002424}
2425EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2426
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002427void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2428 const struct flowi6 *fl6)
2429{
2430#ifdef CONFIG_IPV6_SUBTREES
2431 struct ipv6_pinfo *np = inet6_sk(sk);
2432#endif
2433
2434 ip6_dst_store(sk, dst,
2435 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2436 &sk->sk_v6_daddr : NULL,
2437#ifdef CONFIG_IPV6_SUBTREES
2438 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2439 &np->saddr :
2440#endif
2441 NULL);
2442}
2443
David Ahern9b6b35a2019-04-16 14:36:02 -07002444static bool ip6_redirect_nh_match(const struct fib6_result *res,
David Ahern0b34eb02019-04-09 14:41:19 -07002445 struct flowi6 *fl6,
2446 const struct in6_addr *gw,
2447 struct rt6_info **ret)
2448{
David Ahern9b6b35a2019-04-16 14:36:02 -07002449 const struct fib6_nh *nh = res->nh;
2450
David Ahern0b34eb02019-04-09 14:41:19 -07002451 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2452 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2453 return false;
2454
2455 /* rt_cache's gateway might be different from its 'parent'
2456 * in the case of an ip redirect.
2457 * So we keep searching in the exception table if the gateway
2458 * is different.
2459 */
2460 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
2461 struct rt6_info *rt_cache;
2462
David Ahern9b6b35a2019-04-16 14:36:02 -07002463 rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
David Ahern0b34eb02019-04-09 14:41:19 -07002464 if (rt_cache &&
2465 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2466 *ret = rt_cache;
2467 return true;
2468 }
2469 return false;
2470 }
2471 return true;
2472}
2473
Duan Jiongb55b76b2013-09-04 19:44:21 +08002474/* Handle redirects */
2475struct ip6rd_flowi {
2476 struct flowi6 fl6;
2477 struct in6_addr gateway;
2478};
2479
2480static struct rt6_info *__ip6_route_redirect(struct net *net,
2481 struct fib6_table *table,
2482 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002483 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002484 int flags)
2485{
2486 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002487 struct rt6_info *ret = NULL;
David Ahern9b6b35a2019-04-16 14:36:02 -07002488 struct fib6_result res = {};
David Ahern8d1c8022018-04-17 17:33:26 -07002489 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002490 struct fib6_node *fn;
2491
2492 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002493 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002494 *
2495 * RFC 4861 specifies that redirects should only be
2496 * accepted if they come from the nexthop to the target.
2497 * Due to the way the routes are chosen, this notion
2498 * is a bit fuzzy and one might need to check all possible
2499 * routes.
2500 */
2501
Wei Wang66f5d6c2017-10-06 12:06:10 -07002502 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002503 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002504restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002505 for_each_fib6_node_rt_rcu(fn) {
David Ahern9b6b35a2019-04-16 14:36:02 -07002506 res.f6i = rt;
2507 res.nh = &rt->fib6_nh;
2508
David Ahern14895682018-04-17 17:33:17 -07002509 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002510 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002511 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002512 break;
David Ahern9b6b35a2019-04-16 14:36:02 -07002513 if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, &ret))
David Ahern0b34eb02019-04-09 14:41:19 -07002514 goto out;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002515 }
2516
2517 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002518 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002519 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002520 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002521 goto out;
2522 }
2523
David Ahern421842e2018-04-17 17:33:18 -07002524 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002525 fn = fib6_backtrack(fn, &fl6->saddr);
2526 if (fn)
2527 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002528 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002529
David Ahern9b6b35a2019-04-16 14:36:02 -07002530 res.f6i = rt;
2531 res.nh = &rt->fib6_nh;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002532out:
David Ahern23fb93a2018-04-17 17:33:23 -07002533 if (ret)
David Ahern10585b42019-03-20 09:24:50 -07002534 ip6_hold_safe(net, &ret);
David Ahern23fb93a2018-04-17 17:33:23 -07002535 else
David Ahern9b6b35a2019-04-16 14:36:02 -07002536 ret = ip6_create_rt_rcu(&res);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002537
Wei Wang66f5d6c2017-10-06 12:06:10 -07002538 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002539
David Ahern8ff2e5b2019-04-16 14:36:09 -07002540 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002541 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002542};
2543
2544static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002545 const struct flowi6 *fl6,
2546 const struct sk_buff *skb,
2547 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002548{
2549 int flags = RT6_LOOKUP_F_HAS_SADDR;
2550 struct ip6rd_flowi rdfl;
2551
2552 rdfl.fl6 = *fl6;
2553 rdfl.gateway = *gateway;
2554
David Ahernb75cc8f2018-03-02 08:32:17 -08002555 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002556 flags, __ip6_route_redirect);
2557}
2558
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002559void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
2560 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002561{
2562 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2563 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07002564 struct flowi6 fl6 = {
2565 .flowi6_iif = LOOPBACK_IFINDEX,
2566 .flowi6_oif = oif,
2567 .flowi6_mark = mark,
2568 .daddr = iph->daddr,
2569 .saddr = iph->saddr,
2570 .flowlabel = ip6_flowinfo(iph),
2571 .flowi6_uid = uid,
2572 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002573
David Ahernb75cc8f2018-03-02 08:32:17 -08002574 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002575 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002576 dst_release(dst);
2577}
2578EXPORT_SYMBOL_GPL(ip6_redirect);
2579
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07002580void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08002581{
2582 const struct ipv6hdr *iph = ipv6_hdr(skb);
2583 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
2584 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002585 struct flowi6 fl6 = {
2586 .flowi6_iif = LOOPBACK_IFINDEX,
2587 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002588 .daddr = msg->dest,
2589 .saddr = iph->daddr,
2590 .flowi6_uid = sock_net_uid(net, NULL),
2591 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08002592
David Ahernb75cc8f2018-03-02 08:32:17 -08002593 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002594 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08002595 dst_release(dst);
2596}
2597
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002598void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
2599{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002600 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
2601 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002602}
2603EXPORT_SYMBOL_GPL(ip6_sk_redirect);
2604
David S. Miller0dbaee32010-12-13 12:52:14 -08002605static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606{
David S. Miller0dbaee32010-12-13 12:52:14 -08002607 struct net_device *dev = dst->dev;
2608 unsigned int mtu = dst_mtu(dst);
2609 struct net *net = dev_net(dev);
2610
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
2612
Daniel Lezcano55786892008-03-04 13:47:47 -08002613 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
2614 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615
2616 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002617 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
2618 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
2619 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620 * rely only on pmtu discovery"
2621 */
2622 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
2623 mtu = IPV6_MAXPLEN;
2624 return mtu;
2625}
2626
Steffen Klassertebb762f2011-11-23 02:12:51 +00002627static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08002628{
David S. Millerd33e4552010-12-14 13:01:14 -08002629 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07002630 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002631
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002632 mtu = dst_metric_raw(dst, RTAX_MTU);
2633 if (mtu)
2634 goto out;
2635
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002636 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08002637
2638 rcu_read_lock();
2639 idev = __in6_dev_get(dst->dev);
2640 if (idev)
2641 mtu = idev->cnf.mtu6;
2642 rcu_read_unlock();
2643
Eric Dumazet30f78d82014-04-10 21:23:36 -07002644out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07002645 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2646
2647 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08002648}
2649
David Ahern901731b2018-05-21 09:08:14 -07002650/* MTU selection:
2651 * 1. mtu on route is locked - use it
2652 * 2. mtu from nexthop exception
2653 * 3. mtu from egress device
2654 *
2655 * based on ip6_dst_mtu_forward and exception logic of
2656 * rt6_find_cached_rt; called with rcu_read_lock
2657 */
David Ahernb748f262019-04-16 14:36:06 -07002658u32 ip6_mtu_from_fib6(const struct fib6_result *res,
2659 const struct in6_addr *daddr,
2660 const struct in6_addr *saddr)
David Ahern901731b2018-05-21 09:08:14 -07002661{
2662 struct rt6_exception_bucket *bucket;
David Ahernb748f262019-04-16 14:36:06 -07002663 const struct fib6_nh *nh = res->nh;
2664 struct fib6_info *f6i = res->f6i;
2665 const struct in6_addr *src_key;
David Ahern901731b2018-05-21 09:08:14 -07002666 struct rt6_exception *rt6_ex;
David Ahern901731b2018-05-21 09:08:14 -07002667 struct inet6_dev *idev;
2668 u32 mtu = 0;
2669
2670 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
2671 mtu = f6i->fib6_pmtu;
2672 if (mtu)
2673 goto out;
2674 }
2675
2676 src_key = NULL;
2677#ifdef CONFIG_IPV6_SUBTREES
2678 if (f6i->fib6_src.plen)
2679 src_key = saddr;
2680#endif
2681
2682 bucket = rcu_dereference(f6i->rt6i_exception_bucket);
2683 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
2684 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
2685 mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
2686
2687 if (likely(!mtu)) {
David Ahernb748f262019-04-16 14:36:06 -07002688 struct net_device *dev = nh->fib_nh_dev;
David Ahern901731b2018-05-21 09:08:14 -07002689
2690 mtu = IPV6_MIN_MTU;
2691 idev = __in6_dev_get(dev);
2692 if (idev && idev->cnf.mtu6 > mtu)
2693 mtu = idev->cnf.mtu6;
2694 }
2695
2696 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2697out:
David Ahernb748f262019-04-16 14:36:06 -07002698 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahern901731b2018-05-21 09:08:14 -07002699}
2700
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08002701struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05002702 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703{
David S. Miller87a11572011-12-06 17:04:13 -05002704 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 struct rt6_info *rt;
2706 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002707 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708
David S. Miller38308472011-12-03 18:02:47 -05002709 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00002710 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711
Martin KaFai Lauad706862015-08-14 11:05:52 -07002712 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05002713 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05002715 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716 goto out;
2717 }
2718
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002719 rt->dst.flags |= DST_HOST;
Brendan McGrath588753f2017-12-13 22:14:57 +11002720 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002721 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03002722 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05002723 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002724 rt->rt6i_dst.plen = 128;
2725 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08002726 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727
Ido Schimmel4c981e22018-01-07 12:45:04 +02002728 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07002729 * do proper release of the net_device
2730 */
2731 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002732 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
David S. Miller87a11572011-12-06 17:04:13 -05002734 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
2735
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736out:
David S. Miller87a11572011-12-06 17:04:13 -05002737 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738}
2739
Daniel Lezcano569d3642008-01-18 03:56:57 -08002740static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002742 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08002743 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
2744 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
2745 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
2746 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
2747 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002748 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749
Eric Dumazetfc66f952010-10-08 06:37:34 +00002750 entries = dst_entries_get_fast(ops);
Michal Kubeček49a18d82013-08-01 10:04:24 +02002751 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00002752 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 goto out;
2754
Benjamin Thery6891a342008-03-04 13:49:47 -08002755 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08002756 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002757 entries = dst_entries_get_slow(ops);
2758 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08002759 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08002761 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002762 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763}
2764
David Ahern8c145862016-04-24 21:26:04 -07002765static struct rt6_info *ip6_nh_lookup_table(struct net *net,
2766 struct fib6_config *cfg,
David Ahernf4797b32018-01-25 16:55:08 -08002767 const struct in6_addr *gw_addr,
2768 u32 tbid, int flags)
David Ahern8c145862016-04-24 21:26:04 -07002769{
2770 struct flowi6 fl6 = {
2771 .flowi6_oif = cfg->fc_ifindex,
2772 .daddr = *gw_addr,
2773 .saddr = cfg->fc_prefsrc,
2774 };
2775 struct fib6_table *table;
2776 struct rt6_info *rt;
David Ahern8c145862016-04-24 21:26:04 -07002777
David Ahernf4797b32018-01-25 16:55:08 -08002778 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07002779 if (!table)
2780 return NULL;
2781
2782 if (!ipv6_addr_any(&cfg->fc_prefsrc))
2783 flags |= RT6_LOOKUP_F_HAS_SADDR;
2784
David Ahernf4797b32018-01-25 16:55:08 -08002785 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahernb75cc8f2018-03-02 08:32:17 -08002786 rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07002787
2788 /* if table lookup failed, fall back to full lookup */
2789 if (rt == net->ipv6.ip6_null_entry) {
2790 ip6_rt_put(rt);
2791 rt = NULL;
2792 }
2793
2794 return rt;
2795}
2796
David Ahernfc1e64e2018-01-25 16:55:09 -08002797static int ip6_route_check_nh_onlink(struct net *net,
2798 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07002799 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08002800 struct netlink_ext_ack *extack)
2801{
David Ahern44750f82018-02-06 13:17:06 -08002802 u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08002803 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2804 u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002805 struct fib6_info *from;
David Ahernfc1e64e2018-01-25 16:55:09 -08002806 struct rt6_info *grt;
2807 int err;
2808
2809 err = 0;
2810 grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
2811 if (grt) {
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002812 rcu_read_lock();
2813 from = rcu_dereference(grt->from);
David Ahern58e354c2018-02-06 12:14:12 -08002814 if (!grt->dst.error &&
David Ahern4ed591c2018-10-24 13:58:39 -07002815 /* ignore match if it is the default route */
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002816 from && !ipv6_addr_any(&from->fib6_dst.addr) &&
David Ahern58e354c2018-02-06 12:14:12 -08002817 (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
David Ahern44750f82018-02-06 13:17:06 -08002818 NL_SET_ERR_MSG(extack,
2819 "Nexthop has invalid gateway or device mismatch");
David Ahernfc1e64e2018-01-25 16:55:09 -08002820 err = -EINVAL;
2821 }
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002822 rcu_read_unlock();
David Ahernfc1e64e2018-01-25 16:55:09 -08002823
2824 ip6_rt_put(grt);
2825 }
2826
2827 return err;
2828}
2829
David Ahern1edce992018-01-25 16:55:07 -08002830static int ip6_route_check_nh(struct net *net,
2831 struct fib6_config *cfg,
2832 struct net_device **_dev,
2833 struct inet6_dev **idev)
2834{
2835 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2836 struct net_device *dev = _dev ? *_dev : NULL;
2837 struct rt6_info *grt = NULL;
2838 int err = -EHOSTUNREACH;
2839
2840 if (cfg->fc_table) {
David Ahernf4797b32018-01-25 16:55:08 -08002841 int flags = RT6_LOOKUP_F_IFACE;
2842
2843 grt = ip6_nh_lookup_table(net, cfg, gw_addr,
2844 cfg->fc_table, flags);
David Ahern1edce992018-01-25 16:55:07 -08002845 if (grt) {
2846 if (grt->rt6i_flags & RTF_GATEWAY ||
2847 (dev && dev != grt->dst.dev)) {
2848 ip6_rt_put(grt);
2849 grt = NULL;
2850 }
2851 }
2852 }
2853
2854 if (!grt)
David Ahernb75cc8f2018-03-02 08:32:17 -08002855 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1);
David Ahern1edce992018-01-25 16:55:07 -08002856
2857 if (!grt)
2858 goto out;
2859
2860 if (dev) {
2861 if (dev != grt->dst.dev) {
2862 ip6_rt_put(grt);
2863 goto out;
2864 }
2865 } else {
2866 *_dev = dev = grt->dst.dev;
2867 *idev = grt->rt6i_idev;
2868 dev_hold(dev);
2869 in6_dev_hold(grt->rt6i_idev);
2870 }
2871
2872 if (!(grt->rt6i_flags & RTF_GATEWAY))
2873 err = 0;
2874
2875 ip6_rt_put(grt);
2876
2877out:
2878 return err;
2879}
2880
David Ahern9fbb7042018-03-13 08:29:36 -07002881static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
2882 struct net_device **_dev, struct inet6_dev **idev,
2883 struct netlink_ext_ack *extack)
2884{
2885 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2886 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07002887 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07002888 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07002889 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07002890 int err = -EINVAL;
2891
2892 /* if gw_addr is local we will fail to detect this in case
2893 * address is still TENTATIVE (DAD in progress). rt6_lookup()
2894 * will return already-added prefix route via interface that
2895 * prefix route was assigned to, which might be non-loopback.
2896 */
David Ahern232378e2018-03-13 08:29:37 -07002897 if (dev &&
2898 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2899 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07002900 goto out;
2901 }
2902
2903 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
2904 /* IPv6 strictly inhibits using not link-local
2905 * addresses as nexthop address.
2906 * Otherwise, router will not able to send redirects.
2907 * It is very good, but in some (rare!) circumstances
2908 * (SIT, PtP, NBMA NOARP links) it is handy to allow
2909 * some exceptions. --ANK
2910 * We allow IPv4-mapped nexthops to support RFC4798-type
2911 * addressing
2912 */
2913 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
2914 NL_SET_ERR_MSG(extack, "Invalid gateway address");
2915 goto out;
2916 }
2917
2918 if (cfg->fc_flags & RTNH_F_ONLINK)
2919 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
2920 else
2921 err = ip6_route_check_nh(net, cfg, _dev, idev);
2922
2923 if (err)
2924 goto out;
2925 }
2926
2927 /* reload in case device was changed */
2928 dev = *_dev;
2929
2930 err = -EINVAL;
2931 if (!dev) {
2932 NL_SET_ERR_MSG(extack, "Egress device not specified");
2933 goto out;
2934 } else if (dev->flags & IFF_LOOPBACK) {
2935 NL_SET_ERR_MSG(extack,
2936 "Egress device can not be loopback device for this route");
2937 goto out;
2938 }
David Ahern232378e2018-03-13 08:29:37 -07002939
2940 /* if we did not check gw_addr above, do so now that the
2941 * egress device has been resolved.
2942 */
2943 if (need_addr_check &&
2944 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2945 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2946 goto out;
2947 }
2948
David Ahern9fbb7042018-03-13 08:29:36 -07002949 err = 0;
2950out:
2951 return err;
2952}
2953
David Ahern83c442512019-03-27 20:53:50 -07002954static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
2955{
2956 if ((flags & RTF_REJECT) ||
2957 (dev && (dev->flags & IFF_LOOPBACK) &&
2958 !(addr_type & IPV6_ADDR_LOOPBACK) &&
2959 !(flags & RTF_LOCAL)))
2960 return true;
2961
2962 return false;
2963}
2964
2965int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
2966 struct fib6_config *cfg, gfp_t gfp_flags,
2967 struct netlink_ext_ack *extack)
2968{
2969 struct net_device *dev = NULL;
2970 struct inet6_dev *idev = NULL;
2971 int addr_type;
2972 int err;
2973
David Ahernf1741732019-03-27 20:53:57 -07002974 fib6_nh->fib_nh_family = AF_INET6;
2975
David Ahern83c442512019-03-27 20:53:50 -07002976 err = -ENODEV;
2977 if (cfg->fc_ifindex) {
2978 dev = dev_get_by_index(net, cfg->fc_ifindex);
2979 if (!dev)
2980 goto out;
2981 idev = in6_dev_get(dev);
2982 if (!idev)
2983 goto out;
2984 }
2985
2986 if (cfg->fc_flags & RTNH_F_ONLINK) {
2987 if (!dev) {
2988 NL_SET_ERR_MSG(extack,
2989 "Nexthop device required for onlink");
2990 goto out;
2991 }
2992
2993 if (!(dev->flags & IFF_UP)) {
2994 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
2995 err = -ENETDOWN;
2996 goto out;
2997 }
2998
David Ahernad1601a2019-03-27 20:53:56 -07002999 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07003000 }
3001
David Ahernad1601a2019-03-27 20:53:56 -07003002 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07003003
3004 /* We cannot add true routes via loopback here,
3005 * they would result in kernel looping; promote them to reject routes
3006 */
3007 addr_type = ipv6_addr_type(&cfg->fc_dst);
3008 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
3009 /* hold loopback dev/idev if we haven't done so. */
3010 if (dev != net->loopback_dev) {
3011 if (dev) {
3012 dev_put(dev);
3013 in6_dev_put(idev);
3014 }
3015 dev = net->loopback_dev;
3016 dev_hold(dev);
3017 idev = in6_dev_get(dev);
3018 if (!idev) {
3019 err = -ENODEV;
3020 goto out;
3021 }
3022 }
3023 goto set_dev;
3024 }
3025
3026 if (cfg->fc_flags & RTF_GATEWAY) {
3027 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
3028 if (err)
3029 goto out;
3030
David Ahernad1601a2019-03-27 20:53:56 -07003031 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07003032 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07003033 }
3034
3035 err = -ENODEV;
3036 if (!dev)
3037 goto out;
3038
3039 if (idev->cnf.disable_ipv6) {
3040 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3041 err = -EACCES;
3042 goto out;
3043 }
3044
3045 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3046 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3047 err = -ENETDOWN;
3048 goto out;
3049 }
3050
3051 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3052 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003053 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003054
David Ahern979e2762019-03-27 20:53:58 -07003055 err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
3056 cfg->fc_encap_type, cfg, gfp_flags, extack);
3057 if (err)
3058 goto out;
David Ahern83c442512019-03-27 20:53:50 -07003059set_dev:
David Ahernad1601a2019-03-27 20:53:56 -07003060 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003061 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003062 err = 0;
3063out:
3064 if (idev)
3065 in6_dev_put(idev);
3066
3067 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003068 lwtstate_put(fib6_nh->fib_nh_lws);
3069 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003070 if (dev)
3071 dev_put(dev);
3072 }
3073
3074 return err;
3075}
3076
David Aherndac7d0f2019-03-27 20:53:51 -07003077void fib6_nh_release(struct fib6_nh *fib6_nh)
3078{
David Ahern979e2762019-03-27 20:53:58 -07003079 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003080}
3081
David Ahern8d1c8022018-04-17 17:33:26 -07003082static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003083 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003084 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003085{
Daniel Lezcano55786892008-03-04 13:47:47 -08003086 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003087 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003088 struct fib6_table *table;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003089 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003090 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091
David Ahern557c44b2017-04-19 14:19:43 -07003092 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003093 if (cfg->fc_flags & RTF_PCPU) {
3094 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003095 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003096 }
David Ahern557c44b2017-04-19 14:19:43 -07003097
Wei Wang2ea23522017-10-27 17:30:12 -07003098 /* RTF_CACHE is an internal flag; can not be set by userspace */
3099 if (cfg->fc_flags & RTF_CACHE) {
3100 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3101 goto out;
3102 }
3103
David Aherne8478e82018-04-17 17:33:13 -07003104 if (cfg->fc_type > RTN_MAX) {
3105 NL_SET_ERR_MSG(extack, "Invalid route type");
3106 goto out;
3107 }
3108
David Ahernd5d531c2017-05-21 10:12:05 -06003109 if (cfg->fc_dst_len > 128) {
3110 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003111 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003112 }
3113 if (cfg->fc_src_len > 128) {
3114 NL_SET_ERR_MSG(extack, "Invalid source address length");
3115 goto out;
3116 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003118 if (cfg->fc_src_len) {
3119 NL_SET_ERR_MSG(extack,
3120 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003121 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003123#endif
David Ahernfc1e64e2018-01-25 16:55:09 -08003124
Matti Vaittinend71314b2011-11-14 00:14:49 +00003125 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003126 if (cfg->fc_nlinfo.nlh &&
3127 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003128 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003129 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003130 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003131 table = fib6_new_table(net, cfg->fc_table);
3132 }
3133 } else {
3134 table = fib6_new_table(net, cfg->fc_table);
3135 }
David S. Miller38308472011-12-03 18:02:47 -05003136
3137 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003138 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003139
David Ahern93531c62018-04-17 17:33:25 -07003140 err = -ENOMEM;
3141 rt = fib6_info_alloc(gfp_flags);
3142 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003143 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003144
David Ahernd7e774f2018-11-06 12:51:15 -08003145 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3146 extack);
David Ahern767a2212018-10-04 20:07:51 -07003147 if (IS_ERR(rt->fib6_metrics)) {
3148 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003149 /* Do not leave garbage there. */
3150 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003151 goto out;
3152 }
3153
David Ahern93531c62018-04-17 17:33:25 -07003154 if (cfg->fc_flags & RTF_ADDRCONF)
3155 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003156
Gao feng1716a962012-04-06 00:13:10 +00003157 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003158 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003159 clock_t_to_jiffies(cfg->fc_expires));
3160 else
David Ahern14895682018-04-17 17:33:17 -07003161 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003162
Thomas Graf86872cb2006-08-22 00:01:08 -07003163 if (cfg->fc_protocol == RTPROT_UNSPEC)
3164 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003165 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003166
David Ahern83c442512019-03-27 20:53:50 -07003167 rt->fib6_table = table;
3168 rt->fib6_metric = cfg->fc_metric;
3169 rt->fib6_type = cfg->fc_type;
David Ahern2b2450c2019-03-27 20:53:52 -07003170 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003171
David Ahern93c2fb22018-04-18 15:38:59 -07003172 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3173 rt->fib6_dst.plen = cfg->fc_dst_len;
3174 if (rt->fib6_dst.plen == 128)
David Ahern3b6761d2018-04-17 17:33:20 -07003175 rt->dst_host = true;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003176
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003178 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3179 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003180#endif
David Ahern83c442512019-03-27 20:53:50 -07003181 err = fib6_nh_init(net, &rt->fib6_nh, cfg, gfp_flags, extack);
3182 if (err)
3183 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003184
3185 /* We cannot add true routes via loopback here,
David Ahern83c442512019-03-27 20:53:50 -07003186 * they would result in kernel looping; promote them to reject routes
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187 */
David Ahern83c442512019-03-27 20:53:50 -07003188 addr_type = ipv6_addr_type(&cfg->fc_dst);
David Ahernad1601a2019-03-27 20:53:56 -07003189 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh.fib_nh_dev, addr_type))
David Ahern83c442512019-03-27 20:53:50 -07003190 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
David Ahern955ec4c2018-01-24 19:45:29 -08003191
Daniel Walterc3968a82011-04-13 21:10:57 +00003192 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahern83c442512019-03-27 20:53:50 -07003193 struct net_device *dev = fib6_info_nh_dev(rt);
3194
Daniel Walterc3968a82011-04-13 21:10:57 +00003195 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003196 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003197 err = -EINVAL;
3198 goto out;
3199 }
David Ahern93c2fb22018-04-18 15:38:59 -07003200 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3201 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003202 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003203 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003204
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003205 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206out:
David Ahern93531c62018-04-17 17:33:25 -07003207 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003208 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003209}
3210
David Ahernacb54e32018-04-17 17:33:22 -07003211int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003212 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003213{
David Ahern8d1c8022018-04-17 17:33:26 -07003214 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003215 int err;
3216
David Ahernacb54e32018-04-17 17:33:22 -07003217 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003218 if (IS_ERR(rt))
3219 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003220
David Ahernd4ead6b2018-04-17 17:33:16 -07003221 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003222 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003223
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224 return err;
3225}
3226
David Ahern8d1c8022018-04-17 17:33:26 -07003227static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228{
David Ahernafb1d4b52018-04-17 17:33:11 -07003229 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003230 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003231 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003232
David Ahern421842e2018-04-17 17:33:18 -07003233 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003234 err = -ENOENT;
3235 goto out;
3236 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003237
David Ahern93c2fb22018-04-18 15:38:59 -07003238 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003239 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003240 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003241 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003242
Gao feng6825a262012-09-19 19:25:34 +00003243out:
David Ahern93531c62018-04-17 17:33:25 -07003244 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003245 return err;
3246}
3247
David Ahern8d1c8022018-04-17 17:33:26 -07003248int ip6_del_rt(struct net *net, struct fib6_info *rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003249{
David Ahernafb1d4b52018-04-17 17:33:11 -07003250 struct nl_info info = { .nl_net = net };
3251
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003252 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003253}
3254
David Ahern8d1c8022018-04-17 17:33:26 -07003255static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003256{
3257 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003258 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003259 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003260 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003261 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003262
David Ahern421842e2018-04-17 17:33:18 -07003263 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003264 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003265 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003266 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003267
David Ahern93c2fb22018-04-18 15:38:59 -07003268 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003269 struct fib6_info *sibling, *next_sibling;
David Ahern0ae81332017-02-02 12:37:08 -08003270
David Ahern16a16cd2017-02-02 12:37:11 -08003271 /* prefer to send a single notification with all hops */
3272 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3273 if (skb) {
3274 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3275
David Ahernd4ead6b2018-04-17 17:33:16 -07003276 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003277 NULL, NULL, 0, RTM_DELROUTE,
3278 info->portid, seq, 0) < 0) {
3279 kfree_skb(skb);
3280 skb = NULL;
3281 } else
3282 info->skip_notify = 1;
3283 }
3284
David Ahern0ae81332017-02-02 12:37:08 -08003285 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003286 &rt->fib6_siblings,
3287 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003288 err = fib6_del(sibling, info);
3289 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003290 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003291 }
3292 }
3293
3294 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003295out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003296 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003297out_put:
David Ahern93531c62018-04-17 17:33:25 -07003298 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003299
3300 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003301 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003302 info->nlh, gfp_any());
3303 }
David Ahern0ae81332017-02-02 12:37:08 -08003304 return err;
3305}
3306
David Ahern23fb93a2018-04-17 17:33:23 -07003307static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
3308{
3309 int rc = -ESRCH;
3310
3311 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3312 goto out;
3313
3314 if (cfg->fc_flags & RTF_GATEWAY &&
3315 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3316 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003317
3318 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003319out:
3320 return rc;
3321}
3322
David Ahern333c4302017-05-21 10:12:04 -06003323static int ip6_route_del(struct fib6_config *cfg,
3324 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325{
David Ahern8d1c8022018-04-17 17:33:26 -07003326 struct rt6_info *rt_cache;
Thomas Grafc71099a2006-08-04 23:20:06 -07003327 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003328 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003329 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003330 int err = -ESRCH;
3331
Daniel Lezcano55786892008-03-04 13:47:47 -08003332 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003333 if (!table) {
3334 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003335 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003336 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337
Wei Wang66f5d6c2017-10-06 12:06:10 -07003338 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003339
3340 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003341 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003342 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003343 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003344
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003346 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003347 struct fib6_nh *nh;
3348
Wei Wang2b760fc2017-10-06 12:06:03 -07003349 if (cfg->fc_flags & RTF_CACHE) {
David Ahern7e4b5122019-04-16 14:36:00 -07003350 struct fib6_result res = {
3351 .f6i = rt,
3352 };
David Ahern23fb93a2018-04-17 17:33:23 -07003353 int rc;
3354
David Ahern7e4b5122019-04-16 14:36:00 -07003355 rt_cache = rt6_find_cached_rt(&res,
3356 &cfg->fc_dst,
Wei Wang2b760fc2017-10-06 12:06:03 -07003357 &cfg->fc_src);
David Ahern23fb93a2018-04-17 17:33:23 -07003358 if (rt_cache) {
3359 rc = ip6_del_cached_rt(rt_cache, cfg);
Eric Dumazet9e575012018-05-09 10:05:46 -07003360 if (rc != -ESRCH) {
3361 rcu_read_unlock();
David Ahern23fb93a2018-04-17 17:33:23 -07003362 return rc;
Eric Dumazet9e575012018-05-09 10:05:46 -07003363 }
David Ahern23fb93a2018-04-17 17:33:23 -07003364 }
3365 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003366 }
David Ahernad1601a2019-03-27 20:53:56 -07003367
3368 nh = &rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003369 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003370 (!nh->fib_nh_dev ||
3371 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003372 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003373 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003374 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003375 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003376 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003377 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003378 if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
Mantas Mc2ed1882016-12-16 10:30:59 +02003379 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003380 if (!fib6_info_hold_safe(rt))
3381 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003382 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003383
David Ahern0ae81332017-02-02 12:37:08 -08003384 /* if gateway was specified only delete the one hop */
3385 if (cfg->fc_flags & RTF_GATEWAY)
3386 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3387
3388 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003389 }
3390 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003391 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003392
3393 return err;
3394}
3395
David S. Miller6700c272012-07-17 03:29:28 -07003396static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003397{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003398 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003399 struct rt6_info *rt, *nrt = NULL;
David Ahern85bd05d2019-04-16 14:36:01 -07003400 struct fib6_result res = {};
David S. Millere8599ff2012-07-11 23:43:53 -07003401 struct ndisc_options ndopts;
3402 struct inet6_dev *in6_dev;
3403 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003404 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003405 int optlen, on_link;
3406 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07003407
Simon Horman29a3cad2013-05-28 20:34:26 +00003408 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003409 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07003410
3411 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07003412 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003413 return;
3414 }
3415
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003416 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07003417
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003418 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003419 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003420 return;
3421 }
3422
David S. Miller6e157b62012-07-12 00:05:02 -07003423 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003424 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003425 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003426 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07003427 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003428 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003429 return;
3430 }
3431
3432 in6_dev = __in6_dev_get(skb->dev);
3433 if (!in6_dev)
3434 return;
3435 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
3436 return;
3437
3438 /* RFC2461 8.1:
3439 * The IP source address of the Redirect MUST be the same as the current
3440 * first-hop router for the specified ICMP Destination Address.
3441 */
3442
Alexander Aringf997c552016-06-15 21:20:23 +02003443 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003444 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
3445 return;
3446 }
David S. Miller6e157b62012-07-12 00:05:02 -07003447
3448 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003449 if (ndopts.nd_opts_tgt_lladdr) {
3450 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
3451 skb->dev);
3452 if (!lladdr) {
3453 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
3454 return;
3455 }
3456 }
3457
David S. Miller6e157b62012-07-12 00:05:02 -07003458 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01003459 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07003460 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
3461 return;
3462 }
3463
3464 /* Redirect received -> path was valid.
3465 * Look, redirects are sent only in response to data packets,
3466 * so that this nexthop apparently is reachable. --ANK
3467 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02003468 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07003469
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003470 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07003471 if (!neigh)
3472 return;
3473
Linus Torvalds1da177e2005-04-16 15:20:36 -07003474 /*
3475 * We have finally decided to accept it.
3476 */
3477
Alexander Aringf997c552016-06-15 21:20:23 +02003478 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003479 NEIGH_UPDATE_F_WEAK_OVERRIDE|
3480 NEIGH_UPDATE_F_OVERRIDE|
3481 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02003482 NEIGH_UPDATE_F_ISROUTER)),
3483 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003484
David Ahern4d85cd02018-04-20 15:37:59 -07003485 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07003486 res.f6i = rcu_dereference(rt->from);
Wei Wange873e4b2018-07-21 20:56:32 -07003487 /* This fib6_info_hold() is safe here because we hold reference to rt
3488 * and rt already holds reference to fib6_info.
3489 */
David Ahern85bd05d2019-04-16 14:36:01 -07003490 fib6_info_hold(res.f6i);
David Ahern4d85cd02018-04-20 15:37:59 -07003491 rcu_read_unlock();
David Ahern8a14e462018-04-23 11:32:07 -07003492
David Ahern85bd05d2019-04-16 14:36:01 -07003493 res.nh = &res.f6i->fib6_nh;
3494 nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05003495 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003496 goto out;
3497
3498 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
3499 if (on_link)
3500 nrt->rt6i_flags &= ~RTF_GATEWAY;
3501
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003502 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003503
Wei Wang2b760fc2017-10-06 12:06:03 -07003504 /* No need to remove rt from the exception table if rt is
3505 * a cached route because rt6_insert_exception() will
3506 * takes care of it
3507 */
David Ahern5012f0a2019-04-16 14:36:05 -07003508 if (rt6_insert_exception(nrt, &res)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07003509 dst_release_immediate(&nrt->dst);
3510 goto out;
3511 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003512
Changli Gaod8d1f302010-06-10 23:31:35 -07003513 netevent.old = &rt->dst;
3514 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003515 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00003516 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07003517 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
3518
Linus Torvalds1da177e2005-04-16 15:20:36 -07003519out:
David Ahern85bd05d2019-04-16 14:36:01 -07003520 fib6_info_release(res.f6i);
David S. Millere8599ff2012-07-11 23:43:53 -07003521 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07003522}
3523
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003524#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07003525static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003526 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003527 const struct in6_addr *gwaddr,
3528 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003529{
David Ahern830218c2016-10-24 10:52:35 -07003530 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
3531 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003532 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07003533 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003534 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003535
David Ahern830218c2016-10-24 10:52:35 -07003536 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003537 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003538 return NULL;
3539
Wei Wang66f5d6c2017-10-06 12:06:10 -07003540 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07003541 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003542 if (!fn)
3543 goto out;
3544
Wei Wang66f5d6c2017-10-06 12:06:10 -07003545 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003546 if (rt->fib6_nh.fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003547 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07003548 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahernbdf00462019-04-05 16:30:26 -07003549 !rt->fib6_nh.fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003550 continue;
David Ahernad1601a2019-03-27 20:53:56 -07003551 if (!ipv6_addr_equal(&rt->fib6_nh.fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003552 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003553 if (!fib6_info_hold_safe(rt))
3554 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003555 break;
3556 }
3557out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003558 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003559 return rt;
3560}
3561
David Ahern8d1c8022018-04-17 17:33:26 -07003562static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003563 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003564 const struct in6_addr *gwaddr,
3565 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00003566 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003567{
Thomas Graf86872cb2006-08-22 00:01:08 -07003568 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08003569 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07003570 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07003571 .fc_dst_len = prefixlen,
3572 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
3573 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003574 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003575 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003576 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08003577 .fc_nlinfo.nlh = NULL,
3578 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003579 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003580
David Ahern830218c2016-10-24 10:52:35 -07003581 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003582 cfg.fc_dst = *prefix;
3583 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07003584
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08003585 /* We should treat it as a default route if prefix length is 0. */
3586 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07003587 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003588
David Ahernacb54e32018-04-17 17:33:22 -07003589 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003590
David Ahern830218c2016-10-24 10:52:35 -07003591 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003592}
3593#endif
3594
David Ahern8d1c8022018-04-17 17:33:26 -07003595struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003596 const struct in6_addr *addr,
3597 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003598{
David Ahern830218c2016-10-24 10:52:35 -07003599 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07003600 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07003601 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602
David Ahernafb1d4b52018-04-17 17:33:11 -07003603 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003604 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003605 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606
Wei Wang66f5d6c2017-10-06 12:06:10 -07003607 rcu_read_lock();
3608 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernad1601a2019-03-27 20:53:56 -07003609 struct fib6_nh *nh = &rt->fib6_nh;
3610
3611 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07003612 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07003613 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003614 break;
3615 }
Wei Wange873e4b2018-07-21 20:56:32 -07003616 if (rt && !fib6_info_hold_safe(rt))
3617 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003618 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003619 return rt;
3620}
3621
David Ahern8d1c8022018-04-17 17:33:26 -07003622struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003623 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08003624 struct net_device *dev,
3625 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626{
Thomas Graf86872cb2006-08-22 00:01:08 -07003627 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07003628 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08003629 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07003630 .fc_ifindex = dev->ifindex,
3631 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
3632 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003633 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003634 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003635 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08003636 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07003637 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003638 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003640 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003641
David Ahernacb54e32018-04-17 17:33:22 -07003642 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07003643 struct fib6_table *table;
3644
3645 table = fib6_get_table(dev_net(dev), cfg.fc_table);
3646 if (table)
3647 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
3648 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003649
David Ahernafb1d4b52018-04-17 17:33:11 -07003650 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003651}
3652
David Ahernafb1d4b52018-04-17 17:33:11 -07003653static void __rt6_purge_dflt_routers(struct net *net,
3654 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003655{
David Ahern8d1c8022018-04-17 17:33:26 -07003656 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003657
3658restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003659 rcu_read_lock();
3660 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07003661 struct net_device *dev = fib6_info_nh_dev(rt);
3662 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
3663
David Ahern93c2fb22018-04-18 15:38:59 -07003664 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07003665 (!idev || idev->cnf.accept_ra != 2) &&
3666 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07003667 rcu_read_unlock();
3668 ip6_del_rt(net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003669 goto restart;
3670 }
3671 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003672 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07003673
3674 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
3675}
3676
3677void rt6_purge_dflt_routers(struct net *net)
3678{
3679 struct fib6_table *table;
3680 struct hlist_head *head;
3681 unsigned int h;
3682
3683 rcu_read_lock();
3684
3685 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3686 head = &net->ipv6.fib_table_hash[h];
3687 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
3688 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07003689 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07003690 }
3691 }
3692
3693 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003694}
3695
Daniel Lezcano55786892008-03-04 13:47:47 -08003696static void rtmsg_to_fib6_config(struct net *net,
3697 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07003698 struct fib6_config *cfg)
3699{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003700 *cfg = (struct fib6_config){
3701 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
3702 : RT6_TABLE_MAIN,
3703 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07003704 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003705 .fc_expires = rtmsg->rtmsg_info,
3706 .fc_dst_len = rtmsg->rtmsg_dst_len,
3707 .fc_src_len = rtmsg->rtmsg_src_len,
3708 .fc_flags = rtmsg->rtmsg_flags,
3709 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07003710
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003711 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003712
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003713 .fc_dst = rtmsg->rtmsg_dst,
3714 .fc_src = rtmsg->rtmsg_src,
3715 .fc_gateway = rtmsg->rtmsg_gateway,
3716 };
Thomas Graf86872cb2006-08-22 00:01:08 -07003717}
3718
Daniel Lezcano55786892008-03-04 13:47:47 -08003719int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720{
Thomas Graf86872cb2006-08-22 00:01:08 -07003721 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722 struct in6_rtmsg rtmsg;
3723 int err;
3724
Ian Morris67ba4152014-08-24 21:53:10 +01003725 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003726 case SIOCADDRT: /* Add a route */
3727 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00003728 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003729 return -EPERM;
3730 err = copy_from_user(&rtmsg, arg,
3731 sizeof(struct in6_rtmsg));
3732 if (err)
3733 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07003734
Daniel Lezcano55786892008-03-04 13:47:47 -08003735 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07003736
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737 rtnl_lock();
3738 switch (cmd) {
3739 case SIOCADDRT:
David Ahernacb54e32018-04-17 17:33:22 -07003740 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003741 break;
3742 case SIOCDELRT:
David Ahern333c4302017-05-21 10:12:04 -06003743 err = ip6_route_del(&cfg, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003744 break;
3745 default:
3746 err = -EINVAL;
3747 }
3748 rtnl_unlock();
3749
3750 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07003751 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752
3753 return -EINVAL;
3754}
3755
3756/*
3757 * Drop the packet on the floor
3758 */
3759
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07003760static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003761{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003762 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00003763 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003764 switch (ipstats_mib_noroutes) {
3765 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07003766 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00003767 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputrabdb7cc62018-04-16 13:42:16 -04003768 IP6_INC_STATS(dev_net(dst->dev),
3769 __in6_dev_get_safely(skb->dev),
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003770 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003771 break;
3772 }
3773 /* FALLTHROUGH */
3774 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003775 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
3776 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003777 break;
3778 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00003779 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003780 kfree_skb(skb);
3781 return 0;
3782}
3783
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003784static int ip6_pkt_discard(struct sk_buff *skb)
3785{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003786 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003787}
3788
Eric W. Biedermanede20592015-10-07 16:48:47 -05003789static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003790{
Eric Dumazetadf30902009-06-02 05:19:30 +00003791 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003792 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003793}
3794
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003795static int ip6_pkt_prohibit(struct sk_buff *skb)
3796{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003797 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003798}
3799
Eric W. Biedermanede20592015-10-07 16:48:47 -05003800static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003801{
Eric Dumazetadf30902009-06-02 05:19:30 +00003802 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003803 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003804}
3805
Linus Torvalds1da177e2005-04-16 15:20:36 -07003806/*
3807 * Allocate a dst for local (unicast / anycast) address.
3808 */
3809
David Ahern360a9882018-04-18 15:39:00 -07003810struct fib6_info *addrconf_f6i_alloc(struct net *net,
3811 struct inet6_dev *idev,
3812 const struct in6_addr *addr,
3813 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003814{
David Ahernc7a1ce32019-03-21 05:21:35 -07003815 struct fib6_config cfg = {
3816 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
3817 .fc_ifindex = idev->dev->ifindex,
3818 .fc_flags = RTF_UP | RTF_ADDRCONF | RTF_NONEXTHOP,
3819 .fc_dst = *addr,
3820 .fc_dst_len = 128,
3821 .fc_protocol = RTPROT_KERNEL,
3822 .fc_nlinfo.nl_net = net,
3823 .fc_ignore_dev_down = true,
3824 };
David Ahern5f02ce242016-09-10 12:09:54 -07003825
David Aherne8478e82018-04-17 17:33:13 -07003826 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07003827 cfg.fc_type = RTN_ANYCAST;
3828 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07003829 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07003830 cfg.fc_type = RTN_LOCAL;
3831 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07003832 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003833
David Ahernc7a1ce32019-03-21 05:21:35 -07003834 return ip6_route_info_create(&cfg, gfp_flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003835}
3836
Daniel Walterc3968a82011-04-13 21:10:57 +00003837/* remove deleted ip from prefsrc entries */
3838struct arg_dev_net_ip {
3839 struct net_device *dev;
3840 struct net *net;
3841 struct in6_addr *addr;
3842};
3843
David Ahern8d1c8022018-04-17 17:33:26 -07003844static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00003845{
3846 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
3847 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
3848 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
3849
David Ahernad1601a2019-03-27 20:53:56 -07003850 if (((void *)rt->fib6_nh.fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07003851 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07003852 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07003853 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003854 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07003855 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07003856 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003857 }
3858 return 0;
3859}
3860
3861void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
3862{
3863 struct net *net = dev_net(ifp->idev->dev);
3864 struct arg_dev_net_ip adni = {
3865 .dev = ifp->idev->dev,
3866 .net = net,
3867 .addr = &ifp->addr,
3868 };
Li RongQing0c3584d2013-12-27 16:32:38 +08003869 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00003870}
3871
David Ahern2b2450c2019-03-27 20:53:52 -07003872#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003873
3874/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07003875static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003876{
3877 struct in6_addr *gateway = (struct in6_addr *)arg;
3878
David Ahern93c2fb22018-04-18 15:38:59 -07003879 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Ahernbdf00462019-04-05 16:30:26 -07003880 rt->fib6_nh.fib_nh_gw_family &&
David Ahernad1601a2019-03-27 20:53:56 -07003881 ipv6_addr_equal(gateway, &rt->fib6_nh.fib_nh_gw6)) {
Duan Jiongbe7a0102014-05-15 15:56:14 +08003882 return -1;
3883 }
Wei Wangb16cb452017-10-06 12:06:00 -07003884
3885 /* Further clean up cached routes in exception table.
3886 * This is needed because cached route may have a different
3887 * gateway than its 'parent' in the case of an ip redirect.
3888 */
3889 rt6_exceptions_clean_tohost(rt, gateway);
3890
Duan Jiongbe7a0102014-05-15 15:56:14 +08003891 return 0;
3892}
3893
3894void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
3895{
3896 fib6_clean_all(net, fib6_clean_tohost, gateway);
3897}
3898
Ido Schimmel2127d952018-01-07 12:45:03 +02003899struct arg_netdev_event {
3900 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003901 union {
3902 unsigned int nh_flags;
3903 unsigned long event;
3904 };
Ido Schimmel2127d952018-01-07 12:45:03 +02003905};
3906
David Ahern8d1c8022018-04-17 17:33:26 -07003907static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003908{
David Ahern8d1c8022018-04-17 17:33:26 -07003909 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003910 struct fib6_node *fn;
3911
David Ahern93c2fb22018-04-18 15:38:59 -07003912 fn = rcu_dereference_protected(rt->fib6_node,
3913 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003914 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07003915 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003916 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07003917 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07003918 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003919 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07003920 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07003921 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003922 }
3923
3924 return NULL;
3925}
3926
David Ahern8d1c8022018-04-17 17:33:26 -07003927static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003928{
David Ahernad1601a2019-03-27 20:53:56 -07003929 if (rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ||
3930 (rt->fib6_nh.fib_nh_flags & RTNH_F_LINKDOWN &&
3931 ip6_ignore_linkdown(rt->fib6_nh.fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003932 return true;
3933
3934 return false;
3935}
3936
David Ahern8d1c8022018-04-17 17:33:26 -07003937static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003938{
David Ahern8d1c8022018-04-17 17:33:26 -07003939 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003940 int total = 0;
3941
3942 if (!rt6_is_dead(rt))
David Ahernad1601a2019-03-27 20:53:56 -07003943 total += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003944
David Ahern93c2fb22018-04-18 15:38:59 -07003945 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003946 if (!rt6_is_dead(iter))
David Ahernad1601a2019-03-27 20:53:56 -07003947 total += iter->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003948 }
3949
3950 return total;
3951}
3952
David Ahern8d1c8022018-04-17 17:33:26 -07003953static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003954{
3955 int upper_bound = -1;
3956
3957 if (!rt6_is_dead(rt)) {
David Ahernad1601a2019-03-27 20:53:56 -07003958 *weight += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003959 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
3960 total) - 1;
3961 }
David Ahernad1601a2019-03-27 20:53:56 -07003962 atomic_set(&rt->fib6_nh.fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003963}
3964
David Ahern8d1c8022018-04-17 17:33:26 -07003965static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003966{
David Ahern8d1c8022018-04-17 17:33:26 -07003967 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003968 int weight = 0;
3969
3970 rt6_upper_bound_set(rt, &weight, total);
3971
David Ahern93c2fb22018-04-18 15:38:59 -07003972 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003973 rt6_upper_bound_set(iter, &weight, total);
3974}
3975
David Ahern8d1c8022018-04-17 17:33:26 -07003976void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003977{
David Ahern8d1c8022018-04-17 17:33:26 -07003978 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003979 int total;
3980
3981 /* In case the entire multipath route was marked for flushing,
3982 * then there is no need to rebalance upon the removal of every
3983 * sibling route.
3984 */
David Ahern93c2fb22018-04-18 15:38:59 -07003985 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003986 return;
3987
3988 /* During lookup routes are evaluated in order, so we need to
3989 * make sure upper bounds are assigned from the first sibling
3990 * onwards.
3991 */
3992 first = rt6_multipath_first_sibling(rt);
3993 if (WARN_ON_ONCE(!first))
3994 return;
3995
3996 total = rt6_multipath_total_weight(first);
3997 rt6_multipath_upper_bound_set(first, total);
3998}
3999
David Ahern8d1c8022018-04-17 17:33:26 -07004000static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02004001{
4002 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07004003 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02004004
David Ahernad1601a2019-03-27 20:53:56 -07004005 if (rt != net->ipv6.fib6_null_entry &&
4006 rt->fib6_nh.fib_nh_dev == arg->dev) {
4007 rt->fib6_nh.fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07004008 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004009 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004010 }
Ido Schimmel2127d952018-01-07 12:45:03 +02004011
4012 return 0;
4013}
4014
4015void rt6_sync_up(struct net_device *dev, unsigned int nh_flags)
4016{
4017 struct arg_netdev_event arg = {
4018 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004019 {
4020 .nh_flags = nh_flags,
4021 },
Ido Schimmel2127d952018-01-07 12:45:03 +02004022 };
4023
4024 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
4025 arg.nh_flags |= RTNH_F_LINKDOWN;
4026
4027 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
4028}
4029
David Ahern8d1c8022018-04-17 17:33:26 -07004030static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004031 const struct net_device *dev)
4032{
David Ahern8d1c8022018-04-17 17:33:26 -07004033 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004034
David Ahernad1601a2019-03-27 20:53:56 -07004035 if (rt->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004036 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07004037 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004038 if (iter->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004039 return true;
4040
4041 return false;
4042}
4043
David Ahern8d1c8022018-04-17 17:33:26 -07004044static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004045{
David Ahern8d1c8022018-04-17 17:33:26 -07004046 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004047
4048 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004049 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004050 iter->should_flush = 1;
4051}
4052
David Ahern8d1c8022018-04-17 17:33:26 -07004053static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004054 const struct net_device *down_dev)
4055{
David Ahern8d1c8022018-04-17 17:33:26 -07004056 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004057 unsigned int dead = 0;
4058
David Ahernad1601a2019-03-27 20:53:56 -07004059 if (rt->fib6_nh.fib_nh_dev == down_dev ||
4060 rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004061 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004062 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004063 if (iter->fib6_nh.fib_nh_dev == down_dev ||
4064 iter->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004065 dead++;
4066
4067 return dead;
4068}
4069
David Ahern8d1c8022018-04-17 17:33:26 -07004070static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004071 const struct net_device *dev,
4072 unsigned int nh_flags)
4073{
David Ahern8d1c8022018-04-17 17:33:26 -07004074 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004075
David Ahernad1601a2019-03-27 20:53:56 -07004076 if (rt->fib6_nh.fib_nh_dev == dev)
4077 rt->fib6_nh.fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004078 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004079 if (iter->fib6_nh.fib_nh_dev == dev)
4080 iter->fib6_nh.fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004081}
4082
David Aherna1a22c12017-01-18 07:40:36 -08004083/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004084static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004085{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004086 const struct arg_netdev_event *arg = p_arg;
4087 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004088 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004089
David Ahern421842e2018-04-17 17:33:18 -07004090 if (rt == net->ipv6.fib6_null_entry)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004091 return 0;
4092
4093 switch (arg->event) {
4094 case NETDEV_UNREGISTER:
David Ahernad1601a2019-03-27 20:53:56 -07004095 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004096 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004097 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004098 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004099 if (!rt->fib6_nsiblings)
David Ahernad1601a2019-03-27 20:53:56 -07004100 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004101 if (rt6_multipath_uses_dev(rt, dev)) {
4102 unsigned int count;
4103
4104 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004105 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004106 rt6_multipath_flush(rt);
4107 return -1;
4108 }
4109 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4110 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004111 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004112 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004113 }
4114 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004115 case NETDEV_CHANGE:
David Ahernad1601a2019-03-27 20:53:56 -07004116 if (rt->fib6_nh.fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004117 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004118 break;
David Ahernad1601a2019-03-27 20:53:56 -07004119 rt->fib6_nh.fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004120 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004121 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004122 }
David S. Millerc159d302011-12-26 15:24:36 -05004123
Linus Torvalds1da177e2005-04-16 15:20:36 -07004124 return 0;
4125}
4126
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004127void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004128{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004129 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004130 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004131 {
4132 .event = event,
4133 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004134 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004135 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004136
David Ahern7c6bb7d2018-10-11 20:17:21 -07004137 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4138 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4139 else
4140 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004141}
4142
4143void rt6_disable_ip(struct net_device *dev, unsigned long event)
4144{
4145 rt6_sync_down_dev(dev, event);
4146 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4147 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004148}
4149
Eric Dumazet95c96172012-04-15 05:58:06 +00004150struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004151 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004152 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004153};
4154
David Ahern8d1c8022018-04-17 17:33:26 -07004155static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004156{
4157 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4158 struct inet6_dev *idev;
4159
4160 /* In IPv6 pmtu discovery is not optional,
4161 so that RTAX_MTU lock cannot disable it.
4162 We still use this lock to block changes
4163 caused by addrconf/ndisc.
4164 */
4165
4166 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004167 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004168 return 0;
4169
4170 /* For administrative MTU increase, there is no way to discover
4171 IPv6 PMTU increase, so PMTU increase should be updated here.
4172 Since RFC 1981 doesn't include administrative MTU increase
4173 update PMTU increase is a MUST. (i.e. jumbo frame)
4174 */
David Ahernad1601a2019-03-27 20:53:56 -07004175 if (rt->fib6_nh.fib_nh_dev == arg->dev &&
David Ahernd4ead6b2018-04-17 17:33:16 -07004176 !fib6_metric_locked(rt, RTAX_MTU)) {
4177 u32 mtu = rt->fib6_pmtu;
4178
4179 if (mtu >= arg->mtu ||
4180 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4181 fib6_metric_set(rt, RTAX_MTU, arg->mtu);
4182
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004183 spin_lock_bh(&rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01004184 rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004185 spin_unlock_bh(&rt6_exception_lock);
Simon Arlott566cfd82007-07-26 00:09:55 -07004186 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187 return 0;
4188}
4189
Eric Dumazet95c96172012-04-15 05:58:06 +00004190void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004191{
Thomas Grafc71099a2006-08-04 23:20:06 -07004192 struct rt6_mtu_change_arg arg = {
4193 .dev = dev,
4194 .mtu = mtu,
4195 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004196
Li RongQing0c3584d2013-12-27 16:32:38 +08004197 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004198}
4199
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004200static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07004201 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004202 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004203 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004204 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004205 [RTA_PRIORITY] = { .type = NLA_U32 },
4206 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004207 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004208 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004209 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4210 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004211 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004212 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004213 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004214 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004215 [RTA_IP_PROTO] = { .type = NLA_U8 },
4216 [RTA_SPORT] = { .type = NLA_U16 },
4217 [RTA_DPORT] = { .type = NLA_U16 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004218};
4219
4220static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004221 struct fib6_config *cfg,
4222 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004223{
Thomas Graf86872cb2006-08-22 00:01:08 -07004224 struct rtmsg *rtm;
4225 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004226 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004227 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004228
Johannes Bergfceb6432017-04-12 14:34:07 +02004229 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy,
David Aherndac9c972018-10-07 20:16:24 -07004230 extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004231 if (err < 0)
4232 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004233
Thomas Graf86872cb2006-08-22 00:01:08 -07004234 err = -EINVAL;
4235 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004236
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004237 *cfg = (struct fib6_config){
4238 .fc_table = rtm->rtm_table,
4239 .fc_dst_len = rtm->rtm_dst_len,
4240 .fc_src_len = rtm->rtm_src_len,
4241 .fc_flags = RTF_UP,
4242 .fc_protocol = rtm->rtm_protocol,
4243 .fc_type = rtm->rtm_type,
4244
4245 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4246 .fc_nlinfo.nlh = nlh,
4247 .fc_nlinfo.nl_net = sock_net(skb->sk),
4248 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004249
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004250 if (rtm->rtm_type == RTN_UNREACHABLE ||
4251 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004252 rtm->rtm_type == RTN_PROHIBIT ||
4253 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004254 cfg->fc_flags |= RTF_REJECT;
4255
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004256 if (rtm->rtm_type == RTN_LOCAL)
4257 cfg->fc_flags |= RTF_LOCAL;
4258
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004259 if (rtm->rtm_flags & RTM_F_CLONED)
4260 cfg->fc_flags |= RTF_CACHE;
4261
David Ahernfc1e64e2018-01-25 16:55:09 -08004262 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4263
Thomas Graf86872cb2006-08-22 00:01:08 -07004264 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004265 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004266 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004267 }
David Aherne3818542019-02-26 09:00:03 -08004268 if (tb[RTA_VIA]) {
4269 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4270 goto errout;
4271 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004272
4273 if (tb[RTA_DST]) {
4274 int plen = (rtm->rtm_dst_len + 7) >> 3;
4275
4276 if (nla_len(tb[RTA_DST]) < plen)
4277 goto errout;
4278
4279 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004280 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004281
4282 if (tb[RTA_SRC]) {
4283 int plen = (rtm->rtm_src_len + 7) >> 3;
4284
4285 if (nla_len(tb[RTA_SRC]) < plen)
4286 goto errout;
4287
4288 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004289 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004290
Daniel Walterc3968a82011-04-13 21:10:57 +00004291 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004292 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004293
Thomas Graf86872cb2006-08-22 00:01:08 -07004294 if (tb[RTA_OIF])
4295 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4296
4297 if (tb[RTA_PRIORITY])
4298 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4299
4300 if (tb[RTA_METRICS]) {
4301 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4302 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004303 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004304
4305 if (tb[RTA_TABLE])
4306 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4307
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004308 if (tb[RTA_MULTIPATH]) {
4309 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4310 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004311
4312 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004313 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004314 if (err < 0)
4315 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004316 }
4317
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004318 if (tb[RTA_PREF]) {
4319 pref = nla_get_u8(tb[RTA_PREF]);
4320 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4321 pref != ICMPV6_ROUTER_PREF_HIGH)
4322 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4323 cfg->fc_flags |= RTF_PREF(pref);
4324 }
4325
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004326 if (tb[RTA_ENCAP])
4327 cfg->fc_encap = tb[RTA_ENCAP];
4328
David Ahern9ed59592017-01-17 14:57:36 -08004329 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004330 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4331
David Ahernc255bd62017-05-27 16:19:27 -06004332 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004333 if (err < 0)
4334 goto errout;
4335 }
4336
Xin Long32bc2012015-12-16 17:50:11 +08004337 if (tb[RTA_EXPIRES]) {
4338 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4339
4340 if (addrconf_finite_timeout(timeout)) {
4341 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
4342 cfg->fc_flags |= RTF_EXPIRES;
4343 }
4344 }
4345
Thomas Graf86872cb2006-08-22 00:01:08 -07004346 err = 0;
4347errout:
4348 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004349}
4350
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004351struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07004352 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004353 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004354 struct list_head next;
4355};
4356
David Ahernd4ead6b2018-04-17 17:33:16 -07004357static int ip6_route_info_append(struct net *net,
4358 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07004359 struct fib6_info *rt,
4360 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004361{
4362 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004363 int err = -EEXIST;
4364
4365 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004366 /* check if fib6_info already exists */
4367 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004368 return err;
4369 }
4370
4371 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
4372 if (!nh)
4373 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07004374 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004375 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
4376 list_add_tail(&nh->next, rt6_nh_list);
4377
4378 return 0;
4379}
4380
David Ahern8d1c8022018-04-17 17:33:26 -07004381static void ip6_route_mpath_notify(struct fib6_info *rt,
4382 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08004383 struct nl_info *info,
4384 __u16 nlflags)
4385{
4386 /* if this is an APPEND route, then rt points to the first route
4387 * inserted and rt_last points to last route inserted. Userspace
4388 * wants a consistent dump of the route which starts at the first
4389 * nexthop. Since sibling routes are always added at the end of
4390 * the list, find the first sibling of the last route appended
4391 */
David Ahern93c2fb22018-04-18 15:38:59 -07004392 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
4393 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07004394 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07004395 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08004396 }
4397
4398 if (rt)
4399 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4400}
4401
David Ahern333c4302017-05-21 10:12:04 -06004402static int ip6_route_multipath_add(struct fib6_config *cfg,
4403 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004404{
David Ahern8d1c8022018-04-17 17:33:26 -07004405 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08004406 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004407 struct fib6_config r_cfg;
4408 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07004409 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004410 struct rt6_nh *err_nh;
4411 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08004412 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004413 int remaining;
4414 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004415 int err = 1;
4416 int nhn = 0;
4417 int replace = (cfg->fc_nlinfo.nlh &&
4418 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
4419 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004420
David Ahern3b1137f2017-02-02 12:37:10 -08004421 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
4422 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
4423 nlflags |= NLM_F_APPEND;
4424
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02004425 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004426 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004427
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004428 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07004429 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004430 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004431 while (rtnh_ok(rtnh, remaining)) {
4432 memcpy(&r_cfg, cfg, sizeof(*cfg));
4433 if (rtnh->rtnh_ifindex)
4434 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4435
4436 attrlen = rtnh_attrlen(rtnh);
4437 if (attrlen > 0) {
4438 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4439
4440 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4441 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004442 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004443 r_cfg.fc_flags |= RTF_GATEWAY;
4444 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004445 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
4446 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
4447 if (nla)
4448 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004449 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004450
David Ahern68e2ffd2018-03-20 10:06:59 -07004451 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07004452 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004453 if (IS_ERR(rt)) {
4454 err = PTR_ERR(rt);
4455 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004456 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004457 }
David Ahernb5d2d752018-07-15 09:35:19 -07004458 if (!rt6_qualify_for_ecmp(rt)) {
4459 err = -EINVAL;
4460 NL_SET_ERR_MSG(extack,
4461 "Device only routes can not be added for IPv6 using the multipath API.");
4462 fib6_info_release(rt);
4463 goto cleanup;
4464 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004465
David Ahernad1601a2019-03-27 20:53:56 -07004466 rt->fib6_nh.fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02004467
David Ahernd4ead6b2018-04-17 17:33:16 -07004468 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
4469 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004470 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07004471 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004472 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004473 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004474
4475 rtnh = rtnh_next(rtnh, &remaining);
4476 }
4477
David Ahern3b1137f2017-02-02 12:37:10 -08004478 /* for add and replace send one notification with all nexthops.
4479 * Skip the notification in fib6_add_rt2node and send one with
4480 * the full route when done
4481 */
4482 info->skip_notify = 1;
4483
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004484 err_nh = NULL;
4485 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004486 err = __ip6_ins_rt(nh->fib6_info, info, extack);
4487 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08004488
David Ahernf7225172018-06-04 13:41:42 -07004489 if (!err) {
4490 /* save reference to last route successfully inserted */
4491 rt_last = nh->fib6_info;
4492
4493 /* save reference to first route for notification */
4494 if (!rt_notif)
4495 rt_notif = nh->fib6_info;
4496 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004497
David Ahern8d1c8022018-04-17 17:33:26 -07004498 /* nh->fib6_info is used or freed at this point, reset to NULL*/
4499 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004500 if (err) {
4501 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08004502 NL_SET_ERR_MSG_MOD(extack,
4503 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004504 err_nh = nh;
4505 goto add_errout;
4506 }
4507
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004508 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02004509 * these flags after the first nexthop: if there is a collision,
4510 * we have already failed to add the first nexthop:
4511 * fib6_add_rt2node() has rejected it; when replacing, old
4512 * nexthops have been replaced by first new, the rest should
4513 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004514 */
Michal Kubeček27596472015-05-18 20:54:00 +02004515 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
4516 NLM_F_REPLACE);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004517 nhn++;
4518 }
4519
David Ahern3b1137f2017-02-02 12:37:10 -08004520 /* success ... tell user about new route */
4521 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004522 goto cleanup;
4523
4524add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08004525 /* send notification for routes that were added so that
4526 * the delete notifications sent by ip6_route_del are
4527 * coherent
4528 */
4529 if (rt_notif)
4530 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
4531
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004532 /* Delete routes that were already added */
4533 list_for_each_entry(nh, &rt6_nh_list, next) {
4534 if (err_nh == nh)
4535 break;
David Ahern333c4302017-05-21 10:12:04 -06004536 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004537 }
4538
4539cleanup:
4540 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004541 if (nh->fib6_info)
4542 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004543 list_del(&nh->next);
4544 kfree(nh);
4545 }
4546
4547 return err;
4548}
4549
David Ahern333c4302017-05-21 10:12:04 -06004550static int ip6_route_multipath_del(struct fib6_config *cfg,
4551 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004552{
4553 struct fib6_config r_cfg;
4554 struct rtnexthop *rtnh;
4555 int remaining;
4556 int attrlen;
4557 int err = 1, last_err = 0;
4558
4559 remaining = cfg->fc_mp_len;
4560 rtnh = (struct rtnexthop *)cfg->fc_mp;
4561
4562 /* Parse a Multipath Entry */
4563 while (rtnh_ok(rtnh, remaining)) {
4564 memcpy(&r_cfg, cfg, sizeof(*cfg));
4565 if (rtnh->rtnh_ifindex)
4566 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4567
4568 attrlen = rtnh_attrlen(rtnh);
4569 if (attrlen > 0) {
4570 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4571
4572 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4573 if (nla) {
4574 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
4575 r_cfg.fc_flags |= RTF_GATEWAY;
4576 }
4577 }
David Ahern333c4302017-05-21 10:12:04 -06004578 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004579 if (err)
4580 last_err = err;
4581
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004582 rtnh = rtnh_next(rtnh, &remaining);
4583 }
4584
4585 return last_err;
4586}
4587
David Ahernc21ef3e2017-04-16 09:48:24 -07004588static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4589 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004590{
Thomas Graf86872cb2006-08-22 00:01:08 -07004591 struct fib6_config cfg;
4592 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004593
David Ahern333c4302017-05-21 10:12:04 -06004594 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004595 if (err < 0)
4596 return err;
4597
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004598 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004599 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004600 else {
4601 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06004602 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004603 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004604}
4605
David Ahernc21ef3e2017-04-16 09:48:24 -07004606static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4607 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004608{
Thomas Graf86872cb2006-08-22 00:01:08 -07004609 struct fib6_config cfg;
4610 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004611
David Ahern333c4302017-05-21 10:12:04 -06004612 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004613 if (err < 0)
4614 return err;
4615
David Ahern67f69512019-03-21 05:21:34 -07004616 if (cfg.fc_metric == 0)
4617 cfg.fc_metric = IP6_RT_PRIO_USER;
4618
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004619 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004620 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004621 else
David Ahernacb54e32018-04-17 17:33:22 -07004622 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004623}
4624
David Ahern8d1c8022018-04-17 17:33:26 -07004625static size_t rt6_nlmsg_size(struct fib6_info *rt)
Thomas Graf339bf982006-11-10 14:10:15 -08004626{
David Ahernbeb1afac52017-02-02 12:37:09 -08004627 int nexthop_len = 0;
4628
David Ahern93c2fb22018-04-18 15:38:59 -07004629 if (rt->fib6_nsiblings) {
David Ahernbeb1afac52017-02-02 12:37:09 -08004630 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
4631 + NLA_ALIGN(sizeof(struct rtnexthop))
4632 + nla_total_size(16) /* RTA_GATEWAY */
David Ahernad1601a2019-03-27 20:53:56 -07004633 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08004634
David Ahern93c2fb22018-04-18 15:38:59 -07004635 nexthop_len *= rt->fib6_nsiblings;
David Ahernbeb1afac52017-02-02 12:37:09 -08004636 }
4637
Thomas Graf339bf982006-11-10 14:10:15 -08004638 return NLMSG_ALIGN(sizeof(struct rtmsg))
4639 + nla_total_size(16) /* RTA_SRC */
4640 + nla_total_size(16) /* RTA_DST */
4641 + nla_total_size(16) /* RTA_GATEWAY */
4642 + nla_total_size(16) /* RTA_PREFSRC */
4643 + nla_total_size(4) /* RTA_TABLE */
4644 + nla_total_size(4) /* RTA_IIF */
4645 + nla_total_size(4) /* RTA_OIF */
4646 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08004647 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01004648 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004649 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004650 + nla_total_size(1) /* RTA_PREF */
David Ahernad1601a2019-03-27 20:53:56 -07004651 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws)
David Ahernbeb1afac52017-02-02 12:37:09 -08004652 + nexthop_len;
4653}
4654
David Ahernd4ead6b2018-04-17 17:33:16 -07004655static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07004656 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07004657 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004658 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08004659 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004660{
Xin Long22d0bd82018-09-11 14:33:58 +08004661 struct rt6_info *rt6 = (struct rt6_info *)dst;
4662 struct rt6key *rt6_dst, *rt6_src;
4663 u32 *pmetrics, table, rt6_flags;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004664 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08004665 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07004666 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004667
Eric W. Biederman15e47302012-09-07 20:12:54 +00004668 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05004669 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08004670 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004671
Xin Long22d0bd82018-09-11 14:33:58 +08004672 if (rt6) {
4673 rt6_dst = &rt6->rt6i_dst;
4674 rt6_src = &rt6->rt6i_src;
4675 rt6_flags = rt6->rt6i_flags;
4676 } else {
4677 rt6_dst = &rt->fib6_dst;
4678 rt6_src = &rt->fib6_src;
4679 rt6_flags = rt->fib6_flags;
4680 }
4681
Thomas Graf2d7202b2006-08-22 00:01:27 -07004682 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004683 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08004684 rtm->rtm_dst_len = rt6_dst->plen;
4685 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004686 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07004687 if (rt->fib6_table)
4688 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07004689 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07004690 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08004691 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04004692 if (nla_put_u32(skb, RTA_TABLE, table))
4693 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07004694
4695 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004696 rtm->rtm_flags = 0;
4697 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07004698 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004699
Xin Long22d0bd82018-09-11 14:33:58 +08004700 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004701 rtm->rtm_flags |= RTM_F_CLONED;
4702
David Ahernd4ead6b2018-04-17 17:33:16 -07004703 if (dest) {
4704 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04004705 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004706 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004707 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08004708 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004709 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004710#ifdef CONFIG_IPV6_SUBTREES
4711 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02004712 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04004713 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004714 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04004715 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08004716 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004717 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004718#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004719 if (iif) {
4720#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08004721 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08004722 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02004723
David Ahernfd61c6b2017-01-17 15:51:07 -08004724 if (err == 0)
4725 return 0;
4726 if (err < 0)
4727 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004728 } else
4729#endif
David S. Millerc78679e2012-04-01 20:27:33 -04004730 if (nla_put_u32(skb, RTA_IIF, iif))
4731 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07004732 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004733 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07004734 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02004735 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004736 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004737 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07004738
David Ahern93c2fb22018-04-18 15:38:59 -07004739 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00004740 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07004741 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02004742 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004743 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00004744 }
4745
David Ahernd4ead6b2018-04-17 17:33:16 -07004746 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
4747 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07004748 goto nla_put_failure;
4749
David Ahern93c2fb22018-04-18 15:38:59 -07004750 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04004751 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00004752
David Ahernbeb1afac52017-02-02 12:37:09 -08004753 /* For multipath routes, walk the siblings list and add
4754 * each as a nexthop within RTA_MULTIPATH.
4755 */
Xin Long22d0bd82018-09-11 14:33:58 +08004756 if (rt6) {
4757 if (rt6_flags & RTF_GATEWAY &&
4758 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
4759 goto nla_put_failure;
4760
4761 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
4762 goto nla_put_failure;
4763 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07004764 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08004765 struct nlattr *mp;
4766
4767 mp = nla_nest_start(skb, RTA_MULTIPATH);
4768 if (!mp)
4769 goto nla_put_failure;
4770
David Ahernc0a72072019-04-02 14:11:58 -07004771 if (fib_add_nexthop(skb, &rt->fib6_nh.nh_common,
4772 rt->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004773 goto nla_put_failure;
4774
4775 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07004776 &rt->fib6_siblings, fib6_siblings) {
David Ahernc0a72072019-04-02 14:11:58 -07004777 if (fib_add_nexthop(skb, &sibling->fib6_nh.nh_common,
4778 sibling->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004779 goto nla_put_failure;
4780 }
4781
4782 nla_nest_end(skb, mp);
4783 } else {
David Ahernc0a72072019-04-02 14:11:58 -07004784 if (fib_nexthop_info(skb, &rt->fib6_nh.nh_common,
4785 &rtm->rtm_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004786 goto nla_put_failure;
4787 }
4788
Xin Long22d0bd82018-09-11 14:33:58 +08004789 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07004790 expires = dst ? dst->expires : rt->expires;
4791 expires -= jiffies;
4792 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07004793
David Ahernd4ead6b2018-04-17 17:33:16 -07004794 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08004795 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004796
Xin Long22d0bd82018-09-11 14:33:58 +08004797 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004798 goto nla_put_failure;
4799
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004800
Johannes Berg053c0952015-01-16 22:09:00 +01004801 nlmsg_end(skb, nlh);
4802 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004803
4804nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08004805 nlmsg_cancel(skb, nlh);
4806 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004807}
4808
David Ahern13e38902018-10-15 18:56:44 -07004809static bool fib6_info_uses_dev(const struct fib6_info *f6i,
4810 const struct net_device *dev)
4811{
David Ahernad1601a2019-03-27 20:53:56 -07004812 if (f6i->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004813 return true;
4814
4815 if (f6i->fib6_nsiblings) {
4816 struct fib6_info *sibling, *next_sibling;
4817
4818 list_for_each_entry_safe(sibling, next_sibling,
4819 &f6i->fib6_siblings, fib6_siblings) {
David Ahernad1601a2019-03-27 20:53:56 -07004820 if (sibling->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004821 return true;
4822 }
4823 }
4824
4825 return false;
4826}
4827
David Ahern8d1c8022018-04-17 17:33:26 -07004828int rt6_dump_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004829{
4830 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07004831 struct fib_dump_filter *filter = &arg->filter;
4832 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08004833 struct net *net = arg->net;
4834
David Ahern421842e2018-04-17 17:33:18 -07004835 if (rt == net->ipv6.fib6_null_entry)
David Ahern1f17e2f2017-01-26 13:54:08 -08004836 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004837
David Ahern13e38902018-10-15 18:56:44 -07004838 if ((filter->flags & RTM_F_PREFIX) &&
4839 !(rt->fib6_flags & RTF_PREFIX_RT)) {
4840 /* success since this is not a prefix route */
4841 return 1;
4842 }
4843 if (filter->filter_set) {
4844 if ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
4845 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
4846 (filter->protocol && rt->fib6_protocol != filter->protocol)) {
David Ahernf8cfe2c2017-01-17 15:51:08 -08004847 return 1;
4848 }
David Ahern13e38902018-10-15 18:56:44 -07004849 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08004850 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004851
David Ahernd4ead6b2018-04-17 17:33:16 -07004852 return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
4853 RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
David Ahern13e38902018-10-15 18:56:44 -07004854 arg->cb->nlh->nlmsg_seq, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004855}
4856
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004857static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
4858 const struct nlmsghdr *nlh,
4859 struct nlattr **tb,
4860 struct netlink_ext_ack *extack)
4861{
4862 struct rtmsg *rtm;
4863 int i, err;
4864
4865 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
4866 NL_SET_ERR_MSG_MOD(extack,
4867 "Invalid header for get route request");
4868 return -EINVAL;
4869 }
4870
4871 if (!netlink_strict_get_check(skb))
4872 return nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
4873 rtm_ipv6_policy, extack);
4874
4875 rtm = nlmsg_data(nlh);
4876 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
4877 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
4878 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
4879 rtm->rtm_type) {
4880 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
4881 return -EINVAL;
4882 }
4883 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
4884 NL_SET_ERR_MSG_MOD(extack,
4885 "Invalid flags for get route request");
4886 return -EINVAL;
4887 }
4888
4889 err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
4890 rtm_ipv6_policy, extack);
4891 if (err)
4892 return err;
4893
4894 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
4895 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
4896 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
4897 return -EINVAL;
4898 }
4899
4900 for (i = 0; i <= RTA_MAX; i++) {
4901 if (!tb[i])
4902 continue;
4903
4904 switch (i) {
4905 case RTA_SRC:
4906 case RTA_DST:
4907 case RTA_IIF:
4908 case RTA_OIF:
4909 case RTA_MARK:
4910 case RTA_UID:
4911 case RTA_SPORT:
4912 case RTA_DPORT:
4913 case RTA_IP_PROTO:
4914 break;
4915 default:
4916 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
4917 return -EINVAL;
4918 }
4919 }
4920
4921 return 0;
4922}
4923
David Ahernc21ef3e2017-04-16 09:48:24 -07004924static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
4925 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004926{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09004927 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07004928 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004929 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07004930 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004931 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004932 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07004933 struct sk_buff *skb;
4934 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07004935 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004936 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07004937
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004938 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07004939 if (err < 0)
4940 goto errout;
4941
4942 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02004943 rtm = nlmsg_data(nlh);
4944 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004945 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07004946
4947 if (tb[RTA_SRC]) {
4948 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
4949 goto errout;
4950
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004951 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07004952 }
4953
4954 if (tb[RTA_DST]) {
4955 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
4956 goto errout;
4957
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004958 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07004959 }
4960
4961 if (tb[RTA_IIF])
4962 iif = nla_get_u32(tb[RTA_IIF]);
4963
4964 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004965 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07004966
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07004967 if (tb[RTA_MARK])
4968 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
4969
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004970 if (tb[RTA_UID])
4971 fl6.flowi6_uid = make_kuid(current_user_ns(),
4972 nla_get_u32(tb[RTA_UID]));
4973 else
4974 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
4975
Roopa Prabhueacb9382018-05-22 14:03:28 -07004976 if (tb[RTA_SPORT])
4977 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
4978
4979 if (tb[RTA_DPORT])
4980 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
4981
4982 if (tb[RTA_IP_PROTO]) {
4983 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08004984 &fl6.flowi6_proto, AF_INET6,
4985 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07004986 if (err)
4987 goto errout;
4988 }
4989
Thomas Grafab364a62006-08-22 00:01:47 -07004990 if (iif) {
4991 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004992 int flags = 0;
4993
Florian Westphal121622d2017-08-15 16:34:42 +02004994 rcu_read_lock();
4995
4996 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07004997 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02004998 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07004999 err = -ENODEV;
5000 goto errout;
5001 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005002
5003 fl6.flowi6_iif = iif;
5004
5005 if (!ipv6_addr_any(&fl6.saddr))
5006 flags |= RT6_LOOKUP_F_HAS_SADDR;
5007
David Ahernb75cc8f2018-03-02 08:32:17 -08005008 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02005009
5010 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005011 } else {
5012 fl6.flowi6_oif = oif;
5013
Ido Schimmel58acfd72017-12-20 12:28:25 +02005014 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005015 }
5016
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005017
5018 rt = container_of(dst, struct rt6_info, dst);
5019 if (rt->dst.error) {
5020 err = rt->dst.error;
5021 ip6_rt_put(rt);
5022 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07005023 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005024
WANG Cong9d6acb32017-03-01 20:48:39 -08005025 if (rt == net->ipv6.ip6_null_entry) {
5026 err = rt->dst.error;
5027 ip6_rt_put(rt);
5028 goto errout;
5029 }
5030
Linus Torvalds1da177e2005-04-16 15:20:36 -07005031 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05005032 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00005033 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07005034 err = -ENOBUFS;
5035 goto errout;
5036 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005037
Changli Gaod8d1f302010-06-10 23:31:35 -07005038 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07005039
5040 rcu_read_lock();
5041 from = rcu_dereference(rt->from);
5042
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005043 if (fibmatch)
David Aherna68886a2018-04-20 15:38:02 -07005044 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005045 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
5046 nlh->nlmsg_seq, 0);
5047 else
David Aherna68886a2018-04-20 15:38:02 -07005048 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5049 &fl6.saddr, iif, RTM_NEWROUTE,
David Ahernd4ead6b2018-04-17 17:33:16 -07005050 NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
5051 0);
David Aherna68886a2018-04-20 15:38:02 -07005052 rcu_read_unlock();
5053
Linus Torvalds1da177e2005-04-16 15:20:36 -07005054 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005055 kfree_skb(skb);
5056 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005057 }
5058
Eric W. Biederman15e47302012-09-07 20:12:54 +00005059 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005060errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005061 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005062}
5063
David Ahern8d1c8022018-04-17 17:33:26 -07005064void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07005065 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005066{
5067 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08005068 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005069 u32 seq;
5070 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005071
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005072 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05005073 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07005074
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005075 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05005076 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07005077 goto errout;
5078
David Ahernd4ead6b2018-04-17 17:33:16 -07005079 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
5080 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08005081 if (err < 0) {
5082 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
5083 WARN_ON(err == -EMSGSIZE);
5084 kfree_skb(skb);
5085 goto errout;
5086 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00005087 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08005088 info->nlh, gfp_any());
5089 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07005090errout:
5091 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08005092 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005093}
5094
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005095static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00005096 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005097{
Jiri Pirko351638e2013-05-28 01:30:21 +00005098 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09005099 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005100
WANG Cong242d3a42017-05-08 10:12:13 -07005101 if (!(dev->flags & IFF_LOOPBACK))
5102 return NOTIFY_OK;
5103
5104 if (event == NETDEV_REGISTER) {
David Ahernad1601a2019-03-27 20:53:56 -07005105 net->ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07005106 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005107 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
5108#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07005109 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005110 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07005111 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005112 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
5113#endif
WANG Cong76da0702017-06-20 11:42:27 -07005114 } else if (event == NETDEV_UNREGISTER &&
5115 dev->reg_state != NETREG_UNREGISTERED) {
5116 /* NETDEV_UNREGISTER could be fired for multiple times by
5117 * netdev_wait_allrefs(). Make sure we only call this once.
5118 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07005119 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005120#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07005121 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
5122 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005123#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005124 }
5125
5126 return NOTIFY_OK;
5127}
5128
Linus Torvalds1da177e2005-04-16 15:20:36 -07005129/*
5130 * /proc
5131 */
5132
5133#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07005134static int rt6_stats_seq_show(struct seq_file *seq, void *v)
5135{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005136 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005137 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005138 net->ipv6.rt6_stats->fib_nodes,
5139 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07005140 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005141 net->ipv6.rt6_stats->fib_rt_entries,
5142 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00005143 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005144 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005145
5146 return 0;
5147}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005148#endif /* CONFIG_PROC_FS */
5149
5150#ifdef CONFIG_SYSCTL
5151
Linus Torvalds1da177e2005-04-16 15:20:36 -07005152static
Joe Perchesfe2c6332013-06-11 23:04:25 -07005153int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005154 void __user *buffer, size_t *lenp, loff_t *ppos)
5155{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005156 struct net *net;
5157 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005158 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005159 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005160 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005161
5162 net = (struct net *)ctl->extra1;
5163 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005164 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5165 if (ret)
5166 return ret;
5167
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02005168 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005169 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170}
5171
David Ahern7c6bb7d2018-10-11 20:17:21 -07005172static int zero;
5173static int one = 1;
5174
David Aherned792e22018-10-08 14:06:34 -07005175static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005176 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005177 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08005178 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005179 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07005180 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005181 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182 },
5183 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005184 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005185 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005186 .maxlen = sizeof(int),
5187 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005188 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005189 },
5190 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005191 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08005192 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005193 .maxlen = sizeof(int),
5194 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005195 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005196 },
5197 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005198 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005199 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005200 .maxlen = sizeof(int),
5201 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005202 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005203 },
5204 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005205 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08005206 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005207 .maxlen = sizeof(int),
5208 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005209 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005210 },
5211 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005212 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005213 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005214 .maxlen = sizeof(int),
5215 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005216 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005217 },
5218 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005219 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08005220 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005221 .maxlen = sizeof(int),
5222 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005223 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005224 },
5225 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08005227 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005228 .maxlen = sizeof(int),
5229 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005230 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005231 },
5232 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005233 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08005234 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005235 .maxlen = sizeof(int),
5236 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005237 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005238 },
5239 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005240 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08005241 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005242 .maxlen = sizeof(int),
5243 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005244 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005245 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07005246 {
5247 .procname = "skip_notify_on_dev_down",
5248 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
5249 .maxlen = sizeof(int),
5250 .mode = 0644,
5251 .proc_handler = proc_dointvec,
5252 .extra1 = &zero,
5253 .extra2 = &one,
5254 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08005255 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005256};
5257
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005258struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005259{
5260 struct ctl_table *table;
5261
5262 table = kmemdup(ipv6_route_table_template,
5263 sizeof(ipv6_route_table_template),
5264 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005265
5266 if (table) {
5267 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005268 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005269 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005270 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
5271 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
5272 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
5273 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
5274 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
5275 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
5276 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08005277 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005278 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00005279
5280 /* Don't export sysctls to unprivileged users */
5281 if (net->user_ns != &init_user_ns)
5282 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005283 }
5284
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005285 return table;
5286}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005287#endif
5288
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005289static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005290{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07005291 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005292
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005293 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
5294 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005295
Eric Dumazetfc66f952010-10-08 06:37:34 +00005296 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
5297 goto out_ip6_dst_ops;
5298
David Ahern421842e2018-04-17 17:33:18 -07005299 net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
5300 sizeof(*net->ipv6.fib6_null_entry),
5301 GFP_KERNEL);
5302 if (!net->ipv6.fib6_null_entry)
5303 goto out_ip6_dst_entries;
5304
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005305 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
5306 sizeof(*net->ipv6.ip6_null_entry),
5307 GFP_KERNEL);
5308 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07005309 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005310 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005311 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
5312 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005313
5314#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02005315 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005316 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
5317 sizeof(*net->ipv6.ip6_prohibit_entry),
5318 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005319 if (!net->ipv6.ip6_prohibit_entry)
5320 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005321 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005322 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
5323 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005324
5325 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
5326 sizeof(*net->ipv6.ip6_blk_hole_entry),
5327 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005328 if (!net->ipv6.ip6_blk_hole_entry)
5329 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005330 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005331 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
5332 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005333#endif
5334
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005335 net->ipv6.sysctl.flush_delay = 0;
5336 net->ipv6.sysctl.ip6_rt_max_size = 4096;
5337 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
5338 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
5339 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
5340 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
5341 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
5342 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005343 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005344
Benjamin Thery6891a342008-03-04 13:49:47 -08005345 net->ipv6.ip6_rt_gc_expire = 30*HZ;
5346
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005347 ret = 0;
5348out:
5349 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005350
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005351#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5352out_ip6_prohibit_entry:
5353 kfree(net->ipv6.ip6_prohibit_entry);
5354out_ip6_null_entry:
5355 kfree(net->ipv6.ip6_null_entry);
5356#endif
David Ahern421842e2018-04-17 17:33:18 -07005357out_fib6_null_entry:
5358 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005359out_ip6_dst_entries:
5360 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005361out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005362 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005363}
5364
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005365static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005366{
David Ahern421842e2018-04-17 17:33:18 -07005367 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005368 kfree(net->ipv6.ip6_null_entry);
5369#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5370 kfree(net->ipv6.ip6_prohibit_entry);
5371 kfree(net->ipv6.ip6_blk_hole_entry);
5372#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005373 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005374}
5375
Thomas Grafd1896342012-06-18 12:08:33 +00005376static int __net_init ip6_route_net_init_late(struct net *net)
5377{
5378#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02005379 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
5380 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02005381 proc_create_net_single("rt6_stats", 0444, net->proc_net,
5382 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00005383#endif
5384 return 0;
5385}
5386
5387static void __net_exit ip6_route_net_exit_late(struct net *net)
5388{
5389#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00005390 remove_proc_entry("ipv6_route", net->proc_net);
5391 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00005392#endif
5393}
5394
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005395static struct pernet_operations ip6_route_net_ops = {
5396 .init = ip6_route_net_init,
5397 .exit = ip6_route_net_exit,
5398};
5399
David S. Millerc3426b42012-06-09 16:27:05 -07005400static int __net_init ipv6_inetpeer_init(struct net *net)
5401{
5402 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
5403
5404 if (!bp)
5405 return -ENOMEM;
5406 inet_peer_base_init(bp);
5407 net->ipv6.peers = bp;
5408 return 0;
5409}
5410
5411static void __net_exit ipv6_inetpeer_exit(struct net *net)
5412{
5413 struct inet_peer_base *bp = net->ipv6.peers;
5414
5415 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07005416 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07005417 kfree(bp);
5418}
5419
David S. Miller2b823f72012-06-09 19:00:16 -07005420static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07005421 .init = ipv6_inetpeer_init,
5422 .exit = ipv6_inetpeer_exit,
5423};
5424
Thomas Grafd1896342012-06-18 12:08:33 +00005425static struct pernet_operations ip6_route_net_late_ops = {
5426 .init = ip6_route_net_init_late,
5427 .exit = ip6_route_net_exit_late,
5428};
5429
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005430static struct notifier_block ip6_route_dev_notifier = {
5431 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07005432 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005433};
5434
WANG Cong2f460932017-05-03 22:07:31 -07005435void __init ip6_route_init_special_entries(void)
5436{
5437 /* Registering of the loopback is done before this portion of code,
5438 * the loopback reference in rt6_info will not be taken, do it
5439 * manually for init_net */
David Ahernad1601a2019-03-27 20:53:56 -07005440 init_net.ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07005441 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
5442 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5443 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
5444 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
5445 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5446 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
5447 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5448 #endif
5449}
5450
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005451int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005452{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005453 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005454 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005455
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005456 ret = -ENOMEM;
5457 ip6_dst_ops_template.kmem_cachep =
5458 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
5459 SLAB_HWCACHE_ALIGN, NULL);
5460 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08005461 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07005462
Eric Dumazetfc66f952010-10-08 06:37:34 +00005463 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005464 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005465 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005466
David S. Millerc3426b42012-06-09 16:27:05 -07005467 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
5468 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005469 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00005470
David S. Miller7e52b332012-06-15 15:51:55 -07005471 ret = register_pernet_subsys(&ip6_route_net_ops);
5472 if (ret)
5473 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07005474
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07005475 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
5476
David S. Millere8803b62012-06-16 01:12:19 -07005477 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005478 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005479 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005480
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005481 ret = xfrm6_init();
5482 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005483 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08005484
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005485 ret = fib6_rules_init();
5486 if (ret)
5487 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08005488
Thomas Grafd1896342012-06-18 12:08:33 +00005489 ret = register_pernet_subsys(&ip6_route_net_late_ops);
5490 if (ret)
5491 goto fib6_rules_init;
5492
Florian Westphal16feebc2017-12-02 21:44:08 +01005493 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
5494 inet6_rtm_newroute, NULL, 0);
5495 if (ret < 0)
5496 goto out_register_late_subsys;
5497
5498 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
5499 inet6_rtm_delroute, NULL, 0);
5500 if (ret < 0)
5501 goto out_register_late_subsys;
5502
5503 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
5504 inet6_rtm_getroute, NULL,
5505 RTNL_FLAG_DOIT_UNLOCKED);
5506 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00005507 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005508
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005509 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005510 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00005511 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005512
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005513 for_each_possible_cpu(cpu) {
5514 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
5515
5516 INIT_LIST_HEAD(&ul->head);
5517 spin_lock_init(&ul->lock);
5518 }
5519
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005520out:
5521 return ret;
5522
Thomas Grafd1896342012-06-18 12:08:33 +00005523out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01005524 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00005525 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005526fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005527 fib6_rules_cleanup();
5528xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005529 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00005530out_fib6_init:
5531 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005532out_register_subsys:
5533 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07005534out_register_inetpeer:
5535 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005536out_dst_entries:
5537 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005538out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005539 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005540 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005541}
5542
5543void ip6_route_cleanup(void)
5544{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005545 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00005546 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07005547 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005548 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005549 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07005550 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005551 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005552 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005553 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005554}